Go API Documentation

github.com/TykTechnologies/tyk/test

No package summary is available.

Package

Files: 10. Third party imports: 3. Imports from organisation: 0. Tests: 0. Benchmarks: 0.

Constants

const maxRetryCount = 2

Vars

var (
	DomainsToAddresses	= map[string][]string{
		"host1.":	{"127.0.0.1"},
		"host2.":	{"127.0.0.1"},
		"host3.":	{"127.0.0.1"},
	}
	DomainsToIgnore	= []string{
		"redis.",
		"tyk-redis.",
		"mongo.",	// For dashboard integration tests
		"tyk-mongo.",
	}
)
var buflogger *BufferedLogger
var (
	// exclusiveTestMu is used by Exclusive()
	exclusiveTestMu sync.Mutex
)
var initOnce sync.Once
var once sync.Once
var retryReasons = []string{
	"broken pipe",
	"protocol wrong type for socket",
	"connection reset by peer",
}

Types

BufferedLog

BufferedLog is the struct that holds the log entry.

Field name Field type Comment
Message

string

No comment on field.
Time

time.Time

No comment on field.
Level

logrus.Level

No comment on field.
type BufferedLog struct {
	Message	string		`json:"message"`
	Time	time.Time	`json:"time"`
	Level	logrus.Level	`json:"level"`
}

BufferedLogger

BufferedLogger is the struct that holds the logger and the buffer.

Field name Field type Comment

*logrus.Logger

No comment on field.
bufferingFormatter

*BufferingFormatter

No comment on field.
type BufferedLogger struct {
	*logrus.Logger
	bufferingFormatter	*BufferingFormatter
}

BufferingFormatter

BufferingFormatter is the struct that holds the buffer of the logs.

Field name Field type Comment
bufferMutex

sync.Mutex

No comment on field.
buffer

[]*BufferedLog

No comment on field.
type BufferingFormatter struct {
	bufferMutex	sync.Mutex
	buffer		[]*BufferedLog
}

DnsMockHandle

This type doesn't have documentation.

Field name Field type Comment
id

string

No comment on field.
mockServer

*dns.Server

No comment on field.
ShutdownDnsMock

func() error

No comment on field.
type DnsMockHandle struct {
	id		string
	mockServer	*dns.Server
	ShutdownDnsMock	func() error
}

HTTPTestRunner

This type doesn't have documentation.

Field name Field type Comment
Do

func(*http.Request, *TestCase) (*http.Response, error)

No comment on field.
Assert

func(*http.Response, *TestCase) error

No comment on field.
RequestBuilder

func(*TestCase) (*http.Request, error)

No comment on field.
type HTTPTestRunner struct {
	Do		func(*http.Request, *TestCase) (*http.Response, error)
	Assert		func(*http.Response, *TestCase) error
	RequestBuilder	func(*TestCase) (*http.Request, error)
}

Monitor

This type doesn't have documentation.

Field name Field type Comment
Alloc

uint64

No comment on field.
NumGC

uint32

No comment on field.
NumGoroutine

int

No comment on field.
type Monitor struct {
	Alloc,
	TotalAlloc,
	Sys,
	Mallocs,
	Frees,
	LiveObjects,
	PauseTotalNs	uint64

	NumGC		uint32
	NumGoroutine	int
}

TCPTestCase

This type doesn't have documentation.

Field name Field type Comment
Action

string

No comment on field.
Payload

string

No comment on field.
ErrorMatch

string

No comment on field.
type TCPTestCase struct {
	Action		string	//read or write
	Payload		string
	ErrorMatch	string
}

TCPTestRunner

This type doesn't have documentation.

Field name Field type Comment
UseSSL

bool

No comment on field.
Target

string

No comment on field.
Hostname

string

No comment on field.
TLSClientConfig

*tls.Config

No comment on field.
type TCPTestRunner struct {
	UseSSL		bool
	Target		string
	Hostname	string
	TLSClientConfig	*tls.Config
}

TestCase

This type doesn't have documentation.

Field name Field type Comment
Host

string

No comment on field.
Method

string

No comment on field.
Path

string

No comment on field.
BaseURL

string

No comment on field.
Domain

string

No comment on field.
Proto

string

No comment on field.
Code

int

Code is the expected HTTP response status code.

Data

any

No comment on field.
Headers

map[string]string

No comment on field.
PathParams

map[string]string

No comment on field.
FormParams

map[string]string

No comment on field.
QueryParams

map[string]string

No comment on field.
Cookies

[]*http.Cookie

No comment on field.
Delay

time.Duration

No comment on field.
BodyMatch

string

No comment on field.
BodyNotMatch

string

No comment on field.
HeadersMatch

map[string]string

No comment on field.
HeadersNotMatch

map[string]string

No comment on field.
JSONMatch

map[string]string

No comment on field.
ErrorMatch

string

