refactor: interfaces and data types into the model package (#642)

## Checklist

- [x] I have read the [contribution guidelines](https://github.com/ooni/probe-cli/blob/master/CONTRIBUTING.md)
- [x] reference issue for this pull request: https://github.com/ooni/probe/issues/1885
- [x] related ooni/spec pull request: N/A

Location of the issue tracker: https://github.com/ooni/probe

## Description

This PR contains a set of changes to move important interfaces and data types into the `./internal/model` package.

The criteria for including an interface or data type in here is roughly that the type should be important and used by several packages. We are especially interested to move more interfaces here to increase modularity.

An additional side effect is that, by reading this package, one should be able to understand more quickly how different parts of the codebase interact with each other.

This is what I want to move in `internal/model`:

- [x] most important interfaces from `internal/netxlite`
- [x] everything that was previously part of `internal/engine/model`
- [x] mocks from `internal/netxlite/mocks` should also be moved in here as a subpackage
This commit is contained in:
Simone Basso
2022-01-03 13:53:23 +01:00
committed by GitHub
parent 69aca619ea
commit 273b70bacc
275 changed files with 1281 additions and 1375 deletions
+77
View File
@@ -0,0 +1,77 @@
package mocks
import (
"context"
"net"
"time"
)
// Dialer is a mockable Dialer.
type Dialer struct {
MockDialContext func(ctx context.Context, network, address string) (net.Conn, error)
MockCloseIdleConnections func()
}
// DialContext calls MockDialContext.
func (d *Dialer) DialContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.MockDialContext(ctx, network, address)
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (d *Dialer) CloseIdleConnections() {
d.MockCloseIdleConnections()
}
// Conn is a mockable net.Conn.
type Conn struct {
MockRead func(b []byte) (int, error)
MockWrite func(b []byte) (int, error)
MockClose func() error
MockLocalAddr func() net.Addr
MockRemoteAddr func() net.Addr
MockSetDeadline func(t time.Time) error
MockSetReadDeadline func(t time.Time) error
MockSetWriteDeadline func(t time.Time) error
}
// Read calls MockRead.
func (c *Conn) Read(b []byte) (int, error) {
return c.MockRead(b)
}
// Write calls MockWrite.
func (c *Conn) Write(b []byte) (int, error) {
return c.MockWrite(b)
}
// Close calls MockClose.
func (c *Conn) Close() error {
return c.MockClose()
}
// LocalAddr calls MockLocalAddr.
func (c *Conn) LocalAddr() net.Addr {
return c.MockLocalAddr()
}
// RemoteAddr calls MockRemoteAddr.
func (c *Conn) RemoteAddr() net.Addr {
return c.MockRemoteAddr()
}
// SetDeadline calls MockSetDeadline.
func (c *Conn) SetDeadline(t time.Time) error {
return c.MockSetDeadline(t)
}
// SetReadDeadline calls MockSetReadDeadline.
func (c *Conn) SetReadDeadline(t time.Time) error {
return c.MockSetReadDeadline(t)
}
// SetWriteDeadline calls MockSetWriteDeadline.
func (c *Conn) SetWriteDeadline(t time.Time) error {
return c.MockSetWriteDeadline(t)
}
var _ net.Conn = &Conn{}
+161
View File
@@ -0,0 +1,161 @@
package mocks
import (
"context"
"errors"
"net"
"testing"
"time"
"github.com/google/go-cmp/cmp"
)
func TestDialer(t *testing.T) {
t.Run("DialContext", func(t *testing.T) {
expected := errors.New("mocked error")
d := Dialer{
MockDialContext: func(ctx context.Context, network string, address string) (net.Conn, error) {
return nil, expected
},
}
ctx := context.Background()
conn, err := d.DialContext(ctx, "tcp", "8.8.8.8:53")
if !errors.Is(err, expected) {
t.Fatal("not the error we expected")
}
if conn != nil {
t.Fatal("expected nil conn")
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
var called bool
d := &Dialer{
MockCloseIdleConnections: func() {
called = true
},
}
d.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
})
}
func TestConn(t *testing.T) {
t.Run("Read", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockRead: func(b []byte) (int, error) {
return 0, expected
},
}
count, err := c.Read(make([]byte, 128))
if !errors.Is(err, expected) {
t.Fatal("not the error we expected")
}
if count != 0 {
t.Fatal("expected 0 bytes")
}
})
t.Run("Write", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockWrite: func(b []byte) (int, error) {
return 0, expected
},
}
count, err := c.Write(make([]byte, 128))
if !errors.Is(err, expected) {
t.Fatal("not the error we expected")
}
if count != 0 {
t.Fatal("expected 0 bytes")
}
})
t.Run("Close", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockClose: func() error {
return expected
},
}
err := c.Close()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected")
}
})
t.Run("LocalAddr", func(t *testing.T) {
expected := &net.TCPAddr{
IP: net.IPv6loopback,
Port: 1234,
}
c := &Conn{
MockLocalAddr: func() net.Addr {
return expected
},
}
out := c.LocalAddr()
if diff := cmp.Diff(expected, out); diff != "" {
t.Fatal(diff)
}
})
t.Run("RemoteAddr", func(t *testing.T) {
expected := &net.TCPAddr{
IP: net.IPv6loopback,
Port: 1234,
}
c := &Conn{
MockRemoteAddr: func() net.Addr {
return expected
},
}
out := c.RemoteAddr()
if diff := cmp.Diff(expected, out); diff != "" {
t.Fatal(diff)
}
})
t.Run("SetDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockSetDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("SetReadDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockSetReadDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetReadDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("SetWriteDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &Conn{
MockSetWriteDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetWriteDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
}
+20
View File
@@ -0,0 +1,20 @@
package mocks
import "github.com/ooni/probe-cli/v3/internal/model"
// DNSDecoder allows mocking dnsx.DNSDecoder.
type DNSDecoder struct {
MockDecodeLookupHost func(qtype uint16, reply []byte) ([]string, error)
MockDecodeHTTPS func(reply []byte) (*model.HTTPSSvc, error)
}
// DecodeLookupHost calls MockDecodeLookupHost.
func (e *DNSDecoder) DecodeLookupHost(qtype uint16, reply []byte) ([]string, error) {
return e.MockDecodeLookupHost(qtype, reply)
}
// DecodeHTTPS calls MockDecodeHTTPS.
func (e *DNSDecoder) DecodeHTTPS(reply []byte) (*model.HTTPSSvc, error) {
return e.MockDecodeHTTPS(reply)
}
+43
View File
@@ -0,0 +1,43 @@
package mocks
import (
"errors"
"testing"
"github.com/miekg/dns"
"github.com/ooni/probe-cli/v3/internal/model"
)
func TestDNSDecoder(t *testing.T) {
t.Run("DecodeLookupHost", func(t *testing.T) {
expected := errors.New("mocked error")
e := &DNSDecoder{
MockDecodeLookupHost: func(qtype uint16, reply []byte) ([]string, error) {
return nil, expected
},
}
out, err := e.DecodeLookupHost(dns.TypeA, make([]byte, 17))
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("unexpected out")
}
})
t.Run("DecodeHTTPS", func(t *testing.T) {
expected := errors.New("mocked error")
e := &DNSDecoder{
MockDecodeHTTPS: func(reply []byte) (*model.HTTPSSvc, error) {
return nil, expected
},
}
out, err := e.DecodeHTTPS(make([]byte, 17))
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("unexpected out")
}
})
}
+11
View File
@@ -0,0 +1,11 @@
package mocks
// DNSEncoder allows mocking dnsx.DNSEncoder.
type DNSEncoder struct {
MockEncode func(domain string, qtype uint16, padding bool) ([]byte, error)
}
// Encode calls MockEncode.
func (e *DNSEncoder) Encode(domain string, qtype uint16, padding bool) ([]byte, error) {
return e.MockEncode(domain, qtype, padding)
}
+26
View File
@@ -0,0 +1,26 @@
package mocks
import (
"errors"
"testing"
"github.com/miekg/dns"
)
func TestDNSEncoder(t *testing.T) {
t.Run("Encode", func(t *testing.T) {
expected := errors.New("mocked error")
e := &DNSEncoder{
MockEncode: func(domain string, qtype uint16, padding bool) ([]byte, error) {
return nil, expected
},
}
out, err := e.Encode("dns.google", dns.TypeA, true)
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if out != nil {
t.Fatal("unexpected out")
}
})
}
+41
View File
@@ -0,0 +1,41 @@
package mocks
import "context"
// DNSTransport allows mocking dnsx.DNSTransport.
type DNSTransport struct {
MockRoundTrip func(ctx context.Context, query []byte) (reply []byte, err error)
MockRequiresPadding func() bool
MockNetwork func() string
MockAddress func() string
MockCloseIdleConnections func()
}
// RoundTrip calls MockRoundTrip.
func (txp *DNSTransport) RoundTrip(ctx context.Context, query []byte) (reply []byte, err error) {
return txp.MockRoundTrip(ctx, query)
}
// RequiresPadding calls MockRequiresPadding.
func (txp *DNSTransport) RequiresPadding() bool {
return txp.MockRequiresPadding()
}
// Network calls MockNetwork.
func (txp *DNSTransport) Network() string {
return txp.MockNetwork()
}
// Address calls MockAddress.
func (txp *DNSTransport) Address() string {
return txp.MockAddress()
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (txp *DNSTransport) CloseIdleConnections() {
txp.MockCloseIdleConnections()
}
+73
View File
@@ -0,0 +1,73 @@
package mocks
import (
"context"
"errors"
"testing"
"github.com/ooni/probe-cli/v3/internal/atomicx"
)
func TestDNSTransport(t *testing.T) {
t.Run("RoundTrip", func(t *testing.T) {
expected := errors.New("mocked error")
txp := &DNSTransport{
MockRoundTrip: func(ctx context.Context, query []byte) ([]byte, error) {
return nil, expected
},
}
resp, err := txp.RoundTrip(context.Background(), make([]byte, 16))
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if resp != nil {
t.Fatal("expected nil response here")
}
})
t.Run("RequiresPadding", func(t *testing.T) {
txp := &DNSTransport{
MockRequiresPadding: func() bool {
return true
},
}
if txp.RequiresPadding() != true {
t.Fatal("unexpected result")
}
})
t.Run("Network", func(t *testing.T) {
txp := &DNSTransport{
MockNetwork: func() string {
return "antani"
},
}
if txp.Network() != "antani" {
t.Fatal("unexpected result")
}
})
t.Run("Address", func(t *testing.T) {
txp := &DNSTransport{
MockAddress: func() string {
return "mascetti"
},
}
if txp.Address() != "mascetti" {
t.Fatal("unexpected result")
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
called := &atomicx.Int64{}
txp := &DNSTransport{
MockCloseIdleConnections: func() {
called.Add(1)
},
}
txp.CloseIdleConnections()
if called.Load() != 1 {
t.Fatal("not called")
}
})
}
+2
View File
@@ -0,0 +1,2 @@
// Package mocks contains mocks for internal/model interfaces.
package mocks
+36
View File
@@ -0,0 +1,36 @@
package mocks
import "net/http"
// HTTPTransport mocks netxlite.HTTPTransport.
type HTTPTransport struct {
MockRoundTrip func(req *http.Request) (*http.Response, error)
MockCloseIdleConnections func()
}
// RoundTrip calls MockRoundTrip.
func (txp *HTTPTransport) RoundTrip(req *http.Request) (*http.Response, error) {
return txp.MockRoundTrip(req)
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (txp *HTTPTransport) CloseIdleConnections() {
txp.MockCloseIdleConnections()
}
// HTTPClient allows mocking an http.Client.
type HTTPClient struct {
MockDo func(req *http.Request) (*http.Response, error)
MockCloseIdleConnections func()
}
// Do calls MockDo.
func (txp *HTTPClient) Do(req *http.Request) (*http.Response, error) {
return txp.MockDo(req)
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (txp *HTTPClient) CloseIdleConnections() {
txp.MockCloseIdleConnections()
}
+71
View File
@@ -0,0 +1,71 @@
package mocks
import (
"errors"
"net/http"
"testing"
"github.com/ooni/probe-cli/v3/internal/atomicx"
)
func TestHTTPTransport(t *testing.T) {
t.Run("RoundTrip", func(t *testing.T) {
expected := errors.New("mocked error")
txp := &HTTPTransport{
MockRoundTrip: func(req *http.Request) (*http.Response, error) {
return nil, expected
},
}
resp, err := txp.RoundTrip(&http.Request{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if resp != nil {
t.Fatal("expected nil response here")
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
called := &atomicx.Int64{}
txp := &HTTPTransport{
MockCloseIdleConnections: func() {
called.Add(1)
},
}
txp.CloseIdleConnections()
if called.Load() != 1 {
t.Fatal("not called")
}
})
}
func TestHTTPClient(t *testing.T) {
t.Run("Do", func(t *testing.T) {
expected := errors.New("mocked error")
clnt := &HTTPClient{
MockDo: func(req *http.Request) (*http.Response, error) {
return nil, expected
},
}
resp, err := clnt.Do(&http.Request{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if resp != nil {
t.Fatal("expected nil response here")
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
called := &atomicx.Int64{}
clnt := &HTTPClient{
MockCloseIdleConnections: func() {
called.Add(1)
},
}
clnt.CloseIdleConnections()
if called.Load() != 1 {
t.Fatal("not called")
}
})
}
+32
View File
@@ -0,0 +1,32 @@
package mocks
import "net"
// Listener allows mocking a net.Listener.
type Listener struct {
// Accept allows mocking Accept.
MockAccept func() (net.Conn, error)
// Close allows mocking Close.
MockClose func() error
// Addr allows mocking Addr.
MockAddr func() net.Addr
}
var _ net.Listener = &Listener{}
// Accept implements net.Listener.Accept
func (li *Listener) Accept() (net.Conn, error) {
return li.MockAccept()
}
// Close implements net.Listener.Closer.
func (li *Listener) Close() error {
return li.MockClose()
}
// Addr implements net.Listener.Addr
func (li *Listener) Addr() net.Addr {
return li.MockAddr()
}
+56
View File
@@ -0,0 +1,56 @@
package mocks
import (
"errors"
"net"
"testing"
"github.com/google/go-cmp/cmp"
)
func TestListener(t *testing.T) {
t.Run("Accept", func(t *testing.T) {
expected := errors.New("mocked error")
li := &Listener{
MockAccept: func() (net.Conn, error) {
return nil, expected
},
}
conn, err := li.Accept()
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
if conn != nil {
t.Fatal("expected nil conn")
}
})
t.Run("Close", func(t *testing.T) {
expected := errors.New("mocked error")
li := &Listener{
MockClose: func() error {
return expected
},
}
err := li.Close()
if !errors.Is(err, expected) {
t.Fatal("unexpected err", err)
}
})
t.Run("Addr", func(t *testing.T) {
addr := &net.TCPAddr{
IP: net.IPv4(127, 0, 0, 1),
Port: 1234,
}
li := &Listener{
MockAddr: func() net.Addr {
return addr
},
}
outAddr := li.Addr()
if diff := cmp.Diff(addr, outAddr); diff != "" {
t.Fatal(diff)
}
})
}
+18
View File
@@ -0,0 +1,18 @@
package mocks
// Logger allows mocking a logger.
type Logger struct {
MockDebug func(message string)
MockDebugf func(format string, v ...interface{})
}
// Debug calls MockDebug.
func (lo *Logger) Debug(message string) {
lo.MockDebug(message)
}
// Debugf calls MockDebugf.
func (lo *Logger) Debugf(format string, v ...interface{}) {
lo.MockDebugf(format, v...)
}
+31
View File
@@ -0,0 +1,31 @@
package mocks
import "testing"
func TestLogger(t *testing.T) {
t.Run("Debug", func(t *testing.T) {
var called bool
lo := &Logger{
MockDebug: func(message string) {
called = true
},
}
lo.Debug("antani")
if !called {
t.Fatal("not called")
}
})
t.Run("Debugf", func(t *testing.T) {
var called bool
lo := &Logger{
MockDebugf: func(message string, v ...interface{}) {
called = true
},
}
lo.Debugf("antani", 1, 2, 3, 4)
if !called {
t.Fatal("not called")
}
})
}
+206
View File
@@ -0,0 +1,206 @@
package mocks
import (
"context"
"crypto/tls"
"net"
"syscall"
"time"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/model"
)
// QUICListener is a mockable netxlite.QUICListener.
type QUICListener struct {
MockListen func(addr *net.UDPAddr) (model.UDPLikeConn, error)
}
// Listen calls MockListen.
func (ql *QUICListener) Listen(addr *net.UDPAddr) (model.UDPLikeConn, error) {
return ql.MockListen(addr)
}
// QUICDialer is a mockable netxlite.QUICDialer.
type QUICDialer struct {
// MockDialContext allows mocking DialContext.
MockDialContext func(ctx context.Context, network, address string,
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error)
// MockCloseIdleConnections allows mocking CloseIdleConnections.
MockCloseIdleConnections func()
}
// DialContext calls MockDialContext.
func (qcd *QUICDialer) DialContext(ctx context.Context, network, address string,
tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
return qcd.MockDialContext(ctx, network, address, tlsConfig, quicConfig)
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (qcd *QUICDialer) CloseIdleConnections() {
qcd.MockCloseIdleConnections()
}
// QUICEarlySession is a mockable quic.EarlySession.
type QUICEarlySession struct {
MockAcceptStream func(context.Context) (quic.Stream, error)
MockAcceptUniStream func(context.Context) (quic.ReceiveStream, error)
MockOpenStream func() (quic.Stream, error)
MockOpenStreamSync func(ctx context.Context) (quic.Stream, error)
MockOpenUniStream func() (quic.SendStream, error)
MockOpenUniStreamSync func(ctx context.Context) (quic.SendStream, error)
MockLocalAddr func() net.Addr
MockRemoteAddr func() net.Addr
MockCloseWithError func(code quic.ApplicationErrorCode, reason string) error
MockContext func() context.Context
MockConnectionState func() quic.ConnectionState
MockHandshakeComplete func() context.Context
MockNextSession func() quic.Session
MockSendMessage func(b []byte) error
MockReceiveMessage func() ([]byte, error)
}
var _ quic.EarlySession = &QUICEarlySession{}
// AcceptStream calls MockAcceptStream.
func (s *QUICEarlySession) AcceptStream(ctx context.Context) (quic.Stream, error) {
return s.MockAcceptStream(ctx)
}
// AcceptUniStream calls MockAcceptUniStream.
func (s *QUICEarlySession) AcceptUniStream(ctx context.Context) (quic.ReceiveStream, error) {
return s.MockAcceptUniStream(ctx)
}
// OpenStream calls MockOpenStream.
func (s *QUICEarlySession) OpenStream() (quic.Stream, error) {
return s.MockOpenStream()
}
// OpenStreamSync calls MockOpenStreamSync.
func (s *QUICEarlySession) OpenStreamSync(ctx context.Context) (quic.Stream, error) {
return s.MockOpenStreamSync(ctx)
}
// OpenUniStream calls MockOpenUniStream.
func (s *QUICEarlySession) OpenUniStream() (quic.SendStream, error) {
return s.MockOpenUniStream()
}
// OpenUniStreamSync calls MockOpenUniStreamSync.
func (s *QUICEarlySession) OpenUniStreamSync(ctx context.Context) (quic.SendStream, error) {
return s.MockOpenUniStreamSync(ctx)
}
// LocalAddr class MockLocalAddr.
func (c *QUICEarlySession) LocalAddr() net.Addr {
return c.MockLocalAddr()
}
// RemoteAddr calls MockRemoteAddr.
func (c *QUICEarlySession) RemoteAddr() net.Addr {
return c.MockRemoteAddr()
}
// CloseWithError calls MockCloseWithError.
func (c *QUICEarlySession) CloseWithError(
code quic.ApplicationErrorCode, reason string) error {
return c.MockCloseWithError(code, reason)
}
// Context calls MockContext.
func (s *QUICEarlySession) Context() context.Context {
return s.MockContext()
}
// ConnectionState calls MockConnectionState.
func (s *QUICEarlySession) ConnectionState() quic.ConnectionState {
return s.MockConnectionState()
}
// HandshakeComplete calls MockHandshakeComplete.
func (s *QUICEarlySession) HandshakeComplete() context.Context {
return s.MockHandshakeComplete()
}
// NextSession calls MockNextSession.
func (s *QUICEarlySession) NextSession() quic.Session {
return s.MockNextSession()
}
// SendMessage calls MockSendMessage.
func (s *QUICEarlySession) SendMessage(b []byte) error {
return s.MockSendMessage(b)
}
// ReceiveMessage calls MockReceiveMessage.
func (s *QUICEarlySession) ReceiveMessage() ([]byte, error) {
return s.MockReceiveMessage()
}
// QUICUDPLikeConn is an UDP conn used by QUIC.
type QUICUDPLikeConn struct {
MockWriteTo func(p []byte, addr net.Addr) (int, error)
MockClose func() error
MockLocalAddr func() net.Addr
MockRemoteAddr func() net.Addr
MockSetDeadline func(t time.Time) error
MockSetReadDeadline func(t time.Time) error
MockSetWriteDeadline func(t time.Time) error
MockReadFrom func(p []byte) (n int, addr net.Addr, err error)
MockSyscallConn func() (syscall.RawConn, error)
MockSetReadBuffer func(n int) error
}
var _ model.UDPLikeConn = &QUICUDPLikeConn{}
// WriteTo calls MockWriteTo.
func (c *QUICUDPLikeConn) WriteTo(p []byte, addr net.Addr) (int, error) {
return c.MockWriteTo(p, addr)
}
// Close calls MockClose.
func (c *QUICUDPLikeConn) Close() error {
return c.MockClose()
}
// LocalAddr calls MockLocalAddr.
func (c *QUICUDPLikeConn) LocalAddr() net.Addr {
return c.MockLocalAddr()
}
// RemoteAddr calls MockRemoteAddr.
func (c *QUICUDPLikeConn) RemoteAddr() net.Addr {
return c.MockRemoteAddr()
}
// SetDeadline calls MockSetDeadline.
func (c *QUICUDPLikeConn) SetDeadline(t time.Time) error {
return c.MockSetDeadline(t)
}
// SetReadDeadline calls MockSetReadDeadline.
func (c *QUICUDPLikeConn) SetReadDeadline(t time.Time) error {
return c.MockSetReadDeadline(t)
}
// SetWriteDeadline calls MockSetWriteDeadline.
func (c *QUICUDPLikeConn) SetWriteDeadline(t time.Time) error {
return c.MockSetWriteDeadline(t)
}
// ReadFrom calls MockReadFrom.
func (c *QUICUDPLikeConn) ReadFrom(b []byte) (int, net.Addr, error) {
return c.MockReadFrom(b)
}
// SyscallConn calls MockSyscallConn.
func (c *QUICUDPLikeConn) SyscallConn() (syscall.RawConn, error) {
return c.MockSyscallConn()
}
// SetReadBuffer calls MockSetReadBuffer.
func (c *QUICUDPLikeConn) SetReadBuffer(n int) error {
return c.MockSetReadBuffer(n)
}
+443
View File
@@ -0,0 +1,443 @@
package mocks
import (
"context"
"crypto/tls"
"errors"
"net"
"reflect"
"syscall"
"testing"
"time"
"github.com/google/go-cmp/cmp"
"github.com/lucas-clemente/quic-go"
"github.com/ooni/probe-cli/v3/internal/model"
)
func TestQUICListenerListen(t *testing.T) {
t.Run("Listen", func(t *testing.T) {
expected := errors.New("mocked error")
ql := &QUICListener{
MockListen: func(addr *net.UDPAddr) (model.UDPLikeConn, error) {
return nil, expected
},
}
pconn, err := ql.Listen(&net.UDPAddr{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", expected)
}
if pconn != nil {
t.Fatal("expected nil conn here")
}
})
}
func TestQUICDialer(t *testing.T) {
t.Run("DialContext", func(t *testing.T) {
expected := errors.New("mocked error")
qcd := &QUICDialer{
MockDialContext: func(ctx context.Context, network string, address string, tlsConfig *tls.Config, quicConfig *quic.Config) (quic.EarlySession, error) {
return nil, expected
},
}
ctx := context.Background()
tlsConfig := &tls.Config{}
quicConfig := &quic.Config{}
sess, err := qcd.DialContext(ctx, "udp", "dns.google:443", tlsConfig, quicConfig)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected")
}
if sess != nil {
t.Fatal("expected nil session")
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
var called bool
qcd := &QUICDialer{
MockCloseIdleConnections: func() {
called = true
},
}
qcd.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
})
}
func TestQUICEarlySession(t *testing.T) {
t.Run("AcceptStream", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockAcceptStream: func(ctx context.Context) (quic.Stream, error) {
return nil, expected
},
}
ctx := context.Background()
stream, err := sess.AcceptStream(ctx)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("AcceptUniStream", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockAcceptUniStream: func(ctx context.Context) (quic.ReceiveStream, error) {
return nil, expected
},
}
ctx := context.Background()
stream, err := sess.AcceptUniStream(ctx)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("OpenStream", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockOpenStream: func() (quic.Stream, error) {
return nil, expected
},
}
stream, err := sess.OpenStream()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("OpenStreamSync", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockOpenStreamSync: func(ctx context.Context) (quic.Stream, error) {
return nil, expected
},
}
ctx := context.Background()
stream, err := sess.OpenStreamSync(ctx)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("OpenUniStream", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockOpenUniStream: func() (quic.SendStream, error) {
return nil, expected
},
}
stream, err := sess.OpenUniStream()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("OpenUniStreamSync", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockOpenUniStreamSync: func(ctx context.Context) (quic.SendStream, error) {
return nil, expected
},
}
ctx := context.Background()
stream, err := sess.OpenUniStreamSync(ctx)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if stream != nil {
t.Fatal("expected nil stream here")
}
})
t.Run("LocalAddr", func(t *testing.T) {
sess := &QUICEarlySession{
MockLocalAddr: func() net.Addr {
return &net.UDPAddr{}
},
}
addr := sess.LocalAddr()
if !reflect.ValueOf(addr).Elem().IsZero() {
t.Fatal("expected a zero address here")
}
})
t.Run("RemoteAddr", func(t *testing.T) {
sess := &QUICEarlySession{
MockRemoteAddr: func() net.Addr {
return &net.UDPAddr{}
},
}
addr := sess.RemoteAddr()
if !reflect.ValueOf(addr).Elem().IsZero() {
t.Fatal("expected a zero address here")
}
})
t.Run("CloseWithError", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockCloseWithError: func(
code quic.ApplicationErrorCode, reason string) error {
return expected
},
}
err := sess.CloseWithError(0, "")
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("Context", func(t *testing.T) {
ctx := context.Background()
sess := &QUICEarlySession{
MockContext: func() context.Context {
return ctx
},
}
out := sess.Context()
if !reflect.DeepEqual(ctx, out) {
t.Fatal("not the context we expected")
}
})
t.Run("ConnectionState", func(t *testing.T) {
state := quic.ConnectionState{SupportsDatagrams: true}
sess := &QUICEarlySession{
MockConnectionState: func() quic.ConnectionState {
return state
},
}
out := sess.ConnectionState()
if !reflect.DeepEqual(state, out) {
t.Fatal("not the context we expected")
}
})
t.Run("HandshakeComplete", func(t *testing.T) {
ctx := context.Background()
sess := &QUICEarlySession{
MockHandshakeComplete: func() context.Context {
return ctx
},
}
out := sess.HandshakeComplete()
if !reflect.DeepEqual(ctx, out) {
t.Fatal("not the context we expected")
}
})
t.Run("NextSession", func(t *testing.T) {
next := &QUICEarlySession{}
sess := &QUICEarlySession{
MockNextSession: func() quic.Session {
return next
},
}
out := sess.NextSession()
if !reflect.DeepEqual(next, out) {
t.Fatal("not the context we expected")
}
})
t.Run("SendMessage", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockSendMessage: func(b []byte) error {
return expected
},
}
b := make([]byte, 17)
err := sess.SendMessage(b)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("ReceiveMessage", func(t *testing.T) {
expected := errors.New("mocked error")
sess := &QUICEarlySession{
MockReceiveMessage: func() ([]byte, error) {
return nil, expected
},
}
b, err := sess.ReceiveMessage()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if b != nil {
t.Fatal("expected nil buffer here")
}
})
}
func TestQUICUDPLikeConn(t *testing.T) {
t.Run("WriteTo", func(t *testing.T) {
expected := errors.New("mocked error")
quc := &QUICUDPLikeConn{
MockWriteTo: func(p []byte, addr net.Addr) (int, error) {
return 0, expected
},
}
pkt := make([]byte, 128)
addr := &net.UDPAddr{}
cnt, err := quc.WriteTo(pkt, addr)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if cnt != 0 {
t.Fatal("expected zero here")
}
})
t.Run("ConnClose", func(t *testing.T) {
expected := errors.New("mocked error")
quc := &QUICUDPLikeConn{
MockClose: func() error {
return expected
},
}
err := quc.Close()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("LocalAddr", func(t *testing.T) {
expected := &net.TCPAddr{
IP: net.IPv6loopback,
Port: 1234,
}
c := &QUICUDPLikeConn{
MockLocalAddr: func() net.Addr {
return expected
},
}
out := c.LocalAddr()
if diff := cmp.Diff(expected, out); diff != "" {
t.Fatal(diff)
}
})
t.Run("RemoteAddr", func(t *testing.T) {
expected := &net.TCPAddr{
IP: net.IPv6loopback,
Port: 1234,
}
c := &QUICUDPLikeConn{
MockRemoteAddr: func() net.Addr {
return expected
},
}
out := c.RemoteAddr()
if diff := cmp.Diff(expected, out); diff != "" {
t.Fatal(diff)
}
})
t.Run("SetDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &QUICUDPLikeConn{
MockSetDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("SetReadDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &QUICUDPLikeConn{
MockSetReadDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetReadDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("SetWriteDeadline", func(t *testing.T) {
expected := errors.New("mocked error")
c := &QUICUDPLikeConn{
MockSetWriteDeadline: func(t time.Time) error {
return expected
},
}
err := c.SetWriteDeadline(time.Time{})
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
t.Run("ConnReadFrom", func(t *testing.T) {
expected := errors.New("mocked error")
quc := &QUICUDPLikeConn{
MockReadFrom: func(b []byte) (int, net.Addr, error) {
return 0, nil, expected
},
}
b := make([]byte, 128)
n, addr, err := quc.ReadFrom(b)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if n != 0 {
t.Fatal("expected zero here")
}
if addr != nil {
t.Fatal("expected nil here")
}
})
t.Run("SyscallConn", func(t *testing.T) {
expected := errors.New("mocked error")
quc := &QUICUDPLikeConn{
MockSyscallConn: func() (syscall.RawConn, error) {
return nil, expected
},
}
conn, err := quc.SyscallConn()
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if conn != nil {
t.Fatal("expected nil here")
}
})
t.Run("SetReadBuffer", func(t *testing.T) {
expected := errors.New("mocked error")
quc := &QUICUDPLikeConn{
MockSetReadBuffer: func(n int) error {
return expected
},
}
err := quc.SetReadBuffer(1 << 10)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
}
+16
View File
@@ -0,0 +1,16 @@
package mocks
import "io"
// Reader allows to mock any io.Reader.
type Reader struct {
MockRead func(b []byte) (int, error)
}
// MockableReader implements an io.Reader.
var _ io.Reader = &Reader{}
// Read implements io.Reader.Read.
func (r *Reader) Read(b []byte) (int, error) {
return r.MockRead(b)
}
+25
View File
@@ -0,0 +1,25 @@
package mocks
import (
"errors"
"testing"
)
func TestReader(t *testing.T) {
t.Run("Read", func(t *testing.T) {
expected := errors.New("mocked error")
r := &Reader{
MockRead: func(b []byte) (int, error) {
return 0, expected
},
}
b := make([]byte, 128)
count, err := r.Read(b)
if !errors.Is(err, expected) {
t.Fatal("unexpected error", err)
}
if count != 0 {
t.Fatal("unexpected count", count)
}
})
}
+41
View File
@@ -0,0 +1,41 @@
package mocks
import (
"context"
"github.com/ooni/probe-cli/v3/internal/model"
)
// Resolver is a mockable Resolver.
type Resolver struct {
MockLookupHost func(ctx context.Context, domain string) ([]string, error)
MockNetwork func() string
MockAddress func() string
MockCloseIdleConnections func()
MockLookupHTTPS func(ctx context.Context, domain string) (*model.HTTPSSvc, error)
}
// LookupHost calls MockLookupHost.
func (r *Resolver) LookupHost(ctx context.Context, domain string) ([]string, error) {
return r.MockLookupHost(ctx, domain)
}
// Address calls MockAddress.
func (r *Resolver) Address() string {
return r.MockAddress()
}
// Network calls MockNetwork.
func (r *Resolver) Network() string {
return r.MockNetwork()
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (r *Resolver) CloseIdleConnections() {
r.MockCloseIdleConnections()
}
// LookupHTTPS calls MockLookupHTTPS.
func (r *Resolver) LookupHTTPS(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return r.MockLookupHTTPS(ctx, domain)
}
+80
View File
@@ -0,0 +1,80 @@
package mocks
import (
"context"
"errors"
"testing"
"github.com/ooni/probe-cli/v3/internal/model"
)
func TestResolver(t *testing.T) {
t.Run("LookupHost", func(t *testing.T) {
expected := errors.New("mocked error")
r := &Resolver{
MockLookupHost: func(ctx context.Context, domain string) ([]string, error) {
return nil, expected
},
}
ctx := context.Background()
addrs, err := r.LookupHost(ctx, "dns.google")
if !errors.Is(err, expected) {
t.Fatal("unexpected error", err)
}
if addrs != nil {
t.Fatal("expected nil addr")
}
})
t.Run("Network", func(t *testing.T) {
r := &Resolver{
MockNetwork: func() string {
return "antani"
},
}
if v := r.Network(); v != "antani" {
t.Fatal("unexpected network", v)
}
})
t.Run("Address", func(t *testing.T) {
r := &Resolver{
MockAddress: func() string {
return "1.1.1.1"
},
}
if v := r.Address(); v != "1.1.1.1" {
t.Fatal("unexpected address", v)
}
})
t.Run("CloseIdleConnections", func(t *testing.T) {
var called bool
r := &Resolver{
MockCloseIdleConnections: func() {
called = true
},
}
r.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
})
t.Run("LookupHTTPS", func(t *testing.T) {
expected := errors.New("mocked error")
r := &Resolver{
MockLookupHTTPS: func(ctx context.Context, domain string) (*model.HTTPSSvc, error) {
return nil, expected
},
}
ctx := context.Background()
https, err := r.LookupHTTPS(ctx, "dns.google")
if !errors.Is(err, expected) {
t.Fatal("unexpected error", err)
}
if https != nil {
t.Fatal("expected nil addr")
}
})
}
+60
View File
@@ -0,0 +1,60 @@
package mocks
import (
"context"
"crypto/tls"
"net"
)
// TLSHandshaker is a mockable TLS handshaker.
type TLSHandshaker struct {
MockHandshake func(ctx context.Context, conn net.Conn, config *tls.Config) (
net.Conn, tls.ConnectionState, error)
}
// Handshake calls MockHandshake.
func (th *TLSHandshaker) Handshake(ctx context.Context, conn net.Conn, config *tls.Config) (
net.Conn, tls.ConnectionState, error) {
return th.MockHandshake(ctx, conn, config)
}
// TLSConn allows to mock netxlite.TLSConn.
type TLSConn struct {
// Conn is the embedded mockable Conn.
Conn
// MockConnectionState allows to mock the ConnectionState method.
MockConnectionState func() tls.ConnectionState
// MockHandshakeContext allows to mock the HandshakeContext method.
MockHandshakeContext func(ctx context.Context) error
}
// ConnectionState calls MockConnectionState.
func (c *TLSConn) ConnectionState() tls.ConnectionState {
return c.MockConnectionState()
}
// HandshakeContext calls MockHandshakeContext.
func (c *TLSConn) HandshakeContext(ctx context.Context) error {
return c.MockHandshakeContext(ctx)
}
// TLSDialer allows to mock netxlite.TLSDialer.
type TLSDialer struct {
// MockCloseIdleConnections allows to mock the CloseIdleConnections method.
MockCloseIdleConnections func()
// MockDialTLSContext allows to mock the DialTLSContext method.
MockDialTLSContext func(ctx context.Context, network, address string) (net.Conn, error)
}
// CloseIdleConnections calls MockCloseIdleConnections.
func (d *TLSDialer) CloseIdleConnections() {
d.MockCloseIdleConnections()
}
// DialTLSContext calls MockDialTLSContext.
func (d *TLSDialer) DialTLSContext(ctx context.Context, network, address string) (net.Conn, error) {
return d.MockDialTLSContext(ctx, network, address)
}
+95
View File
@@ -0,0 +1,95 @@
package mocks
import (
"context"
"crypto/tls"
"errors"
"net"
"reflect"
"testing"
)
func TestTLSHandshaker(t *testing.T) {
t.Run("Handshake", func(t *testing.T) {
expected := errors.New("mocked error")
conn := &Conn{}
ctx := context.Background()
config := &tls.Config{}
th := &TLSHandshaker{
MockHandshake: func(ctx context.Context, conn net.Conn,
config *tls.Config) (net.Conn, tls.ConnectionState, error) {
return nil, tls.ConnectionState{}, expected
},
}
tlsConn, connState, err := th.Handshake(ctx, conn, config)
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if !reflect.ValueOf(connState).IsZero() {
t.Fatal("expected zero ConnectionState here")
}
if tlsConn != nil {
t.Fatal("expected nil conn here")
}
})
}
func TestTLSConn(t *testing.T) {
t.Run("ConnectionState", func(t *testing.T) {
state := tls.ConnectionState{Version: tls.VersionTLS12}
c := &TLSConn{
MockConnectionState: func() tls.ConnectionState {
return state
},
}
out := c.ConnectionState()
if !reflect.DeepEqual(out, state) {
t.Fatal("not the result we expected")
}
})
t.Run("HandshakeContext", func(t *testing.T) {
expected := errors.New("mocked error")
c := &TLSConn{
MockHandshakeContext: func(ctx context.Context) error {
return expected
},
}
err := c.HandshakeContext(context.Background())
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
})
}
func TestTLSDialer(t *testing.T) {
t.Run("CloseIdleConnections", func(t *testing.T) {
var called bool
td := &TLSDialer{
MockCloseIdleConnections: func() {
called = true
},
}
td.CloseIdleConnections()
if !called {
t.Fatal("not called")
}
})
t.Run("DialTLSContext", func(t *testing.T) {
expected := errors.New("mocked error")
td := &TLSDialer{
MockDialTLSContext: func(ctx context.Context, network, address string) (net.Conn, error) {
return nil, expected
},
}
ctx := context.Background()
conn, err := td.DialTLSContext(ctx, "", "")
if !errors.Is(err, expected) {
t.Fatal("not the error we expected", err)
}
if conn != nil {
t.Fatal("expected nil conn here")
}
})
}