Skip to content

Commit

Permalink
build(tests): e2e integration tests with docker and testcontainers (#753
Browse files Browse the repository at this point in the history
)
  • Loading branch information
0xERR0R committed Nov 24, 2022
1 parent d4813a6 commit fb0810f
Show file tree
Hide file tree
Showing 17 changed files with 1,835 additions and 14 deletions.
3 changes: 2 additions & 1 deletion .dockerignore
Expand Up @@ -10,4 +10,5 @@ node_modules
.gitignore
*.md
LICENSE
vendor
vendor
e2e/
27 changes: 27 additions & 0 deletions .github/workflows/e2e-tests.yml
@@ -0,0 +1,27 @@
name: Run e2e tests

on:
push:
pull_request:

jobs:
e2e-test:
name: Build Docker image
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v3
with:
fetch-depth: 0

- name: Set up Go
uses: actions/setup-go@v3
with:
go-version-file: go.mod
id: go

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@v2

- name: Run e2e
run: make e2e-test
1 change: 1 addition & 0 deletions .gitignore
@@ -1,6 +1,7 @@
.idea/
.vscode/
*.iml
*.test
/*.pem
bin/
dist/
Expand Down
1 change: 1 addition & 0 deletions .golangci.yml
Expand Up @@ -77,3 +77,4 @@ issues:
linters:
- gochecknoglobals
- dupl
- gosec
2 changes: 1 addition & 1 deletion Dockerfile
Expand Up @@ -76,4 +76,4 @@ ENV BLOCKY_CONFIG_FILE=/app/config.yml

ENTRYPOINT ["/app/blocky"]

HEALTHCHECK --interval=1m --timeout=3s CMD ["/app/blocky", "healthcheck"]
HEALTHCHECK --start-period=1m --timeout=3s CMD ["/app/blocky", "healthcheck"]
19 changes: 14 additions & 5 deletions Makefile
@@ -1,4 +1,4 @@
.PHONY: all clean build swagger test lint run fmt docker-build help
.PHONY: all clean build swagger test e2e-test lint run fmt docker-build help
.DEFAULT_GOAL:=help

VERSION?=$(shell git describe --always --tags)
Expand Down Expand Up @@ -54,11 +54,20 @@ ifdef BIN_AUTOCAB
setcap 'cap_net_bind_service=+ep' $(GO_BUILD_OUTPUT)
endif

test: ## run tests
go run github.com/onsi/ginkgo/v2/ginkgo -v --coverprofile=coverage.txt --covermode=atomic -cover ./...
test: ## run tests
go run github.com/onsi/ginkgo/v2/ginkgo --label-filter="!e2e" --coverprofile=coverage.txt --covermode=atomic -cover ./...

e2e-test: ## run e2e tests
docker buildx build \
--build-arg VERSION=blocky-e2e \
--network=host \
-o type=docker \
-t blocky-e2e \
.
go run github.com/onsi/ginkgo/v2/ginkgo --label-filter="e2e" ./...

race: ## run tests with race detector
go run github.com/onsi/ginkgo/v2/ginkgo --race ./...
go run github.com/onsi/ginkgo/v2/ginkgo --label-filter="!e2e" --race ./...

lint: ## run golangcli-lint checks
go install github.com/golangci/golangci-lint/cmd/golangci-lint@v1.50.1
Expand All @@ -81,4 +90,4 @@ docker-build: ## Build docker image
.

help: ## Shows help
@grep -E '^[a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
@grep -E '^[0-9a-zA-Z_-]+:.*?## .*$$' $(MAKEFILE_LIST) | sort | awk 'BEGIN {FS = ":.*?## "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
110 changes: 110 additions & 0 deletions e2e/basic_test.go
@@ -0,0 +1,110 @@
package e2e

import (
"context"
"fmt"
"net"
"net/http"

. "github.com/0xERR0R/blocky/helpertest"
"github.com/0xERR0R/blocky/util"
"github.com/miekg/dns"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/testcontainers/testcontainers-go"
)

var _ = Describe("Basic functional tests", func() {
var blocky, moka testcontainers.Container
var err error

Describe("Container start", func() {
BeforeEach(func() {
moka, err = createDNSMokkaContainer("moka1", `A google/NOERROR("A 1.2.3.4 123")`)

Expect(err).Should(Succeed())
DeferCleanup(moka.Terminate)
})
When("Minimal configuration is provided", func() {
BeforeEach(func() {

blocky, err = createBlockyContainer(tmpDir,
"upstream:",
" default:",
" - moka1",
)

Expect(err).Should(Succeed())
DeferCleanup(blocky.Terminate)
})
It("Should start and answer DNS queries", func() {
msg := util.NewMsgWithQuestion("google.de.", dns.Type(dns.TypeA))

Expect(doDNSRequest(blocky, msg)).Should(BeDNSRecord("google.de.", dns.TypeA, 123, "1.2.3.4"))
})
It("should return 'healthy' container status (healthcheck)", func() {
Eventually(func(g Gomega) string {
state, err := blocky.State(context.Background())
g.Expect(err).NotTo(HaveOccurred())

return state.Health.Status
}, "2m", "1s").Should(Equal("healthy"))
})
})
Context("http port configuration", func() {
When("'httpPort' is not defined", func() {
BeforeEach(func() {
blocky, err = createBlockyContainer(tmpDir,
"upstream:",
" default:",
" - moka1",
)

Expect(err).Should(Succeed())
DeferCleanup(blocky.Terminate)
})

It("should not open http port", func() {
host, port, err := getContainerHostPort(blocky, "4000/tcp")
Expect(err).Should(Succeed())

_, err = http.Get(fmt.Sprintf("http://%s", net.JoinHostPort(host, port)))
Expect(err).Should(HaveOccurred())
})
})
When("'httpPort' is defined", func() {
BeforeEach(func() {
blocky, err = createBlockyContainer(tmpDir,
"upstream:",
" default:",
" - moka1",
"httpPort: 4000",
)

Expect(err).Should(Succeed())
DeferCleanup(blocky.Terminate)
})
It("should serve http content", func() {
host, port, err := getContainerHostPort(blocky, "4000/tcp")
Expect(err).Should(Succeed())
url := fmt.Sprintf("http://%s", net.JoinHostPort(host, port))

By("serve static html content", func() {
Eventually(http.Get).WithArguments(url).Should(HaveHTTPStatus(http.StatusOK))
})
By("serve pprof endpoint", func() {
Eventually(http.Get).WithArguments(url + "/debug/").Should(HaveHTTPStatus(http.StatusOK))
})
By("prometheus endpoint should be disabled", func() {
Eventually(http.Get).WithArguments(url + "/metrics").Should(HaveHTTPStatus(http.StatusNotFound))
})
By("serve DoH endpoint", func() {
Eventually(http.Get).WithArguments(url +
"/dns-query?dns=q80BAAABAAAAAAAAA3d3dwdleGFtcGxlA2NvbQAAAQAB").Should(HaveHTTPStatus(http.StatusOK))
})
})
})
})

})
})
117 changes: 117 additions & 0 deletions e2e/blocking_test.go
@@ -0,0 +1,117 @@
package e2e

import (
. "github.com/0xERR0R/blocky/helpertest"
"github.com/0xERR0R/blocky/util"
"github.com/miekg/dns"
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
"github.com/testcontainers/testcontainers-go"
)

var _ = Describe("External lists and query blocking", func() {
var blocky, httpServer, moka testcontainers.Container
var err error
BeforeEach(func() {
moka, err = createDNSMokkaContainer("moka", `A google/NOERROR("A 1.2.3.4 123")`)

Expect(err).Should(Succeed())
DeferCleanup(moka.Terminate)
})
Describe("List download on startup", func() {
When("external blacklist ist not available", func() {
Context("startStrategy = blocking", func() {
BeforeEach(func() {
blocky, err = createBlockyContainer(tmpDir,
"logLevel: warn",
"upstream:",
" default:",
" - moka",
"blocking:",
" startStrategy: blocking",
" blackLists:",
" ads:",
" - http://wrong.domain.url/list.txt",
" clientGroupsBlock:",
" default:",
" - ads",
)

Expect(err).Should(Succeed())
DeferCleanup(blocky.Terminate)
})

It("should start with warning in log work without errors", func() {

msg := util.NewMsgWithQuestion("google.com.", dns.Type(dns.TypeA))

Expect(doDNSRequest(blocky, msg)).Should(BeDNSRecord("google.com.", dns.TypeA, 123, "1.2.3.4"))

Expect(getContainerLogs(blocky)).Should(ContainElement(ContainSubstring("error during file processing")))
})
})
Context("startStrategy = failOnError", func() {
BeforeEach(func() {
blocky, err = createBlockyContainer(tmpDir,
"logLevel: warn",
"upstream:",
" default:",
" - moka",
"blocking:",
" startStrategy: failOnError",
" blackLists:",
" ads:",
" - http://wrong.domain.url/list.txt",
" clientGroupsBlock:",
" default:",
" - ads",
)

Expect(err).Should(HaveOccurred())
DeferCleanup(blocky.Terminate)
})

It("should fail to start", func() {
Expect(blocky.IsRunning()).Should(BeFalse())

Expect(getContainerLogs(blocky)).
Should(ContainElement(ContainSubstring("Error: can't start server: 1 error occurred")))
})
})
})
})
Describe("Query blocking against external blacklists", func() {
When("external blacklists are defined and available", func() {
BeforeEach(func() {
httpServer, err = createHTTPServerContainer("httpserver", tmpDir, "list.txt", "blockeddomain.com")

Expect(err).Should(Succeed())
DeferCleanup(httpServer.Terminate)

blocky, err = createBlockyContainer(tmpDir,
"logLevel: warn",
"upstream:",
" default:",
" - moka",
"blocking:",
" blackLists:",
" ads:",
" - http://httpserver:8080/list.txt",
" clientGroupsBlock:",
" default:",
" - ads",
)

Expect(err).Should(Succeed())
DeferCleanup(blocky.Terminate)
})
It("should download external list on startup and block queries", func() {
msg := util.NewMsgWithQuestion("blockeddomain.com.", dns.Type(dns.TypeA))

Expect(doDNSRequest(blocky, msg)).Should(BeDNSRecord("blockeddomain.com.", dns.TypeA, 6*60*60, "0.0.0.0"))

Expect(getContainerLogs(blocky)).Should(BeEmpty())
})
})
})
})

0 comments on commit fb0810f

Please sign in to comment.