No comment on field.
BodyMatchFunc

func([]byte) bool

No comment on field.
BeforeFn

func()

No comment on field.
Client

*http.Client

No comment on field.
AdminAuth

bool

No comment on field.
ControlRequest

bool

No comment on field.
type TestCase struct {
	Host	string	`json:",omitempty"`
	Method	string	`json:",omitempty"`
	Path	string	`json:",omitempty"`
	BaseURL	string	`json:",omitempty"`
	Domain	string	`json:",omitempty"`
	Proto	string	`json:",omitempty"`

	// Code is the expected HTTP response status code.
	Code	int	`json:",omitempty"`

	Data		interface{}		`json:",omitempty"`
	Headers		map[string]string	`json:",omitempty"`
	PathParams	map[string]string	`json:",omitempty"`
	FormParams	map[string]string	`json:",omitempty"`
	QueryParams	map[string]string	`json:",omitempty"`
	Cookies		[]*http.Cookie		`json:",omitempty"`
	Delay		time.Duration		`json:",omitempty"`
	BodyMatch	string			`json:",omitempty"`	// regex
	BodyNotMatch	string			`json:",omitempty"`
	HeadersMatch	map[string]string	`json:",omitempty"`
	HeadersNotMatch	map[string]string	`json:",omitempty"`
	JSONMatch	map[string]string	`json:",omitempty"`
	ErrorMatch	string			`json:",omitempty"`

	BodyMatchFunc	func([]byte) bool	`json:"-"`
	BeforeFn	func()			`json:"-"`
	Client		*http.Client		`json:"-"`

	AdminAuth	bool	`json:",omitempty"`
	ControlRequest	bool	`json:",omitempty"`
}

TestCases

This type doesn't have documentation.

Field name Field type Comment
type

[]TestCase

No comment on field.
type TestCases []TestCase

ClientOption, TransportOption, DialContext

This type doesn't have documentation.

Field name Field type Comment
type

func(*http.Client)

No comment on field.
type

func(*http.Transport)

No comment on field.
type

func(ctx context.Context, network, addr string) (net.Conn, error)

No comment on field.
type (
	// Options for populating a http.Client
	ClientOption	func(*http.Client)

	// Options for populating a http.Transport
	TransportOption	func(*http.Transport)

	// DialContext function signature
	DialContext	func(ctx context.Context, network, addr string) (net.Conn, error)
)

dnsMockHandler

This type doesn't have documentation.

Field name Field type Comment
domainsToAddresses

map[string][]string

No comment on field.
domainsToErrors

map[string]int

No comment on field.
muDomainsToAddresses

sync.RWMutex

No comment on field.
type dnsMockHandler struct {
	domainsToAddresses	map[string][]string
	domainsToErrors		map[string]int

	muDomainsToAddresses	sync.RWMutex
}

nopCloser

nopCloser is just like ioutil's, but here to let us re-read the same buffer inside by moving position to the start every time we done with reading

Field name Field type Comment

io.ReadSeeker

No comment on field.
type nopCloser struct {
	io.ReadSeeker
}

Functions

func AssertResponse

func AssertResponse(resp *http.Response, tc *TestCase) error {
	body, _ := ioutil.ReadAll(resp.Body)
	resp.Body = ioutil.NopCloser(bytes.NewBuffer(body))
	defer resp.Body.Close()

	if tc.Code != 0 && resp.StatusCode != tc.Code {
		return fmt.Errorf("Expected status code `%d` got `%d. %s`", tc.Code, resp.StatusCode, string(body))
	}

	if tc.BodyMatch != "" {
		if bodyMatch := regexp.MustCompile(tc.BodyMatch); !bodyMatch.MatchString(string(body)) {
			return fmt.Errorf("Response body does not match with regex `%s`. %s", tc.BodyMatch, string(body))
		}
	}

	if tc.BodyNotMatch != "" && bytes.Contains(body, []byte(tc.BodyNotMatch)) {
		return fmt.Errorf("Response body should not contain `%s`. %s", tc.BodyNotMatch, string(body))
	}

	if tc.BodyMatchFunc != nil && !tc.BodyMatchFunc(body) {
		return fmt.Errorf("Response body did not pass BodyMatchFunc: %s", string(body))
	}

	if tc.Proto != "" && tc.Proto != resp.Proto {
		return fmt.Errorf("Expected protocol `%s` got `%s`.", tc.Proto, resp.Proto)
	}

	for k, v := range tc.HeadersMatch {
		if resp.Header.Get(k) != v {
			return fmt.Errorf("Response header `%s` expected `%s` instead `%s`. %v", k, v, resp.Header.Get(k), resp.Header)
		}
	}

	for k, v := range tc.HeadersNotMatch {
		if resp.Header.Get(k) == v {
			return fmt.Errorf("Response header `%s` should not be %s`", k, v)
		}
	}

	if len(tc.JSONMatch) == 0 {
		return nil
	}

	var jsonBody map[string]json.RawMessage
	if err := json.Unmarshal(body, &jsonBody); err != nil {
		return fmt.Errorf("Should return JSON body: %s. %d", string(body), resp.StatusCode)
	}

	for k, expect := range tc.JSONMatch {
		if got, ok := jsonBody[k]; !ok {
			return fmt.Errorf("`%s` JSON field not found: %s", k, string(body))
		} else if string(got) != expect {
			return fmt.Errorf("`%s` not match: `%s` != `%s`", k, got, expect)
		}
	}

	return nil
}

