Files
akvorado/common/helpers/tests.go
2022-04-01 20:21:53 +02:00

130 lines
3.4 KiB
Go

//go:build !release
package helpers
import (
"bufio"
"context"
"errors"
"fmt"
"net"
"net/http"
"os"
"reflect"
"testing"
"time"
"github.com/kylelemons/godebug/pretty"
)
var prettyC = pretty.Config{
Diffable: true,
PrintStringers: false,
SkipZeroFields: true,
IncludeUnexported: false,
Formatter: map[reflect.Type]interface{}{
reflect.TypeOf(net.IP{}): fmt.Sprint,
},
}
// Diff return a diff of two objects. If no diff, an empty string is
// returned.
func Diff(a, b interface{}) string {
return prettyC.Compare(a, b)
}
// HTTPEndpointCases describes case for TestHTTPEndpoints
type HTTPEndpointCases []struct {
URL string
ContentType string
FirstLines []string
}
// TestHTTPEndpoints test a few HTTP endpoints
func TestHTTPEndpoints(t *testing.T, serverAddr net.Addr, cases HTTPEndpointCases) {
t.Helper()
for _, tc := range cases {
t.Run(tc.URL, func(t *testing.T) {
t.Helper()
resp, err := http.Get(fmt.Sprintf("http://%s%s", serverAddr, tc.URL))
if err != nil {
t.Fatalf("GET %s:\n%+v", tc.URL, err)
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t.Fatalf("GET %s: got status code %d, not 200", tc.URL, resp.StatusCode)
}
gotContentType := resp.Header.Get("Content-Type")
if gotContentType != tc.ContentType {
t.Errorf("GET %s Content-Type (-got, +want):\n-%s\n+%s",
tc.URL, gotContentType, tc.ContentType)
}
reader := bufio.NewScanner(resp.Body)
got := []string{}
for reader.Scan() && len(got) < len(tc.FirstLines) {
got = append(got, reader.Text())
}
if diff := Diff(got, tc.FirstLines); diff != "" {
t.Errorf("GET %s (-got, +want):\n%s", tc.URL, diff)
}
})
}
}
// CheckExternalService checks an external service, available either
// as a named service or on a specific port on localhost. This applies
// for example for Kafka and ClickHouse. The timeouts are quite short,
// but we suppose that either the services are run through
// docker-compose manually and ready, either through CI and they are
// checked for readiness.
func CheckExternalService(t *testing.T, name string, dnsCandidates []string, port string) string {
t.Helper()
if testing.Short() {
t.Skipf("Skip test with real %s in short mode", name)
}
mandatory := os.Getenv("CI_AKVORADO_FUNCTIONAL_TESTS") != ""
var err error
found := ""
for _, dnsCandidate := range dnsCandidates {
resolv := net.Resolver{PreferGo: true}
ctx, cancel := context.WithTimeout(context.Background(), 100*time.Millisecond)
_, err = resolv.LookupHost(ctx, dnsCandidate)
cancel()
if err == nil {
found = dnsCandidate
break
}
}
if found == "" {
if mandatory {
t.Fatalf("%s cannot be resolved (CI_AKVORADO_FUNCTIONAL_TESTS is set)", name)
}
t.Skipf("%s cannot be resolved (CI_AKVORADO_FUNCTIONAL_TESTS is not set)", name)
}
var d net.Dialer
server := net.JoinHostPort(found, port)
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
for {
_, err := d.DialContext(ctx, "tcp", server)
if err == nil {
break
}
if mandatory {
t.Logf("DialContext() error:\n%+v", err)
}
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
if mandatory {
t.Fatalf("%s is not running (CI_AKVORADO_FUNCTIONAL_TESTS is set)", name)
} else {
t.Skipf("%s is not running (CI_AKVORADO_FUNCTIONAL_TESTS is not set)", name)
}
}
time.Sleep(100 * time.Millisecond)
}
cancel()
return server
}