From 35b35c5d67c83da87b6f4eeac5e6bd6242f2d807 Mon Sep 17 00:00:00 2001
From: 6543 <6543@noreply.codeberg.org>
Date: Sat, 11 Jun 2022 23:17:43 +0200
Subject: [PATCH] Add integration tests (#86)

close #82
close #32

make sure we dont get regressions again ... as we currently have in **main**

followups:
 - create a DNS subdomayn specific to redirect to mock url ...

Co-authored-by: 6543 <6543@obermui.de>
Reviewed-on: https://codeberg.org/Codeberg/pages-server/pulls/86
Reviewed-by: crapStone <crapstone@noreply.codeberg.org>
---
 .woodpecker.yml          | 17 ++++++++--
 Justfile                 |  2 +-
 go.mod                   |  1 +
 go.sum                   |  2 ++
 integration/get_test.go  | 69 ++++++++++++++++++++++++++++++++++++++++
 integration/main_test.go | 62 ++++++++++++++++++++++++++++++++++++
 main.go                  |  1 +
 7 files changed, 150 insertions(+), 4 deletions(-)
 create mode 100644 integration/get_test.go
 create mode 100644 integration/main_test.go

diff --git a/.woodpecker.yml b/.woodpecker.yml
index 271aaca..e7fa66a 100644
--- a/.woodpecker.yml
+++ b/.woodpecker.yml
@@ -8,19 +8,30 @@ pipeline:
       - go mod vendor
 
   lint:
-    image: golangci/golangci-lint:v1.45.2
+    image: golangci/golangci-lint:latest
+    pull: true
     commands:
       - go version
       - go install mvdan.cc/gofumpt@latest
       - "[ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }"
-      - golangci-lint run --timeout 5m
+      - golangci-lint run --timeout 5m --build-tags integration
 
   test:
     image: golang:1.18
     commands:
-      - go test ./...
+      - go test -race codeberg.org/codeberg/pages/server/...
 
   build:
     image: golang:1.18
     commands:
       - go build
+
+  integration-tests:
+    image: golang:1.18
+    commands:
+      - go test -race -tags integration codeberg.org/codeberg/pages/integration/...
+    environment:
+      - ACME_API=https://acme.mock.directory
+      - PAGES_DOMAIN=localhost.mock.directory
+      - RAW_DOMAIN=raw.localhost.mock.directory
+      - PORT=4430
\ No newline at end of file
diff --git a/Justfile b/Justfile
index bab0a1e..c5c5a08 100644
--- a/Justfile
+++ b/Justfile
@@ -13,7 +13,7 @@ build:
 
 lint: tool-golangci tool-gofumpt
     [ $(gofumpt -extra -l . | wc -l) != 0 ] && { echo 'code not formated'; exit 1; }; \
-    golangci-lint run --timeout 5m
+    golangci-lint run --timeout 5m --build-tags integration
 
 fmt: tool-gofumpt
     gofumpt -w --extra .
diff --git a/go.mod b/go.mod
index 38a289a..13df431 100644
--- a/go.mod
+++ b/go.mod
@@ -60,6 +60,7 @@ require (
 	github.com/infobloxopen/infoblox-go-client v1.1.1 // indirect
 	github.com/jarcoal/httpmock v1.0.6 // indirect
 	github.com/jmespath/go-jmespath v0.4.0 // indirect
+	github.com/joho/godotenv v1.4.0 // indirect
 	github.com/json-iterator/go v1.1.7 // indirect
 	github.com/k0kubun/go-ansi v0.0.0-20180517002512-3bf9e2903213 // indirect
 	github.com/klauspost/compress v1.13.4 // indirect
diff --git a/go.sum b/go.sum
index a443641..f009f5e 100644
--- a/go.sum
+++ b/go.sum
@@ -266,6 +266,8 @@ github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9Y
 github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8=
 github.com/jmespath/go-jmespath/internal/testify v1.5.1/go.mod h1:L3OGu8Wl2/fWfCI6z80xFu9LTZmf1ZRjMHUOPmWr69U=
+github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
+github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
 github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
 github.com/json-iterator/go v1.1.5/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
 github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
diff --git a/integration/get_test.go b/integration/get_test.go
new file mode 100644
index 0000000..3f2048e
--- /dev/null
+++ b/integration/get_test.go
@@ -0,0 +1,69 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+	"bytes"
+	"crypto/tls"
+	"io"
+	"net/http"
+	"net/http/cookiejar"
+	"testing"
+
+	"github.com/rs/zerolog/log"
+	"github.com/stretchr/testify/assert"
+)
+
+func TestGetRedirect(t *testing.T) {
+	log.Printf("== TestGetRedirect ==\n")
+	// test custom domain redirect
+	resp, err := getTestHTTPSClient().Get("https://calciumdibromid.localhost.mock.directory:4430")
+	assert.NoError(t, err)
+	if !assert.EqualValues(t, http.StatusTemporaryRedirect, resp.StatusCode) {
+		t.FailNow()
+	}
+	assert.EqualValues(t, "https://www.cabr2.de/", resp.Header["Location"][0])
+	assert.EqualValues(t, 0, getSize(resp.Body))
+}
+
+func TestGetContent(t *testing.T) {
+	log.Printf("== TestGetContent ==\n")
+	// test get image
+	resp, err := getTestHTTPSClient().Get("https://magiclike.localhost.mock.directory:4430/images/827679288a.jpg")
+	assert.NoError(t, err)
+	if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) {
+		t.FailNow()
+	}
+	assert.EqualValues(t, "image/jpeg", resp.Header["Content-Type"][0])
+	assert.EqualValues(t, "124635", resp.Header["Content-Length"][0])
+	assert.EqualValues(t, 124635, getSize(resp.Body))
+
+	// specify branch
+	resp, err = getTestHTTPSClient().Get("https://momar.localhost.mock.directory:4430/pag/@master/")
+	assert.NoError(t, err)
+	if !assert.EqualValues(t, http.StatusOK, resp.StatusCode) {
+		t.FailNow()
+	}
+	assert.EqualValues(t, "text/html; charset=utf-8", resp.Header["Content-Type"][0])
+	assert.True(t, getSize(resp.Body) > 1000)
+}
+
+func getTestHTTPSClient() *http.Client {
+	cookieJar, _ := cookiejar.New(nil)
+	return &http.Client{
+		Jar: cookieJar,
+		CheckRedirect: func(_ *http.Request, _ []*http.Request) error {
+			return http.ErrUseLastResponse
+		},
+		Transport: &http.Transport{
+			TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
+		},
+	}
+}
+
+func getSize(stream io.Reader) int {
+	buf := new(bytes.Buffer)
+	_, _ = buf.ReadFrom(stream)
+	return buf.Len()
+}
diff --git a/integration/main_test.go b/integration/main_test.go
new file mode 100644
index 0000000..43f6792
--- /dev/null
+++ b/integration/main_test.go
@@ -0,0 +1,62 @@
+//go:build integration
+// +build integration
+
+package integration
+
+import (
+	"context"
+	"os"
+	"testing"
+	"time"
+
+	"codeberg.org/codeberg/pages/cmd"
+
+	"github.com/rs/zerolog/log"
+	"github.com/urfave/cli/v2"
+)
+
+func TestMain(m *testing.M) {
+	log.Printf("=== TestMain: START Server ==\n")
+	serverCtx, serverCancel := context.WithCancel(context.Background())
+	if err := startServer(serverCtx); err != nil {
+		log.Fatal().Msgf("could not start server: %v", err)
+	}
+	defer func() {
+		serverCancel()
+		log.Printf("=== TestMain: Server STOPED ==\n")
+	}()
+
+	time.Sleep(20 * time.Second)
+
+	os.Exit(m.Run())
+}
+
+func startServer(ctx context.Context) error {
+	args := []string{
+		"--verbose",
+		"--acme-accept-terms", "true",
+	}
+	setEnvIfNotSet("ACME_API", "https://acme.mock.directory")
+	setEnvIfNotSet("PAGES_DOMAIN", "localhost.mock.directory")
+	setEnvIfNotSet("RAW_DOMAIN", "raw.localhost.mock.directory")
+	setEnvIfNotSet("PORT", "4430")
+
+	app := cli.NewApp()
+	app.Name = "pages-server"
+	app.Action = cmd.Serve
+	app.Flags = cmd.ServeFlags
+
+	go func() {
+		if err := app.RunContext(ctx, args); err != nil {
+			log.Fatal().Msgf("run server error: %v", err)
+		}
+	}()
+
+	return nil
+}
+
+func setEnvIfNotSet(key, value string) {
+	if _, set := os.LookupEnv(key); !set {
+		os.Setenv(key, value)
+	}
+}
diff --git a/main.go b/main.go
index 205ec93..2836b86 100644
--- a/main.go
+++ b/main.go
@@ -4,6 +4,7 @@ import (
 	"fmt"
 	"os"
 
+	_ "github.com/joho/godotenv/autoload"
 	"github.com/urfave/cli/v2"
 
 	"codeberg.org/codeberg/pages/cmd"