Cognitive complexity: 33, Cyclomatic complexity: 20

Uses: bytes.Contains, bytes.NewBuffer, fmt.Errorf, ioutil.NopCloser, ioutil.ReadAll, json.RawMessage, json.Unmarshal, regexp.MustCompile.

func CI

CI returns true when a non-empty CI env is present

func CI() bool {
	return os.Getenv("CI") != ""
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: os.Getenv.

func Cert

Generate cert

func Cert(domain string) tls.Certificate {
	private, _ := rsa.GenerateKey(rand.Reader, 512)
	template := &x509.Certificate{
		SerialNumber:	big.NewInt(1),
		Subject: pkix.Name{
			Organization:	[]string{"Test Co"},
			CommonName:	domain,
		},
		NotBefore:		time.Time{},
		NotAfter:		time.Now().Add(60 * time.Minute),
		IsCA:			true,
		KeyUsage:		x509.KeyUsageKeyEncipherment | x509.KeyUsageDigitalSignature | x509.KeyUsageCertSign,
		ExtKeyUsage:		[]x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
		BasicConstraintsValid:	true,
	}

	derBytes, _ := x509.CreateCertificate(rand.Reader, template, template, &private.PublicKey, private)

	var cert, key bytes.Buffer
	pem.Encode(&cert, &pem.Block{Type: "CERTIFICATE", Bytes: derBytes})
	pem.Encode(&key, &pem.Block{Type: "RSA PRIVATE KEY", Bytes: x509.MarshalPKCS1PrivateKey(private)})

	tlscert, _ := tls.X509KeyPair(cert.Bytes(), key.Bytes())

	return tlscert
}

Cognitive complexity: 7, Cyclomatic complexity: 1

Uses: big.NewInt, bytes.Buffer, pem.Block, pem.Encode, pkix.Name, rand.Reader, rsa.GenerateKey, time.Minute, time.Now, time.Time, tls.X509KeyPair, x509.Certificate, x509.CreateCertificate, x509.ExtKeyUsage, x509.ExtKeyUsageServerAuth, x509.KeyUsageCertSign, x509.KeyUsageDigitalSignature, x509.KeyUsageKeyEncipherment, x509.MarshalPKCS1PrivateKey.

func Flaky

Flaky skips a flaky test in a CI environment

func Flaky(t *testing.T, fake ...func() (bool, func(...interface{}))) {
	skipCI(t, "Skipping flaky test", fake...)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func GetPythonVersion

func GetPythonVersion() string {
	pythonVersion := os.Getenv("PYTHON_VERSION")
	if pythonVersion == "" {
		pythonVersion = "3.5"
	}
	return pythonVersion
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: os.Getenv.

func HttpHandlerRunner

func HttpHandlerRunner(handler http.HandlerFunc) func(*http.Request, *TestCase) (*http.Response, error) {
	return func(r *http.Request, _ *TestCase) (*http.Response, error) {
		rec := httptest.NewRecorder()
		handler(rec, r)
		return rec.Result(), nil
	}
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Request, http.Response, httptest.NewRecorder.

func HttpServerRequestBuilder

func HttpServerRequestBuilder(baseURL string) func(tc *TestCase) (*http.Request, error) {
	return func(tc *TestCase) (*http.Request, error) {
		tc.BaseURL = baseURL
		return NewRequest(tc)
	}
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Request.

func HttpServerRunner

func HttpServerRunner() func(*http.Request, *TestCase) (*http.Response, error) {
	return func(r *http.Request, tc *TestCase) (*http.Response, error) {
		return tc.Client.Do(r)
	}
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Request, http.Response.

func InitDNSMock

InitDNSMock initializes dns server on udp:0 address and replaces net.DefaultResolver in order to route all dns queries within tests to this server. InitDNSMock returns handle, which can be used to add/remove dns query mock responses or initialization error.

func InitDNSMock(domainsMap map[string][]string, domainsErrorMap map[string]int) (*DnsMockHandle, error) {
	addr, _ := net.ResolveUDPAddr("udp", ":0")
	conn, err := net.ListenUDP("udp", addr)
	if err != nil {
		return &DnsMockHandle{}, err
	}

	startResultChannel := make(chan error)
	started := func() {
		startResultChannel <- nil
	}

	mockServer := &dns.Server{PacketConn: conn, NotifyStartedFunc: started}
	handle := &DnsMockHandle{id: time.Now().String(), mockServer: mockServer}

	dnsMux := &dnsMockHandler{muDomainsToAddresses: sync.RWMutex{}}

	if domainsMap != nil {
		dnsMux.domainsToAddresses = domainsMap
	} else {
		dnsMux.domainsToAddresses = DomainsToAddresses
	}

	if domainsErrorMap != nil {
		dnsMux.domainsToErrors = domainsErrorMap
	}

	mockServer.Handler = dnsMux

	go func() {
		startResultChannel <- mockServer.ActivateAndServe()
	}()

	err = <-startResultChannel
	if err != nil {
		close(startResultChannel)
		return handle, err
	}

	mockResolver := &net.Resolver{
		PreferGo:	true,
		Dial: func(ctx context.Context, network, address string) (net.Conn, error) {
			d := net.Dialer{}
			return d.DialContext(ctx, network, mockServer.PacketConn.LocalAddr().String())
		},
	}

	// TODO: this is destructive, TT-5112
	once.Do(func() {
		net.DefaultResolver = mockResolver
	})

	handle.ShutdownDnsMock = func() error {
		// We run tests against O(1) packages, we can
		// afford a dirty shutdown, if it means less
		// flaky tests.
		return nil	// mockServer.Shutdown()
	}

	return handle, nil
}

Cognitive complexity: 22, Cyclomatic complexity: 5

Uses: context.Context, dns.Server, net.Conn, net.DefaultResolver, net.Dialer, net.ListenUDP, net.ResolveUDPAddr, net.Resolver, sync.RWMutex, time.Now.

func InitTestMain

func InitTestMain(_ context.Context, _ *testing.M) {
	initOnce.Do(func() {
		// Poor mans resource monitoring, prints runtime stats into logs
		if interval := os.Getenv("TEST_MONITOR_INTERVAL"); interval != "" {
			val, _ := strconv.Atoi(interval)
			if val < 1 {
				val = 1
			}
			go NewMonitor(val)
		}

		// Enable pprof server from test env
		if pprof := os.Getenv("TEST_PPROF_ENABLE"); pprof == "1" {
			var listen string
			if listen = os.Getenv("TEST_PPROF_ADDR"); listen == "" {
				listen = ":12345"
			}
			go func() {
				log.Printf("Starting pprof on addr=%q", listen)
				_ = http.ListenAndServe(listen, nil)
			}()
		}
	})
}

Cognitive complexity: 10, Cyclomatic complexity: 5

Uses: http.ListenAndServe, log.Printf, os.Getenv, strconv.Atoi.

func IsDnsRecordsAddrsEqualsTo

func IsDnsRecordsAddrsEqualsTo(itemAddrs, addrs []string) bool {
	return reflect.DeepEqual(itemAddrs, addrs)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

Uses: reflect.DeepEqual.

func IsRaceEnabled

func IsRaceEnabled() bool {
	return false
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func LocalDialer

LocalDialer provides a function to use to dial to localhost

func LocalDialer() func(context.Context, string, string) (net.Conn, error) {
	return func(ctx context.Context, network, addr string) (net.Conn, error) {
		_, port, err := net.SplitHostPort(addr)
		if err != nil {
			return nil, err
		}
		host := "127.0.0.1"

		dialer := net.Dialer{}
		return dialer.DialContext(ctx, network, net.JoinHostPort(host, port))
	}
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: context.Context, net.Conn, net.Dialer, net.JoinHostPort, net.SplitHostPort.

func MarshalJSON

MarshalJSON returns a closure returning the marshalled json while asserting no error occurred during marshalling.

func MarshalJSON(tb testing.TB) func(interface{}) []byte {
	tb.Helper()

	return func(in interface{}) []byte {
		b, err := json.Marshal(in)
		require.NoError(tb, err)
		return b
	}
}

Cognitive complexity: 3, Cyclomatic complexity: 1

Uses: json.Marshal, require.NoError.

func NewBufferingLogger

NewBufferingLogger creates a new buffered logger.

func NewBufferingLogger() *BufferedLogger {
	if buflogger != nil {
		return buflogger
	}

	bufferingFormatter := &BufferingFormatter{buffer: make([]*BufferedLog, 0)}
	logger := logrus.New()
	logger.Level = logrus.DebugLevel
	logger.Formatter = bufferingFormatter
	buflogger = &BufferedLogger{
		Logger:			logger,
		bufferingFormatter:	bufferingFormatter,
	}
	return buflogger
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: logrus.DebugLevel, logrus.New.

func NewClient

NewClient creates a http.Client with options

func NewClient(opts ...ClientOption) *http.Client {
	client := &http.Client{}
	applyClientOptions(client, opts...)
	return client
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Client.

func NewClientLocal

NewClientLocal returns a http.Client that can connect only to localhost. See: WithLocalDialer.

func NewClientLocal(opts ...ClientOption) *http.Client {
	client := NewClient(
		WithTransport(
			NewTransport(WithLocalDialer()),
		),
	)
	applyClientOptions(client, opts...)
	return client
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func NewMonitor

func NewMonitor(duration int) {
	var m Monitor
	var rtm runtime.MemStats
	var interval = time.Duration(duration) * time.Second
	for {
		<-time.After(interval)

		// Read full mem stats
		runtime.ReadMemStats(&rtm)

		// Number of goroutines
		m.NumGoroutine = runtime.NumGoroutine()
		if m.NumGoroutine > 4000 {
			go func() {
				sigs := make(chan os.Signal, 1)
				signal.Notify(sigs, syscall.SIGQUIT)
				buf := make([]byte, 1<<20)
				for {
					<-sigs
					stacklen := runtime.Stack(buf, true)
					log.Printf("=== received SIGQUIT ===\n*** goroutine dump...\n%s\n*** end\n", buf[:stacklen])
				}
			}()
		}

		// Misc memory stats
		m.Alloc = rtm.Alloc
		m.TotalAlloc = rtm.TotalAlloc
		m.Sys = rtm.Sys
		m.Mallocs = rtm.Mallocs
		m.Frees = rtm.Frees

		// Live objects = Mallocs - Frees
		m.LiveObjects = m.Mallocs - m.Frees

		// GC Stats
		m.PauseTotalNs = rtm.PauseTotalNs
		m.NumGC = rtm.NumGC

		// Just encode to json and print
		b, _ := json.Marshal(m)
		fmt.Fprintln(os.Stderr, string(b))
	}
}

Cognitive complexity: 7, Cyclomatic complexity: 4

Uses: fmt.Fprintln, json.Marshal, log.Printf, os.Signal, os.Stderr, runtime.MemStats, runtime.NumGoroutine, runtime.ReadMemStats, runtime.Stack, signal.Notify, syscall.SIGQUIT, time.After, time.Duration, time.Second.

func NewRequest

func NewRequest(tc *TestCase) (req *http.Request, err error) {
	if tc.Method == "" {
		tc.Method = "GET"
	}

	if tc.Path == "" {
		tc.Path = "/"
	}

	if tc.Domain == "" {
		tc.Domain = "127.0.0.1"
	}

	if tc.Client == nil {
		tc.Client = &http.Client{}
	}

	uri := tc.Path
	if tc.BaseURL != "" {
		uri = tc.BaseURL + tc.Path
	}
	if strings.HasPrefix(uri, "http") {
		uri = strings.Replace(uri, "[::]", tc.Domain, 1)
		uri = strings.Replace(uri, "127.0.0.1", tc.Domain, 1)

		req, err = http.NewRequest(tc.Method, uri, ReqBodyReader(tc.Data))
		if err != nil {
			return
		}
	} else {
		req = httptest.NewRequest(tc.Method, uri, ReqBodyReader(tc.Data))
	}

	for k, v := range tc.Headers {
		req.Header.Add(k, v)
	}

	req.Header.Add("Content-Type", "application/json")

	for _, c := range tc.Cookies {
		req.AddCookie(c)
	}

	formParams := url.Values{}
	for k, v := range tc.FormParams {
		formParams.Add(k, v)
	}
	req.PostForm = formParams
	req.Form = formParams

	if tc.QueryParams != nil {
		queryParams := req.URL.Query()
		for k, v := range tc.QueryParams {
			queryParams.Add(k, v)
		}
		req.URL.RawQuery = queryParams.Encode()
	}

	return req, nil
}

Cognitive complexity: 32, Cyclomatic complexity: 13

Uses: http.Client, http.NewRequest, httptest.NewRequest, strings.HasPrefix, strings.Replace, url.Values.

func NewTransport

NewTransport creates a http.Transport with options

func NewTransport(opts ...TransportOption) *http.Transport {
	transport := &http.Transport{}
	for _, v := range opts {
		v(transport)
	}
	return transport
}

Cognitive complexity: 4, Cyclomatic complexity: 2

Uses: http.Transport.

func Racy

Racy skips a racy test in a CI environment

func Racy(t *testing.T, fake ...func() (bool, func(...interface{}))) {
	skipCI(t, "Skipping racy test", fake...)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func ReqBodyReader

func ReqBodyReader(body interface{}) io.Reader {
	switch x := body.(type) {
	case []byte:
		return bytes.NewReader(x)
	case string:
		return strings.NewReader(x)
	case io.Reader:
		return x
	case nil:
		return nil
	default:	// JSON objects (structs)
		bs, err := json.Marshal(x)
		if err != nil {
			panic(err)
		}
		return bytes.NewReader(bs)
	}
}

Cognitive complexity: 9, Cyclomatic complexity: 7

Uses: bytes.NewReader, io.Reader, json.Marshal, strings.NewReader.

func TcpMock

func TcpMock(useSSL bool, cb func(in []byte, err error) (out []byte)) net.Listener {
	var l net.Listener

	if useSSL {
		tlsConfig := &tls.Config{
			Certificates:		[]tls.Certificate{Cert("localhost")},
			InsecureSkipVerify:	true,
			MaxVersion:		tls.VersionTLS12,
		}
		tlsConfig.BuildNameToCertificate()
		l, _ = tls.Listen("tcp", ":0", tlsConfig)
	} else {
		l, _ = net.Listen("tcp", ":0")
	}

	go func() {
		for {
			// Listen for an incoming connection.
			conn, err := l.Accept()
			if err != nil {
				log.Println("Mock Accept error", err.Error())
				return
			}
			buf := make([]byte, 65535)
			n, err := conn.Read(buf)

			resp := cb(buf[:n], err)

			if err != nil {
				log.Println("Mock read error", err.Error())
				return
			}

			if len(resp) > 0 {
				if n, err = conn.Write(resp); err != nil {
					log.Println("Mock Conn write error", err.Error())
				}
			}
		}
	}()

	return l
}

Cognitive complexity: 17, Cyclomatic complexity: 7

Uses: log.Println, net.Listen, net.Listener, tls.Certificate, tls.Config, tls.Listen, tls.VersionTLS12.

func TestHttpHandler

func TestHttpHandler(t testing.TB, handle http.HandlerFunc, testCases ...TestCase) {
	runner := HTTPTestRunner{
		Do: HttpHandlerRunner(handle),
	}
	runner.Run(t, testCases...)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func TestHttpServer

func TestHttpServer(t testing.TB, baseURL string, testCases ...TestCase) {
	runner := HTTPTestRunner{
		Do:		HttpServerRunner(),
		RequestBuilder:	HttpServerRequestBuilder(baseURL),
	}
	runner.Run(t, testCases...)
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func WithDialer

WithDialer sets transport.DialContext

func WithDialer(dialer DialContext) TransportOption {
	return TransportOption(func(transport *http.Transport) {
		transport.DialContext = dialer
	})
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Transport.

func WithLocalDialer

WithLocalDialer sets a http.Transport DialContext, which only connects to 127.0.0.1.

func WithLocalDialer() TransportOption {
	return TransportOption(func(transport *http.Transport) {
		transport.DialContext = LocalDialer()
	})
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Transport.

func WithTransport

WithTransport sets a http.RoundTripper to a http.Client

func WithTransport(transport http.RoundTripper) ClientOption {
	return ClientOption(func(c *http.Client) {
		c.Transport = transport
	})
}

Cognitive complexity: 1, Cyclomatic complexity: 1

Uses: http.Client.

func (*BufferedLogger) ClearLogs

ClearLogs clears the logs from the buffer. IMPORTANT: Must be called before each test iteration.

func (bl *BufferedLogger) ClearLogs() {
	bl.bufferingFormatter.bufferMutex.Lock()
	defer bl.bufferingFormatter.bufferMutex.Unlock()
	bl.bufferingFormatter.buffer = make([]*BufferedLog, 0)
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (*BufferedLogger) GetLogs

GetLogs returns the logs that are stored in the buffer.

func (bl *BufferedLogger) GetLogs(Level logrus.Level) []*BufferedLog {
	bl.bufferingFormatter.bufferMutex.Lock()
	defer bl.bufferingFormatter.bufferMutex.Unlock()

	logs := make([]*BufferedLog, 0)
	for _, log := range bl.bufferingFormatter.buffer {
		if log.Level == Level {
			logs = append(logs, log)
		}
	}
	return logs
}

Cognitive complexity: 5, Cyclomatic complexity: 3

func (*BufferingFormatter) Format

Format is the method that stores the log entry in the buffer.

func (f *BufferingFormatter) Format(entry *logrus.Entry) ([]byte, error) {
	f.bufferMutex.Lock()
	defer f.bufferMutex.Unlock()

	bl := &BufferedLog{
		Message:	entry.Message,
		Time:		entry.Time.UTC(),
		Level:		entry.Level,
	}

	f.buffer = append(f.buffer, bl)
	return nil, nil
}

Cognitive complexity: 1, Cyclomatic complexity: 1

func (*DnsMockHandle) PushDomains

func (h *DnsMockHandle) PushDomains(domainsMap map[string][]string, domainsErrorMap map[string]int) func() {
	handler := h.mockServer.Handler.(*dnsMockHandler)
	handler.muDomainsToAddresses.Lock()
	defer handler.muDomainsToAddresses.Unlock()

	dta := handler.domainsToAddresses
	dte := handler.domainsToErrors

	prevDta := map[string][]string{}
	prevDte := map[string]int{}

	for key, value := range dta {
		prevDta[key] = value
	}

	for key, value := range dte {
		prevDte[key] = value
	}

	pullDomainsFunc := func() {
		handler := h.mockServer.Handler.(*dnsMockHandler)
		handler.muDomainsToAddresses.Lock()
		defer handler.muDomainsToAddresses.Unlock()

		handler.domainsToAddresses = prevDta
		handler.domainsToErrors = prevDte
	}

	for key, ips := range domainsMap {
		addr, ok := dta[key]
		if !ok {
			dta[key] = ips
		} else {
			dta[key] = append(addr, ips...)
		}
	}

	for key, rCode := range domainsErrorMap {
		dte[key] = rCode
	}

	return pullDomainsFunc
}

Cognitive complexity: 19, Cyclomatic complexity: 6

func (*dnsMockHandler) ServeDNS

func (d *dnsMockHandler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	msg := dns.Msg{}
	msg.SetReply(r)
	switch r.Question[0].Qtype {
	case dns.TypeA:
		msg.Authoritative = true
		domain := msg.Question[0].Name

		d.muDomainsToAddresses.RLock()
		defer d.muDomainsToAddresses.RUnlock()

		if rcode, ok := d.domainsToErrors[domain]; ok {
			m := new(dns.Msg)
			m.SetRcode(r, rcode)
			w.WriteMsg(m)
			return
		}

		for _, ignore := range DomainsToIgnore {
			if strings.HasPrefix(domain, ignore) {
				resolver := &net.Resolver{}
				ipAddrs, err := resolver.LookupIPAddr(context.Background(), domain)
				if err != nil {
					m := new(dns.Msg)
					m.SetRcode(r, dns.RcodeServerFailure)
					w.WriteMsg(m)
					return
				}
				msg.Answer = append(msg.Answer, &dns.A{
					Hdr:	dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
					A:	ipAddrs[0].IP,
				})
				w.WriteMsg(&msg)
				return
			}
		}

		var addresses []string

		for d, ips := range d.domainsToAddresses {
			if strings.HasPrefix(domain, d) {
				addresses = ips
			}
		}

		if len(addresses) == 0 {
			// ^ 				start of line
			// localhost\.		match literally
			// ()* 				match between 0 and unlimited times
			// [[:alnum:]]+\.	match single character in [a-zA-Z0-9] minimum one time and ending in . literally
			reg := regexp.MustCompile(`^localhost\.([[:alnum:]]+\.)*`)
			if matched := reg.MatchString(domain); !matched {
				panic(fmt.Sprintf("domain not mocked: %s", domain))
			}

			addresses = []string{"127.0.0.1"}
		}

		for _, addr := range addresses {
			msg.Answer = append(msg.Answer, &dns.A{
				Hdr:	dns.RR_Header{Name: domain, Rrtype: dns.TypeA, Class: dns.ClassINET, Ttl: 60},
				A:	net.ParseIP(addr),
			})
		}
	}
	w.WriteMsg(&msg)
}

Cognitive complexity: 31, Cyclomatic complexity: 12

Uses: context.Background, dns.A, dns.ClassINET, dns.Msg, dns.RR_Header, dns.RcodeServerFailure, dns.TypeA, fmt.Sprintf, net.ParseIP, net.Resolver, regexp.MustCompile, strings.HasPrefix.

func (HTTPTestRunner) Run

func (r HTTPTestRunner) Run(t testing.TB, testCases ...TestCase) (*http.Response, error) {
	t.Helper()
	var lastResponse *http.Response
	var lastError error

	if r.Do == nil {
		panic("Request runner not implemented")
	}

	if r.Assert == nil {
		r.Assert = AssertResponse
	}

	if r.RequestBuilder == nil {
		r.RequestBuilder = NewRequest
	}

	for ti, tc := range testCases {
		var tc *TestCase = &tc

		if tc.BeforeFn != nil {
			tc.BeforeFn()
		}

		req, err := r.RequestBuilder(tc)
		if err != nil {
			t.Errorf("[%d] Request build error: %s", ti, err.Error())
			continue
		}

		if tc.Host != "" {
			req.Host = tc.Host
		}

		retryCount := 0
	retry:

		lastResponse, lastError = r.Do(req, tc)
		tcJSON, _ := json.Marshal(tc)

		if lastError != nil {
			if retryCount < maxRetryCount && needRetry(lastError.Error()) {
				retryCount++
				goto retry
			}

			if tc.ErrorMatch != "" {
				if !strings.Contains(lastError.Error(), tc.ErrorMatch) {
					t.Errorf("[%d] Expect error `%s` to contain `%s`. %s", ti, lastError.Error(), tc.ErrorMatch, string(tcJSON))
				}
			} else {
				t.Errorf("[%d] Connection error: %s. %s", ti, lastError.Error(), string(tcJSON))
			}
			continue
		} else if tc.ErrorMatch != "" {
			t.Error("Expect error.", string(tcJSON))
			continue
		}

		respCopy := copyResponse(lastResponse)
		if lastError = r.Assert(respCopy, tc); lastError != nil {
			t.Errorf("[%d] %s. %s\n", ti, lastError.Error(), string(tcJSON))
		}

		if delay := tc.Delay; delay > 0 {
			time.Sleep(delay)
		}
	}

	return lastResponse, lastError
}

Cognitive complexity: 31, Cyclomatic complexity: 16

Uses: http.Response, json.Marshal, strings.Contains, time.Sleep.

func (TCPTestRunner) Run

func (r TCPTestRunner) Run(t testing.TB, testCases ...TCPTestCase) error {
	var err error
	buf := make([]byte, 65535)

	var client net.Conn
	if r.UseSSL {
		if r.TLSClientConfig == nil {
			r.TLSClientConfig = &tls.Config{
				ServerName:		r.Hostname,
				InsecureSkipVerify:	true,
				MaxVersion:		tls.VersionTLS12,
			}
		}
		client, err = tls.Dial("tcp", r.Target, r.TLSClientConfig)
		if err != nil {
			return err
		}
	} else {
		client, err = net.Dial("tcp", r.Target)
		if err != nil {
			return err
		}
	}
	defer client.Close()

	for ti, tc := range testCases {
		var n int
		client.SetDeadline(time.Now().Add(time.Second))
		switch tc.Action {
		case "write":
			_, err = client.Write([]byte(tc.Payload))
		case "read":
			n, err = client.Read(buf)

			if err == nil {
				if string(buf[:n]) != tc.Payload {
					t.Fatalf("[%d] Expected read %s, got %v", ti, tc.Payload, string(buf[:n]))
				}
			}
		}

		if tc.ErrorMatch != "" {
			if err == nil {
				t.Fatalf("[%d] Expected error: %s", ti, tc.ErrorMatch)
			}

			if !strings.Contains(err.Error(), tc.ErrorMatch) {
				t.Fatalf("[%d] Expected error %s, got %s", ti, err.Error(), tc.ErrorMatch)
			}
		} else {
			if err != nil {
				t.Fatalf("[%d] Unexpected error: %s", ti, err.Error())
			}
		}
	}

	return nil
}

Cognitive complexity: 32, Cyclomatic complexity: 15

Uses: net.Conn, net.Dial, strings.Contains, time.Now, time.Second, tls.Config, tls.Dial, tls.VersionTLS12.

func (nopCloser) Close

Close is a no-op Close

func (n nopCloser) Close() error {
	return nil
}

Cognitive complexity: 0, Cyclomatic complexity: 1

func (nopCloser) Read

Read just a wrapper around real Read which also moves position to the start if we get EOF to have it ready for next read-cycle

func (n nopCloser) Read(p []byte) (int, error) {
	num, err := n.ReadSeeker.Read(p)
	if errors.Is(err, io.EOF) {	// move to start to have it ready for next read cycle
		_, _ = n.Seek(0, io.SeekStart)
	}
	return num, err
}

Cognitive complexity: 2, Cyclomatic complexity: 2

Uses: errors.Is, io.EOF, io.SeekStart.

Private functions

func applyClientOptions

applyClientOptions (c *http.Client, opts ...ClientOption)

func copyBody

copyBody (body io.ReadCloser) io.ReadCloser
References: bytes.Buffer, bytes.NewReader, io.Copy, io.SeekStart.

func copyResponse

copyResponse (r *http.Response) *http.Response

func needRetry

needRetry (errString string) bool
References: strings.Contains.

func skipCI

skipCI (t *testing.T, message string, fake ...func() (bool, func(...interface{})))


Tests

Files: 4. Third party imports: 2. Imports from organisation: 0. Tests: 8. Benchmarks: 0.

Test functions

TestBufferedLoggerClearLogs

References: assert.Greater, assert.LessOrEqual, logrus.InfoLevel, logrus.WarnLevel, testing.T, time.Now.

TestBufferedLoggerGetLogs

References: assert.Len, logrus.DebugLevel, logrus.ErrorLevel, logrus.InfoLevel, logrus.Level, logrus.WarnLevel, testing.T, time.Now.

TestBufferingFormatterFormat

References: assert.Equal, assert.Len, assert.Nil, logrus.Entry, logrus.InfoLevel, sync.Mutex, testing.T, time.Now.

TestCI

References: assert.False, assert.True, os.Getenv.

TestIsRaceEnabled

TestMarshalJSON

References: assert.Equal.

TestNewBufferingLogger

References: assert.Equal, assert.Len, assert.NotNil, assert.True, testing.T.

TestSkipping

References: assert.True, testing.T.