Skip to content

Commit

Permalink
Refactoring/e2e tests (#1316)
Browse files Browse the repository at this point in the history
* WithNetwork refactoring

* removed tmpDir for blocky

* removed tmpDir from HTTPServer
  • Loading branch information
kwitsch authored Jan 17, 2024
1 parent 49c808f commit 2d3ad83
Show file tree
Hide file tree
Showing 10 changed files with 286 additions and 173 deletions.
10 changes: 5 additions & 5 deletions e2e/basic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("wrong port configuration is provided", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"upstreams:",
" groups:",
" default:",
Expand All @@ -50,7 +50,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("Minimal configuration is provided", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"upstreams:",
" groups:",
" default:",
Expand Down Expand Up @@ -81,7 +81,7 @@ var _ = Describe("Basic functional tests", func() {
Context("http port configuration", func() {
When("'httpPort' is not defined", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"upstreams:",
" groups:",
" default:",
Expand All @@ -101,7 +101,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("'httpPort' is defined", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"upstreams:",
" groups:",
" default:",
Expand Down Expand Up @@ -142,7 +142,7 @@ var _ = Describe("Basic functional tests", func() {
})
When("log privacy is enabled", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"upstreams:",
" groups:",
" default:",
Expand Down
8 changes: 4 additions & 4 deletions e2e/blocking_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ var _ = Describe("External lists and query blocking", func() {
When("external blacklist ist not available", func() {
Context("loading.strategy = blocking", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"log:",
" level: warn",
"upstreams:",
Expand Down Expand Up @@ -56,7 +56,7 @@ var _ = Describe("External lists and query blocking", func() {
})
Context("loading.strategy = failOnError", func() {
BeforeEach(func(ctx context.Context) {
blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"log:",
" level: warn",
"upstreams:",
Expand Down Expand Up @@ -93,10 +93,10 @@ var _ = Describe("External lists and query blocking", func() {
Describe("Query blocking against external blacklists", func() {
When("external blacklists are defined and available", func() {
BeforeEach(func(ctx context.Context) {
_, err = createHTTPServerContainer(ctx, "httpserver", tmpDir, "list.txt", "blockeddomain.com")
_, err = createHTTPServerContainer(ctx, "httpserver", "list.txt", "blockeddomain.com")
Expect(err).Should(Succeed())

blocky, err = createBlockyContainer(ctx, tmpDir,
blocky, err = createBlockyContainer(ctx,
"log:",
" level: warn",
"upstreams:",
Expand Down
176 changes: 61 additions & 115 deletions e2e/containers.go
Original file line number Diff line number Diff line change
@@ -1,35 +1,33 @@
package e2e

import (
"bufio"
"context"
"fmt"
"io"
"net"
"net/http"
"os"
"strings"
"time"

log "github.com/sirupsen/logrus"

"github.com/0xERR0R/blocky/config"
"github.com/0xERR0R/blocky/helpertest"
"github.com/0xERR0R/blocky/util"
"github.com/avast/retry-go/v4"
"github.com/docker/docker/api/types/container"
"github.com/docker/go-connections/nat"
"github.com/miekg/dns"
"github.com/onsi/ginkgo/v2"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/testcontainers/testcontainers-go"
"github.com/testcontainers/testcontainers-go/modules/mariadb"
"github.com/testcontainers/testcontainers-go/modules/postgres"
"github.com/testcontainers/testcontainers-go/modules/redis"
"github.com/testcontainers/testcontainers-go/wait"
)

//nolint:gochecknoglobals
var NetworkName = fmt.Sprintf("blocky-e2e-network_%d", time.Now().Unix())

// container image names
const (
redisImage = "redis:7"
postgresImage = "postgres:15.2-alpine"
Expand All @@ -39,18 +37,15 @@ const (
blockyImage = "blocky-e2e"
)

func deferTerminate[T testcontainers.Container](container T, err error) (T, error) {
ginkgo.DeferCleanup(func(ctx context.Context) error {
if container.IsRunning() {
return container.Terminate(ctx)
}

return nil
})

return container, err
}
// helper constants
const (
modeOwner = 700
startupTimeout = 30 * time.Second
)

// createDNSMokkaContainer creates a DNS mokka container with the given rules attached to the test network
// under the given alias.
// It is automatically terminated when the test is finished.
func createDNSMokkaContainer(ctx context.Context, alias string, rules ...string) (testcontainers.Container, error) {
mokaRules := make(map[string]string)

Expand All @@ -59,66 +54,52 @@ func createDNSMokkaContainer(ctx context.Context, alias string, rules ...string)
}

req := testcontainers.ContainerRequest{
Image: mokaImage,
Networks: []string{NetworkName},
ExposedPorts: []string{"53/tcp", "53/udp"},
NetworkAliases: map[string][]string{NetworkName: {alias}},
WaitingFor: wait.ForExposedPort(),
Env: mokaRules,
Image: mokaImage,
ExposedPorts: []string{"53/tcp", "53/udp"},
WaitingFor: wait.ForExposedPort(),
Env: mokaRules,
}

return deferTerminate(testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}))
return startContainerWithNetwork(ctx, req, alias)
}

func createHTTPServerContainer(ctx context.Context, alias string, tmpDir *helpertest.TmpFolder,
filename string, lines ...string,
// createHTTPServerContainer creates a static HTTP server container that serves one file with the given lines
// and is attached to the test network under the given alias.
// It is automatically terminated when the test is finished.
func createHTTPServerContainer(ctx context.Context, alias string, filename string, lines ...string,
) (testcontainers.Container, error) {
f1 := tmpDir.CreateStringFile(filename,
lines...,
)

const modeOwner = 700
file := createTempFile(lines...)

req := testcontainers.ContainerRequest{
Image: staticServerImage,
Networks: []string{NetworkName},
NetworkAliases: map[string][]string{NetworkName: {alias}},
Image: staticServerImage,

ExposedPorts: []string{"8080/tcp"},
Env: map[string]string{"FOLDER": "/"},
Files: []testcontainers.ContainerFile{
{
HostFilePath: f1.Path,
HostFilePath: file,
ContainerFilePath: fmt.Sprintf("/%s", filename),
FileMode: modeOwner,
},
},
}

return deferTerminate(testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}))
}

func WithNetwork(network string) testcontainers.CustomizeRequestOption {
return func(req *testcontainers.GenericContainerRequest) {
req.NetworkAliases = map[string][]string{NetworkName: {network}}
req.Networks = []string{NetworkName}
}
return startContainerWithNetwork(ctx, req, alias)
}

// createRedisContainer creates a redis container attached to the test network under the alias 'redis'.
// It is automatically terminated when the test is finished.
func createRedisContainer(ctx context.Context) (*redis.RedisContainer, error) {
return deferTerminate(redis.RunContainer(ctx,
testcontainers.WithImage(redisImage),
redis.WithLogLevel(redis.LogLevelVerbose),
WithNetwork("redis"),
WithNetwork(ctx, "redis"),
))
}

// createPostgresContainer creates a postgres container attached to the test network under the alias 'postgres'.
// It creates a database 'user' with user 'user' and password 'user'.
// It is automatically terminated when the test is finished.
func createPostgresContainer(ctx context.Context) (*postgres.PostgresContainer, error) {
const waitLogOccurrence = 2

Expand All @@ -132,46 +113,44 @@ func createPostgresContainer(ctx context.Context) (*postgres.PostgresContainer,
wait.ForLog("database system is ready to accept connections").
WithOccurrence(waitLogOccurrence).
WithStartupTimeout(startupTimeout)),
WithNetwork("postgres"),
WithNetwork(ctx, "postgres"),
))
}

// createMariaDBContainer creates a mariadb container attached to the test network under the alias 'mariaDB'.
// It creates a database 'user' with user 'user' and password 'user'.
// It is automatically terminated when the test is finished.
func createMariaDBContainer(ctx context.Context) (*mariadb.MariaDBContainer, error) {
return deferTerminate(mariadb.RunContainer(ctx,
testcontainers.WithImage(mariaDBImage),
mariadb.WithDatabase("user"),
mariadb.WithUsername("user"),
mariadb.WithPassword("user"),
WithNetwork("mariaDB"),
WithNetwork(ctx, "mariaDB"),
))
}

const (
modeOwner = 700
startupTimeout = 30 * time.Second
)

func createBlockyContainer(ctx context.Context, tmpDir *helpertest.TmpFolder,
// createBlockyContainer creates a blocky container with a config provided by the given lines.
// It is attached to the test network under the alias 'blocky'.
// It is automatically terminated when the test is finished.
func createBlockyContainer(ctx context.Context,
lines ...string,
) (testcontainers.Container, error) {
f1 := tmpDir.CreateStringFile("config1.yaml",
lines...,
)
confFile := createTempFile(lines...)

cfg, err := config.LoadConfig(f1.Path, true)
cfg, err := config.LoadConfig(confFile, true)
if err != nil {
return nil, fmt.Errorf("can't create config struct %w", err)
}

req := testcontainers.ContainerRequest{
Image: blockyImage,
Networks: []string{NetworkName},
Image: blockyImage,

ExposedPorts: []string{"53/tcp", "53/udp", "4000/tcp"},

Files: []testcontainers.ContainerFile{
{
HostFilePath: f1.Path,
HostFilePath: confFile,
ContainerFilePath: "/app/config.yml",
FileMode: modeOwner,
},
Expand All @@ -184,15 +163,12 @@ func createBlockyContainer(ctx context.Context, tmpDir *helpertest.TmpFolder,
WaitingFor: wait.ForHealthCheck().WithStartupTimeout(startupTimeout),
}

container, err := deferTerminate(testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{
ContainerRequest: req,
Started: true,
}))
container, err := startContainerWithNetwork(ctx, req, "blocky")
if err != nil {
// attach container log if error occurs
if r, err := container.Logs(ctx); err == nil {
if b, err := io.ReadAll(r); err == nil {
ginkgo.AddReportEntry("blocky container log", string(b))
AddReportEntry("blocky container log", string(b))
}
}

Expand Down Expand Up @@ -279,55 +255,25 @@ func doHTTPRequest(ctx context.Context, container testcontainers.Container, cont
return err
}

func doDNSRequest(ctx context.Context, container testcontainers.Container, message *dns.Msg) (*dns.Msg, error) {
const timeout = 5 * time.Second
// createTempFile creates a temporary file with the given lines which is deleted after the test
// Each created file is prefixed with 'blocky_e2e_file-'
func createTempFile(lines ...string) string {
file, err := os.CreateTemp("", "blocky_e2e_file-")
Expect(err).Should(Succeed())

c := &dns.Client{
Net: "tcp",
Timeout: timeout,
}

host, port, err := getContainerHostPort(ctx, container, "53/tcp")
if err != nil {
return nil, err
}

msg, _, err := c.Exchange(message, net.JoinHostPort(host, port))

return msg, err
}

func getContainerHostPort(ctx context.Context, c testcontainers.Container, p nat.Port) (host, port string, err error) {
res, err := c.MappedPort(ctx, p)
if err != nil {
return "", "", err
}

host, err = c.Host(ctx)

if err != nil {
return "", "", err
}

return host, res.Port(), err
}

func getContainerLogs(ctx context.Context, c testcontainers.Container) (lines []string, err error) {
if r, err := c.Logs(ctx); err == nil {
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := scanner.Text()
if len(strings.TrimSpace(line)) > 0 {
lines = append(lines, line)
}
}
DeferCleanup(func() error {
return os.Remove(file.Name())
})

if err := scanner.Err(); err != nil {
return nil, err
for i, l := range lines {
if i != 0 {
_, err := file.WriteString("\n")
Expect(err).Should(Succeed())
}

return lines, nil
_, err := file.WriteString(l)
Expect(err).Should(Succeed())
}

return nil, err
return file.Name()
}
Loading

0 comments on commit 2d3ad83

Please sign in to comment.