From 93a185302216c404602cb7635eac5cb31cbf6cd7 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 18:16:33 -0500
Subject: [PATCH 01/14] noot
---
caddy.go | 6 +-
caddytest/caddytest.go | 78 +++++++++++++++++-------
caddytest/integration/acme_test.go | 41 ++++++-------
caddytest/integration/acmeserver_test.go | 16 ++---
caddytest/integration/stream_test.go | 11 +---
logging.go | 9 ++-
6 files changed, 96 insertions(+), 65 deletions(-)
diff --git a/caddy.go b/caddy.go
index 7dd989c9e1e..9aba97fa415 100644
--- a/caddy.go
+++ b/caddy.go
@@ -20,6 +20,7 @@ import (
"encoding/hex"
"encoding/json"
"errors"
+ "flag"
"fmt"
"io"
"io/fs"
@@ -778,7 +779,10 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
} else {
logger.Error("unclean shutdown")
}
- os.Exit(exitCode)
+ // check if we are in test environment, and dont call exit if we are
+ if flag.Lookup("test.v") == nil || strings.Contains(os.Args[0], ".test") {
+ os.Exit(exitCode)
+ }
}()
if remoteAdminServer != nil {
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 05aa1e3f52f..d4a0a6c8760 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -81,6 +81,10 @@ func NewTester(t testing.TB) *Tester {
}
}
+func (t *Tester) T() testing.TB {
+ return t.t
+}
+
type configLoadError struct {
Response string
}
@@ -92,33 +96,37 @@ func timeElapsed(start time.Time, name string) {
log.Printf("%s took %s", name, elapsed)
}
-// InitServer this will configure the server with a configurion of a specific
-// type. The configType must be either "json" or the adapter type.
+// launch caddy will start the server
+func (tc *Tester) LaunchCaddy() {
+ if err := tc.startServer(); err != nil {
+ tc.t.Logf("failed to start server: %s", err)
+ tc.t.Fail()
+ }
+}
+
+// DEPRECATED: InitServer
+// Initserver is shorthand for LaunchCaddy() and LoadConfig(rawConfig, configType string)
func (tc *Tester) InitServer(rawConfig string, configType string) {
- if err := tc.initServer(rawConfig, configType); err != nil {
- tc.t.Logf("failed to load config: %s", err)
+ if err := tc.startServer(); err != nil {
+ tc.t.Logf("failed to start server: %s", err)
tc.t.Fail()
}
- if err := tc.ensureConfigRunning(rawConfig, configType); err != nil {
+ if err := tc.LoadConfig(rawConfig, configType); err != nil {
tc.t.Logf("failed ensuring config is running: %s", err)
tc.t.Fail()
}
}
-// InitServer this will configure the server with a configurion of a specific
-// type. The configType must be either "json" or the adapter type.
-func (tc *Tester) initServer(rawConfig string, configType string) error {
+func (tc *Tester) startServer() error {
if testing.Short() {
tc.t.SkipNow()
return nil
}
-
- err := validateTestPrerequisites(tc.t)
+ err := validateAndStartServer(tc.t)
if err != nil {
tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
return nil
}
-
tc.t.Cleanup(func() {
if tc.t.Failed() && tc.configLoaded {
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
@@ -133,8 +141,35 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
_ = json.Indent(&out, body, "", " ")
tc.t.Logf("----------- failed with config -----------\n%s", out.String())
}
+ // now shutdown the server, since the test is done.
+
+ _, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
+ if err != nil {
+ tc.t.Log("couldn't stop admin server")
+ }
+ time.Sleep(1 * time.Millisecond)
+ // try ensure the admin api is stopped three times.
+ for retries := 0; retries < 3; retries++ {
+ if isCaddyAdminRunning() != nil {
+ return
+ }
+ time.Sleep(10 * time.Millisecond)
+ tc.t.Log("timed out waiting for admin server to stop")
+ }
})
+ return nil
+}
+
+func (tc *Tester) MustLoadConfig(rawConfig string, configType string) {
+ if err := tc.LoadConfig(rawConfig, configType); err != nil {
+ tc.t.Logf("failed ensuring config is running: %s", err)
+ tc.t.Fail()
+ }
+}
+// LoadConfig loads the config to the tester server and also ensures that the config was loaded
+func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
+ originalRawConfig := rawConfig
rawConfig = prependCaddyFilePath(rawConfig)
// normalize JSON config
if configType == "json" {
@@ -185,7 +220,7 @@ func (tc *Tester) initServer(rawConfig string, configType string) error {
}
tc.configLoaded = true
- return nil
+ return tc.ensureConfigRunning(originalRawConfig, configType)
}
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
@@ -226,7 +261,9 @@ func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error
return actual
}
- for retries := 10; retries > 0; retries-- {
+ // TODO: does this really need to be tried more than once?
+ // Caddy should block until the new config is loaded, which means needing to wait is a caddy bug
+ for retries := 3; retries > 0; retries-- {
if reflect.DeepEqual(expected, fetchConfig(client)) {
return nil
}
@@ -241,9 +278,9 @@ const initConfig = `{
}
`
-// validateTestPrerequisites ensures the certificates are available in the
-// designated path and Caddy sub-process is running.
-func validateTestPrerequisites(t testing.TB) error {
+// validateAndStartServer ensures the certificates are available in the
+// designated path, launches caddy, and then ensures the Caddy sub-process is running.
+func validateAndStartServer(t testing.TB) error {
// check certificates are found
for _, certName := range Default.Certificates {
if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
@@ -269,9 +306,8 @@ func validateTestPrerequisites(t testing.TB) error {
go func() {
caddycmd.Main()
}()
-
- // wait for caddy to start serving the initial config
- for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
+ // wait for caddy admin api to start. it should happen quickly.
+ for retries := 3; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
time.Sleep(1 * time.Second)
}
}
@@ -342,8 +378,8 @@ func CreateTestingTransport() *http.Transport {
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
tc := NewTester(t)
-
- err := tc.initServer(rawConfig, configType)
+ tc.LaunchCaddy()
+ err := tc.LoadConfig(rawConfig, configType)
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
diff --git a/caddytest/integration/acme_test.go b/caddytest/integration/acme_test.go
index ceacd1db0fa..43f99b15e7e 100644
--- a/caddytest/integration/acme_test.go
+++ b/caddytest/integration/acme_test.go
@@ -19,19 +19,27 @@ import (
"go.uber.org/zap"
)
+func curry2[A, B any](fn func(A, B)) func(a A) func(b B) {
+ return func(a A) func(B) {
+ return func(b B) {
+ fn(a, b)
+ }
+ }
+}
+
const acmeChallengePort = 9081
+func TestAcmeServer(t *testing.T) {
+ tester := caddytest.NewTester(t)
+ tester.LaunchCaddy()
+ t.Run("WithDefaults", curry2(testACMEServerWithDefaults)(tester))
+ t.Run("WithMismatchedChallenges", curry2(testACMEServerWithDefaults)(tester))
+}
+
// Test the basic functionality of Caddy's ACME server
-func TestACMEServerWithDefaults(t *testing.T) {
+func testACMEServerWithDefaults(tester *caddytest.Tester, t *testing.T) {
ctx := context.Background()
- logger, err := zap.NewDevelopment()
- if err != nil {
- t.Error(err)
- return
- }
-
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester.MustLoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -44,6 +52,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
}
`, "caddyfile")
+ logger := caddy.Log().Named("acmeserver")
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
@@ -93,20 +102,10 @@ func TestACMEServerWithDefaults(t *testing.T) {
}
}
-func TestACMEServerWithMismatchedChallenges(t *testing.T) {
+func testACMEServerWithMismatchedChallenges(tester *caddytest.Tester, t *testing.T) {
ctx := context.Background()
logger := caddy.Log().Named("acmez")
-
- tester := caddytest.NewTester(t)
- tester.InitServer(`
- {
- skip_install_trust
- admin localhost:2999
- http_port 9080
- https_port 9443
- local_certs
- }
- acme.localhost {
+ tester.MustLoadConfig(`
acme_server {
challenges tls-alpn-01
}
diff --git a/caddytest/integration/acmeserver_test.go b/caddytest/integration/acmeserver_test.go
index 22b716f84db..f05fb836e15 100644
--- a/caddytest/integration/acmeserver_test.go
+++ b/caddytest/integration/acmeserver_test.go
@@ -8,10 +8,10 @@ import (
"strings"
"testing"
+ "github.com/caddyserver/caddy/v2"
"github.com/caddyserver/caddy/v2/caddytest"
"github.com/mholt/acmez/v2"
"github.com/mholt/acmez/v2/acme"
- "go.uber.org/zap"
)
func TestACMEServerDirectory(t *testing.T) {
@@ -66,11 +66,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
`, "caddyfile")
ctx := context.Background()
- logger, err := zap.NewDevelopment()
- if err != nil {
- t.Error(err)
- return
- }
+ logger := caddy.Log().Named("acmez")
client := acmez.Client{
Client: &acme.Client{
@@ -155,11 +151,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
`, "caddyfile")
ctx := context.Background()
- logger, err := zap.NewDevelopment()
- if err != nil {
- t.Error(err)
- return
- }
+ logger := caddy.Log().Named("acmez")
client := acmez.Client{
Client: &acme.Client{
@@ -197,7 +189,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
_, err := client.ObtainCertificateForSANs(ctx, account, certPrivateKey, []string{"deny.localhost"})
if err == nil {
t.Errorf("obtaining certificate for 'deny.localhost' domain")
- } else if err != nil && !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
+ } else if !strings.Contains(err.Error(), "urn:ietf:params:acme:error:rejectedIdentifier") {
t.Logf("unexpected error: %v", err)
}
}
diff --git a/caddytest/integration/stream_test.go b/caddytest/integration/stream_test.go
index d2f2fd79b95..d82882d6997 100644
--- a/caddytest/integration/stream_test.go
+++ b/caddytest/integration/stream_test.go
@@ -21,7 +21,7 @@ import (
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
func TestH2ToH2CStream(t *testing.T) {
tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester.InitServer(`
{
"admin": {
"listen": "localhost:2999"
@@ -205,18 +205,11 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
func TestH2ToH1ChunkedResponse(t *testing.T) {
tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester.InitServer(`
{
"admin": {
"listen": "localhost:2999"
},
- "logging": {
- "logs": {
- "default": {
- "level": "DEBUG"
- }
- }
- },
"apps": {
"http": {
"http_port": 9080,
diff --git a/logging.go b/logging.go
index ca10beeeddc..09bf8f846bc 100644
--- a/logging.go
+++ b/logging.go
@@ -16,6 +16,7 @@ package caddy
import (
"encoding/json"
+ "flag"
"fmt"
"io"
"log"
@@ -699,7 +700,13 @@ type defaultCustomLog struct {
// and enables INFO-level logs and higher.
func newDefaultProductionLog() (*defaultCustomLog, error) {
cl := new(CustomLog)
- cl.writerOpener = StderrWriter{}
+ f := flag.Lookup("test.v")
+ if (f != nil && f.Value.String() != "true") || strings.Contains(os.Args[0], ".test") {
+ cl.writerOpener = &DiscardWriter{}
+ } else {
+ cl.writerOpener = StderrWriter{}
+ }
+
var err error
cl.writer, err = cl.writerOpener.OpenWriter()
if err != nil {
From 2619271a5c7366215d3319b336bda47b99ad5bf1 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 19:44:05 -0500
Subject: [PATCH 02/14] noot
---
caddytest/caddytest.go | 407 +++---------------
caddytest/caddytest_assert.go | 109 +++++
caddytest/caddytest_test.go | 8 +-
caddytest/integration/acme_test.go | 38 +-
caddytest/integration/acmeserver_test.go | 16 +-
caddytest/integration/autohttps_test.go | 24 +-
caddytest/integration/caddyfile_test.go | 130 +++---
caddytest/integration/handler_test.go | 8 +-
caddytest/integration/intercept_test.go | 4 +-
caddytest/integration/leafcertloaders_test.go | 4 +-
caddytest/integration/listener_test.go | 10 +-
caddytest/integration/map_test.go | 12 +-
caddytest/integration/reverseproxy_test.go | 28 +-
caddytest/integration/sni_test.go | 12 +-
caddytest/integration/stream_test.go | 8 +-
caddytest/testing_harness.go | 223 ++++++++++
16 files changed, 530 insertions(+), 511 deletions(-)
create mode 100644 caddytest/caddytest_assert.go
create mode 100644 caddytest/testing_harness.go
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index d4a0a6c8760..f22a09ef25e 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -1,14 +1,12 @@
package caddytest
import (
- "bytes"
"context"
"crypto/tls"
"encoding/json"
"errors"
"fmt"
"io"
- "io/fs"
"log"
"net"
"net/http"
@@ -16,14 +14,10 @@ import (
"os"
"path"
"reflect"
- "regexp"
"runtime"
"strings"
- "testing"
"time"
- "github.com/aryann/difflib"
-
caddycmd "github.com/caddyserver/caddy/v2/cmd"
"github.com/caddyserver/caddy/v2/caddyconfig"
@@ -51,23 +45,18 @@ var Default = Defaults{
LoadRequestTimeout: 5 * time.Second,
}
-var (
- matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
- matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
-)
-
// Tester represents an instance of a test client.
type Tester struct {
- Client *http.Client
- configLoaded bool
- t testing.TB
+ Client *http.Client
+ configLoaded bool
+ configFileName string
}
// NewTester will create a new testing client with an attached cookie jar
-func NewTester(t testing.TB) *Tester {
+func NewTester() (*Tester, error) {
jar, err := cookiejar.New(nil)
if err != nil {
- t.Fatalf("failed to create cookiejar: %s", err)
+ return nil, fmt.Errorf("failed to create cookiejar: %w", err)
}
return &Tester{
@@ -77,12 +66,7 @@ func NewTester(t testing.TB) *Tester {
Timeout: Default.TestRequestTimeout,
},
configLoaded: false,
- t: t,
- }
-}
-
-func (t *Tester) T() testing.TB {
- return t.t
+ }, nil
}
type configLoadError struct {
@@ -97,83 +81,40 @@ func timeElapsed(start time.Time, name string) {
}
// launch caddy will start the server
-func (tc *Tester) LaunchCaddy() {
+func (tc *Tester) LaunchCaddy() error {
if err := tc.startServer(); err != nil {
- tc.t.Logf("failed to start server: %s", err)
- tc.t.Fail()
- }
-}
-
-// DEPRECATED: InitServer
-// Initserver is shorthand for LaunchCaddy() and LoadConfig(rawConfig, configType string)
-func (tc *Tester) InitServer(rawConfig string, configType string) {
- if err := tc.startServer(); err != nil {
- tc.t.Logf("failed to start server: %s", err)
- tc.t.Fail()
- }
- if err := tc.LoadConfig(rawConfig, configType); err != nil {
- tc.t.Logf("failed ensuring config is running: %s", err)
- tc.t.Fail()
+ return fmt.Errorf("failed to start server: %w", err)
}
+ return nil
}
-func (tc *Tester) startServer() error {
- if testing.Short() {
- tc.t.SkipNow()
- return nil
- }
- err := validateAndStartServer(tc.t)
+func (tc *Tester) CleanupCaddy() error {
+ // now shutdown the server, since the test is done.
+ defer func() {
+ // try to remove the tmp config file we created
+ os.Remove(tc.configFileName)
+ }()
+ _, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
if err != nil {
- tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
- return nil
+ return fmt.Errorf("couldn't stop caddytest server")
}
- tc.t.Cleanup(func() {
- if tc.t.Failed() && tc.configLoaded {
- res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
- if err != nil {
- tc.t.Log("unable to read the current config")
- return
- }
- defer res.Body.Close()
- body, _ := io.ReadAll(res.Body)
-
- var out bytes.Buffer
- _ = json.Indent(&out, body, "", " ")
- tc.t.Logf("----------- failed with config -----------\n%s", out.String())
+ time.Sleep(200 * time.Millisecond)
+ for retries := 0; retries < 10; retries++ {
+ if isCaddyAdminRunning() != nil {
+ return nil
}
- // now shutdown the server, since the test is done.
+ time.Sleep(100 * time.Millisecond)
+ }
- _, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
- if err != nil {
- tc.t.Log("couldn't stop admin server")
- }
- time.Sleep(1 * time.Millisecond)
- // try ensure the admin api is stopped three times.
- for retries := 0; retries < 3; retries++ {
- if isCaddyAdminRunning() != nil {
- return
- }
- time.Sleep(10 * time.Millisecond)
- tc.t.Log("timed out waiting for admin server to stop")
- }
- })
- return nil
-}
+ return fmt.Errorf("timed out waiting for caddytest server to stop")
-func (tc *Tester) MustLoadConfig(rawConfig string, configType string) {
- if err := tc.LoadConfig(rawConfig, configType); err != nil {
- tc.t.Logf("failed ensuring config is running: %s", err)
- tc.t.Fail()
- }
}
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
originalRawConfig := rawConfig
- rawConfig = prependCaddyFilePath(rawConfig)
// normalize JSON config
if configType == "json" {
- tc.t.Logf("Before: %s", rawConfig)
var conf any
if err := json.Unmarshal([]byte(rawConfig), &conf); err != nil {
return err
@@ -183,7 +124,6 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
return err
}
rawConfig = string(c)
- tc.t.Logf("After: %s", rawConfig)
}
client := &http.Client{
Timeout: Default.LoadRequestTimeout,
@@ -191,8 +131,7 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
start := time.Now()
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", Default.AdminPort), strings.NewReader(rawConfig))
if err != nil {
- tc.t.Errorf("failed to create request. %s", err)
- return err
+ return fmt.Errorf("failed to create request. %w", err)
}
if configType == "json" {
@@ -203,16 +142,14 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
res, err := client.Do(req)
if err != nil {
- tc.t.Errorf("unable to contact caddy server. %s", err)
- return err
+ return fmt.Errorf("unable to contact caddy server. %w", err)
}
timeElapsed(start, "caddytest: config load time")
defer res.Body.Close()
body, err := io.ReadAll(res.Body)
if err != nil {
- tc.t.Errorf("unable to read response. %s", err)
- return err
+ return fmt.Errorf("unable to read response. %w", err)
}
if res.StatusCode != 200 {
@@ -224,7 +161,7 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
}
func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
- expectedBytes := []byte(prependCaddyFilePath(rawConfig))
+ expectedBytes := []byte(rawConfig)
if configType != "json" {
adapter := caddyconfig.GetAdapter(configType)
if adapter == nil {
@@ -269,7 +206,6 @@ func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error
}
time.Sleep(1 * time.Second)
}
- tc.t.Errorf("POSTed configuration isn't active")
return errors.New("EnsureConfigRunning: POSTed configuration isn't active")
}
@@ -278,38 +214,30 @@ const initConfig = `{
}
`
-// validateAndStartServer ensures the certificates are available in the
-// designated path, launches caddy, and then ensures the Caddy sub-process is running.
-func validateAndStartServer(t testing.TB) error {
- // check certificates are found
- for _, certName := range Default.Certificates {
- if _, err := os.Stat(getIntegrationDir() + certName); errors.Is(err, fs.ErrNotExist) {
- return fmt.Errorf("caddy integration test certificates (%s) not found", certName)
- }
+// launches caddy, and then ensures the Caddy sub-process is running.
+func (tc *Tester) startServer() error {
+ if isCaddyAdminRunning() == nil {
+ return fmt.Errorf("caddy test admin port still in use")
+ }
+ // setup the init config file, and set the cleanup afterwards
+ f, err := os.CreateTemp("", "")
+ if err != nil {
+ return err
}
+ tc.configFileName = f.Name()
- if isCaddyAdminRunning() != nil {
- // setup the init config file, and set the cleanup afterwards
- f, err := os.CreateTemp("", "")
- if err != nil {
- return err
- }
- t.Cleanup(func() {
- os.Remove(f.Name())
- })
- if _, err := f.WriteString(initConfig); err != nil {
- return err
- }
+ if _, err := f.WriteString(initConfig); err != nil {
+ return err
+ }
- // start inprocess caddy server
- os.Args = []string{"caddy", "run", "--config", f.Name(), "--adapter", "caddyfile"}
- go func() {
- caddycmd.Main()
- }()
- // wait for caddy admin api to start. it should happen quickly.
- for retries := 3; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
- time.Sleep(1 * time.Second)
- }
+ // start inprocess caddy server
+ os.Args = []string{"caddy", "run", "--config", f.Name(), "--adapter", "caddyfile"}
+ go func() {
+ caddycmd.Main()
+ }()
+ // wait for caddy admin api to start. it should happen quickly.
+ for retries := 3; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
+ time.Sleep(1 * time.Second)
}
// one more time to return the error
@@ -339,15 +267,6 @@ func getIntegrationDir() string {
return path.Dir(filename)
}
-// use the convention to replace /[certificatename].[crt|key] with the full path
-// this helps reduce the noise in test configurations and also allow this
-// to run in any path
-func prependCaddyFilePath(rawConfig string) string {
- r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
- r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
- return r
-}
-
// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
func CreateTestingTransport() *http.Transport {
dialer := net.Dialer{
@@ -374,231 +293,3 @@ func CreateTestingTransport() *http.Transport {
TLSClientConfig: &tls.Config{InsecureSkipVerify: true}, //nolint:gosec
}
}
-
-// AssertLoadError will load a config and expect an error
-func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
- tc := NewTester(t)
- tc.LaunchCaddy()
- err := tc.LoadConfig(rawConfig, configType)
- if !strings.Contains(err.Error(), expectedError) {
- t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
- }
-}
-
-// AssertRedirect makes a request and asserts the redirection happens
-func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
- redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
- return http.ErrUseLastResponse
- }
-
- // using the existing client, we override the check redirect policy for this test
- old := tc.Client.CheckRedirect
- tc.Client.CheckRedirect = redirectPolicyFunc
- defer func() { tc.Client.CheckRedirect = old }()
-
- resp, err := tc.Client.Get(requestURI)
- if err != nil {
- tc.t.Errorf("failed to call server %s", err)
- return nil
- }
-
- if expectedStatusCode != resp.StatusCode {
- tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
- }
-
- loc, err := resp.Location()
- if err != nil {
- tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
- }
- if loc == nil && expectedToLocation != "" {
- tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
- }
- if loc != nil {
- if expectedToLocation != loc.String() {
- tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
- }
- }
-
- return resp
-}
-
-// CompareAdapt adapts a config and then compares it against an expected result
-func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
- cfgAdapter := caddyconfig.GetAdapter(adapterName)
- if cfgAdapter == nil {
- t.Logf("unrecognized config adapter '%s'", adapterName)
- return false
- }
-
- options := make(map[string]any)
-
- result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
- if err != nil {
- t.Logf("adapting config using %s adapter: %v", adapterName, err)
- return false
- }
-
- // prettify results to keep tests human-manageable
- var prettyBuf bytes.Buffer
- err = json.Indent(&prettyBuf, result, "", "\t")
- if err != nil {
- return false
- }
- result = prettyBuf.Bytes()
-
- if len(warnings) > 0 {
- for _, w := range warnings {
- t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
- }
- }
-
- diff := difflib.Diff(
- strings.Split(expectedResponse, "\n"),
- strings.Split(string(result), "\n"))
-
- // scan for failure
- failed := false
- for _, d := range diff {
- if d.Delta != difflib.Common {
- failed = true
- break
- }
- }
-
- if failed {
- for _, d := range diff {
- switch d.Delta {
- case difflib.Common:
- fmt.Printf(" %s\n", d.Payload)
- case difflib.LeftOnly:
- fmt.Printf(" - %s\n", d.Payload)
- case difflib.RightOnly:
- fmt.Printf(" + %s\n", d.Payload)
- }
- }
- return false
- }
- return true
-}
-
-// AssertAdapt adapts a config and then tests it against an expected result
-func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
- ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
- if !ok {
- t.Fail()
- }
-}
-
-// Generic request functions
-
-func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
- requestContentType := ""
- for _, requestHeader := range requestHeaders {
- arr := strings.SplitAfterN(requestHeader, ":", 2)
- k := strings.TrimRight(arr[0], ":")
- v := strings.TrimSpace(arr[1])
- if k == "Content-Type" {
- requestContentType = v
- }
- t.Logf("Request header: %s => %s", k, v)
- req.Header.Set(k, v)
- }
-
- if requestContentType == "" {
- t.Logf("Content-Type header not provided")
- }
-}
-
-// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
-func (tc *Tester) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
- resp, err := tc.Client.Do(req)
- if err != nil {
- tc.t.Fatalf("failed to call server %s", err)
- }
-
- if expectedStatusCode != resp.StatusCode {
- tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
- }
-
- return resp
-}
-
-// AssertResponse request a URI and assert the status code and the body contains a string
-func (tc *Tester) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- resp := tc.AssertResponseCode(req, expectedStatusCode)
-
- defer resp.Body.Close()
- bytes, err := io.ReadAll(resp.Body)
- if err != nil {
- tc.t.Fatalf("unable to read the response body %s", err)
- }
-
- body := string(bytes)
-
- if body != expectedBody {
- tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
- }
-
- return resp, body
-}
-
-// Verb specific test functions
-
-// AssertGetResponse GET a URI and expect a statusCode and body text
-func (tc *Tester) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- req, err := http.NewRequest("GET", requestURI, nil)
- if err != nil {
- tc.t.Fatalf("unable to create request %s", err)
- }
-
- return tc.AssertResponse(req, expectedStatusCode, expectedBody)
-}
-
-// AssertDeleteResponse request a URI and expect a statusCode and body text
-func (tc *Tester) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- req, err := http.NewRequest("DELETE", requestURI, nil)
- if err != nil {
- tc.t.Fatalf("unable to create request %s", err)
- }
-
- return tc.AssertResponse(req, expectedStatusCode, expectedBody)
-}
-
-// AssertPostResponseBody POST to a URI and assert the response code and body
-func (tc *Tester) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- req, err := http.NewRequest("POST", requestURI, requestBody)
- if err != nil {
- tc.t.Errorf("failed to create request %s", err)
- return nil, ""
- }
-
- applyHeaders(tc.t, req, requestHeaders)
-
- return tc.AssertResponse(req, expectedStatusCode, expectedBody)
-}
-
-// AssertPutResponseBody PUT to a URI and assert the response code and body
-func (tc *Tester) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- req, err := http.NewRequest("PUT", requestURI, requestBody)
- if err != nil {
- tc.t.Errorf("failed to create request %s", err)
- return nil, ""
- }
-
- applyHeaders(tc.t, req, requestHeaders)
-
- return tc.AssertResponse(req, expectedStatusCode, expectedBody)
-}
-
-// AssertPatchResponseBody PATCH to a URI and assert the response code and body
-func (tc *Tester) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
- req, err := http.NewRequest("PATCH", requestURI, requestBody)
- if err != nil {
- tc.t.Errorf("failed to create request %s", err)
- return nil, ""
- }
-
- applyHeaders(tc.t, req, requestHeaders)
-
- return tc.AssertResponse(req, expectedStatusCode, expectedBody)
-}
diff --git a/caddytest/caddytest_assert.go b/caddytest/caddytest_assert.go
new file mode 100644
index 00000000000..d81764d667e
--- /dev/null
+++ b/caddytest/caddytest_assert.go
@@ -0,0 +1,109 @@
+package caddytest
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+ "testing"
+
+ "github.com/aryann/difflib"
+ "github.com/caddyserver/caddy/v2/caddyconfig"
+)
+
+// AssertLoadError will load a config and expect an error
+func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
+ tc := StartHarness(t)
+ err := tc.tester.LoadConfig(rawConfig, configType)
+ if !strings.Contains(err.Error(), expectedError) {
+ t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
+ }
+}
+
+// CompareAdapt adapts a config and then compares it against an expected result
+func CompareAdapt(t testing.TB, filename, rawConfig string, adapterName string, expectedResponse string) bool {
+ cfgAdapter := caddyconfig.GetAdapter(adapterName)
+ if cfgAdapter == nil {
+ t.Logf("unrecognized config adapter '%s'", adapterName)
+ return false
+ }
+
+ options := make(map[string]any)
+
+ result, warnings, err := cfgAdapter.Adapt([]byte(rawConfig), options)
+ if err != nil {
+ t.Logf("adapting config using %s adapter: %v", adapterName, err)
+ return false
+ }
+
+ // prettify results to keep tests human-manageable
+ var prettyBuf bytes.Buffer
+ err = json.Indent(&prettyBuf, result, "", "\t")
+ if err != nil {
+ return false
+ }
+ result = prettyBuf.Bytes()
+
+ if len(warnings) > 0 {
+ for _, w := range warnings {
+ t.Logf("warning: %s:%d: %s: %s", filename, w.Line, w.Directive, w.Message)
+ }
+ }
+
+ diff := difflib.Diff(
+ strings.Split(expectedResponse, "\n"),
+ strings.Split(string(result), "\n"))
+
+ // scan for failure
+ failed := false
+ for _, d := range diff {
+ if d.Delta != difflib.Common {
+ failed = true
+ break
+ }
+ }
+
+ if failed {
+ for _, d := range diff {
+ switch d.Delta {
+ case difflib.Common:
+ fmt.Printf(" %s\n", d.Payload)
+ case difflib.LeftOnly:
+ fmt.Printf(" - %s\n", d.Payload)
+ case difflib.RightOnly:
+ fmt.Printf(" + %s\n", d.Payload)
+ }
+ }
+ return false
+ }
+ return true
+}
+
+// AssertAdapt adapts a config and then tests it against an expected result
+func AssertAdapt(t testing.TB, rawConfig string, adapterName string, expectedResponse string) {
+ ok := CompareAdapt(t, "Caddyfile", rawConfig, adapterName, expectedResponse)
+ if !ok {
+ t.Fail()
+ }
+}
+
+// Generic request functions
+
+func applyHeaders(t testing.TB, req *http.Request, requestHeaders []string) {
+ requestContentType := ""
+ for _, requestHeader := range requestHeaders {
+ arr := strings.SplitAfterN(requestHeader, ":", 2)
+ k := strings.TrimRight(arr[0], ":")
+ v := strings.TrimSpace(arr[1])
+ if k == "Content-Type" {
+ requestContentType = v
+ }
+ t.Logf("Request header: %s => %s", k, v)
+ req.Header.Set(k, v)
+ }
+
+ if requestContentType == "" {
+ t.Logf("Content-Type header not provided")
+ }
+}
diff --git a/caddytest/caddytest_test.go b/caddytest/caddytest_test.go
index 937537faa7c..589c33ea6dc 100644
--- a/caddytest/caddytest_test.go
+++ b/caddytest/caddytest_test.go
@@ -12,10 +12,10 @@ func TestReplaceCertificatePaths(t *testing.T) {
}
redir / https://b.caddy.localhost:9443/version 301
-
+
respond /version 200 {
body "hello from a.caddy.localhost"
- }
+ }
}`
r := prependCaddyFilePath(rawConfig)
@@ -34,8 +34,8 @@ func TestReplaceCertificatePaths(t *testing.T) {
}
func TestLoadUnorderedJSON(t *testing.T) {
- tester := NewTester(t)
- tester.InitServer(`
+ tester := StartHarness(t)
+ tester.LoadConfig(`
{
"logging": {
"logs": {
diff --git a/caddytest/integration/acme_test.go b/caddytest/integration/acme_test.go
index 43f99b15e7e..9697cf9551b 100644
--- a/caddytest/integration/acme_test.go
+++ b/caddytest/integration/acme_test.go
@@ -19,27 +19,13 @@ import (
"go.uber.org/zap"
)
-func curry2[A, B any](fn func(A, B)) func(a A) func(b B) {
- return func(a A) func(B) {
- return func(b B) {
- fn(a, b)
- }
- }
-}
-
const acmeChallengePort = 9081
-func TestAcmeServer(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.LaunchCaddy()
- t.Run("WithDefaults", curry2(testACMEServerWithDefaults)(tester))
- t.Run("WithMismatchedChallenges", curry2(testACMEServerWithDefaults)(tester))
-}
-
// Test the basic functionality of Caddy's ACME server
-func testACMEServerWithDefaults(tester *caddytest.Tester, t *testing.T) {
+func TestACMEServerWithDefaults(t *testing.T) {
ctx := context.Background()
- tester.MustLoadConfig(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -56,7 +42,7 @@ func testACMEServerWithDefaults(tester *caddytest.Tester, t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client,
+ HTTPClient: tester.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -102,10 +88,20 @@ func testACMEServerWithDefaults(tester *caddytest.Tester, t *testing.T) {
}
}
-func testACMEServerWithMismatchedChallenges(tester *caddytest.Tester, t *testing.T) {
+func TestACMEServerWithMismatchedChallenges(t *testing.T) {
ctx := context.Background()
logger := caddy.Log().Named("acmez")
- tester.MustLoadConfig(`
+
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
+ {
+ skip_install_trust
+ admin localhost:2999
+ http_port 9080
+ https_port 9443
+ local_certs
+ }
+ acme.localhost {
acme_server {
challenges tls-alpn-01
}
@@ -115,7 +111,7 @@ func testACMEServerWithMismatchedChallenges(tester *caddytest.Tester, t *testing
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client,
+ HTTPClient: tester.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
diff --git a/caddytest/integration/acmeserver_test.go b/caddytest/integration/acmeserver_test.go
index f05fb836e15..e3ce29ac094 100644
--- a/caddytest/integration/acmeserver_test.go
+++ b/caddytest/integration/acmeserver_test.go
@@ -15,8 +15,8 @@ import (
)
func TestACMEServerDirectory(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
local_certs
@@ -41,8 +41,8 @@ func TestACMEServerDirectory(t *testing.T) {
}
func TestACMEServerAllowPolicy(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
local_certs
@@ -71,7 +71,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client,
+ HTTPClient: tester.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -127,8 +127,8 @@ func TestACMEServerAllowPolicy(t *testing.T) {
}
func TestACMEServerDenyPolicy(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
local_certs
@@ -156,7 +156,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client,
+ HTTPClient: tester.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
diff --git a/caddytest/integration/autohttps_test.go b/caddytest/integration/autohttps_test.go
index 1dbdbcee209..993fa2b9aeb 100644
--- a/caddytest/integration/autohttps_test.go
+++ b/caddytest/integration/autohttps_test.go
@@ -8,8 +8,8 @@ import (
)
func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
skip_install_trust
@@ -24,8 +24,8 @@ func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -40,8 +40,8 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -56,8 +56,8 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T
}
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -98,8 +98,8 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -123,8 +123,8 @@ func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSite(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
diff --git a/caddytest/integration/caddyfile_test.go b/caddytest/integration/caddyfile_test.go
index 11ffc08aeb1..44c28cd5850 100644
--- a/caddytest/integration/caddyfile_test.go
+++ b/caddytest/integration/caddyfile_test.go
@@ -10,19 +10,19 @@ import (
func TestRespond(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
}
-
+
localhost:9080 {
respond /version 200 {
body "hello from localhost"
- }
+ }
}
`, "caddyfile")
@@ -32,22 +32,22 @@ func TestRespond(t *testing.T) {
func TestRedirect(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
https_port 9443
grace_period 1ns
}
-
+
localhost:9080 {
-
+
redir / http://localhost:9080/hello 301
-
+
respond /hello 200 {
body "hello from localhost"
- }
+ }
}
`, "caddyfile")
@@ -64,8 +64,8 @@ func TestDuplicateHosts(t *testing.T) {
`
localhost:9080 {
}
-
- localhost:9080 {
+
+ localhost:9080 {
}
`,
"caddyfile",
@@ -80,9 +80,9 @@ func TestReadCookie(t *testing.T) {
}
// arrange
- tester := caddytest.NewTester(t)
- tester.Client.Jar.SetCookies(localhost, []*http.Cookie{&cookie})
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.Client().Jar.SetCookies(localhost, []*http.Cookie{&cookie})
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -90,7 +90,7 @@ func TestReadCookie(t *testing.T) {
https_port 9443
grace_period 1ns
}
-
+
localhost:9080 {
templates {
root testdata
@@ -106,8 +106,8 @@ func TestReadCookie(t *testing.T) {
}
func TestReplIndex(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -481,9 +481,9 @@ func TestValidPrefix(t *testing.T) {
}
func TestUriReplace(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -491,16 +491,16 @@ func TestUriReplace(t *testing.T) {
:9080
uri replace "\}" %7D
uri replace "\{" %7B
-
+
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
}
func TestUriOps(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -511,7 +511,7 @@ func TestUriOps(t *testing.T) {
uri query taz test
uri query key=value example
uri query changethis>changed
-
+
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
@@ -523,9 +523,9 @@ func TestUriOps(t *testing.T) {
// refer to 127.0.0.1 or ::1.
// TODO: Test each http version separately (especially http/3)
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -537,9 +537,9 @@ func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
}
func TestSetThenAddQueryParams(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -547,16 +547,16 @@ func TestSetThenAddQueryParams(t *testing.T) {
:9080
uri query foo bar
uri query +foo baz
-
+
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint", 200, "foo=bar&foo=baz")
}
func TestSetThenDeleteParams(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -564,16 +564,16 @@ func TestSetThenDeleteParams(t *testing.T) {
:9080
uri query bar foo{query.foo}
uri query -foo
-
+
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=foobar")
}
func TestRenameAndOtherOps(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -582,36 +582,36 @@ func TestRenameAndOtherOps(t *testing.T) {
uri query foo>bar
uri query bar taz
uri query +bar baz
-
+
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
}
func TestReplaceOps(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query foo bar baz
+ uri query foo bar baz
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
}
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query foo bar {query.placeholder}
+ uri query foo bar {query.placeholder}
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
@@ -619,66 +619,66 @@ func TestReplaceWithReplacementPlaceholder(t *testing.T) {
}
func TestReplaceWithKeyPlaceholder(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query {query.placeholder} bar baz
+ uri query {query.placeholder} bar baz
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
}
func TestPartialReplacement(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query foo ar az
+ uri query foo ar az
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
}
func TestNonExistingSearch(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query foo var baz
+ uri query foo var baz
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
}
func TestReplaceAllOps(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
}
:9080
- uri query * bar baz
+ uri query * bar baz
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
}
func TestUriOpsBlock(t *testing.T) {
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
- tester.InitServer(`
+ tester.LoadConfig(`
{
admin localhost:2999
http_port 9080
@@ -688,15 +688,15 @@ func TestUriOpsBlock(t *testing.T) {
+foo bar
-baz
taz test
- }
+ }
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
}
func TestHandleErrorSimpleCodes(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
admin localhost:2999
http_port 9080
}
@@ -704,7 +704,7 @@ func TestHandleErrorSimpleCodes(t *testing.T) {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
-
+
handle_errors 404 410 {
respond "404 or 410 error"
}
@@ -715,8 +715,8 @@ func TestHandleErrorSimpleCodes(t *testing.T) {
}
func TestHandleErrorRange(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
admin localhost:2999
http_port 9080
}
@@ -735,8 +735,8 @@ func TestHandleErrorRange(t *testing.T) {
}
func TestHandleErrorSort(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
admin localhost:2999
http_port 9080
}
@@ -759,8 +759,8 @@ func TestHandleErrorSort(t *testing.T) {
}
func TestHandleErrorRangeAndCodes(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
admin localhost:2999
http_port 9080
}
diff --git a/caddytest/integration/handler_test.go b/caddytest/integration/handler_test.go
index afc700b02bd..62b37939f7d 100644
--- a/caddytest/integration/handler_test.go
+++ b/caddytest/integration/handler_test.go
@@ -9,8 +9,8 @@ import (
)
func TestBrowse(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -32,8 +32,8 @@ func TestBrowse(t *testing.T) {
}
func TestRespondWithJSON(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
diff --git a/caddytest/integration/intercept_test.go b/caddytest/integration/intercept_test.go
index 81db6a7d639..ef33f1cd8f3 100644
--- a/caddytest/integration/intercept_test.go
+++ b/caddytest/integration/intercept_test.go
@@ -7,8 +7,8 @@ import (
)
func TestIntercept(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
skip_install_trust
admin localhost:2999
http_port 9080
diff --git a/caddytest/integration/leafcertloaders_test.go b/caddytest/integration/leafcertloaders_test.go
index 4399902eaee..39501fe5b57 100644
--- a/caddytest/integration/leafcertloaders_test.go
+++ b/caddytest/integration/leafcertloaders_test.go
@@ -7,8 +7,8 @@ import (
)
func TestLeafCertLoaders(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
diff --git a/caddytest/integration/listener_test.go b/caddytest/integration/listener_test.go
index 30642b1aed9..b2e327b04c6 100644
--- a/caddytest/integration/listener_test.go
+++ b/caddytest/integration/listener_test.go
@@ -12,7 +12,7 @@ import (
"github.com/caddyserver/caddy/v2/caddytest"
)
-func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.Tester {
+func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddytest.TestHarness {
l, err := net.Listen("tcp", "127.0.0.1:0")
if err != nil {
t.Fatalf("failed to listen: %s", err)
@@ -28,8 +28,8 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
_ = srv.Close()
_ = l.Close()
})
- tester := caddytest.NewTester(t)
- tester.InitServer(fmt.Sprintf(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
@@ -69,7 +69,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
writer.WriteHeader(http.StatusNoContent)
})
- resp, err := tester.Client.Post("https://localhost:9443", "application/octet-stream", bytes.NewReader(body))
+ resp, err := tester.Client().Post("https://localhost:9443", "application/octet-stream", bytes.NewReader(body))
if err != nil {
t.Fatalf("failed to post: %s", err)
}
@@ -87,7 +87,7 @@ func TestLargeHttpRequest(t *testing.T) {
// We never read the body in any way, set an extra long header instead.
req, _ := http.NewRequest("POST", "http://localhost:9443", nil)
req.Header.Set("Long-Header", strings.Repeat("X", 1024*1024))
- _, err := tester.Client.Do(req)
+ _, err := tester.Client().Do(req)
if err == nil {
t.Fatal("not supposed to succeed")
}
diff --git a/caddytest/integration/map_test.go b/caddytest/integration/map_test.go
index eb338656469..c5e7d7d3d58 100644
--- a/caddytest/integration/map_test.go
+++ b/caddytest/integration/map_test.go
@@ -9,8 +9,8 @@ import (
func TestMap(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
skip_install_trust
admin localhost:2999
http_port 9080
@@ -39,8 +39,8 @@ func TestMap(t *testing.T) {
func TestMapRespondWithDefault(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
skip_install_trust
admin localhost:2999
http_port 9080
@@ -67,8 +67,8 @@ func TestMapRespondWithDefault(t *testing.T) {
func TestMapAsJSON(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
diff --git a/caddytest/integration/reverseproxy_test.go b/caddytest/integration/reverseproxy_test.go
index cbfe8433bc9..f165c2e5cdc 100644
--- a/caddytest/integration/reverseproxy_test.go
+++ b/caddytest/integration/reverseproxy_test.go
@@ -14,8 +14,8 @@ import (
)
func TestSRVReverseProxy(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -87,8 +87,8 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -139,8 +139,8 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
}
func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -233,8 +233,8 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
}
func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -327,8 +327,8 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
}
func TestReverseProxyHealthCheck(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
skip_install_trust
admin localhost:2999
@@ -364,7 +364,7 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -395,7 +395,7 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester.InitServer(fmt.Sprintf(`
+ tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
@@ -422,7 +422,7 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
- tester := caddytest.NewTester(t)
+ tester := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -453,7 +453,7 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester.InitServer(fmt.Sprintf(`
+ tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
admin localhost:2999
diff --git a/caddytest/integration/sni_test.go b/caddytest/integration/sni_test.go
index 188f9354135..7830bf725a4 100644
--- a/caddytest/integration/sni_test.go
+++ b/caddytest/integration/sni_test.go
@@ -8,8 +8,8 @@ import (
func TestDefaultSNI(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`{
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`{
"admin": {
"listen": "localhost:2999"
},
@@ -107,8 +107,8 @@ func TestDefaultSNI(t *testing.T) {
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -211,8 +211,8 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// arrange
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
diff --git a/caddytest/integration/stream_test.go b/caddytest/integration/stream_test.go
index d82882d6997..77c9bdfceff 100644
--- a/caddytest/integration/stream_test.go
+++ b/caddytest/integration/stream_test.go
@@ -20,8 +20,8 @@ import (
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
func TestH2ToH2CStream(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
@@ -204,8 +204,8 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
func TestH2ToH1ChunkedResponse(t *testing.T) {
- tester := caddytest.NewTester(t)
- tester.InitServer(`
+ tester := caddytest.StartHarness(t)
+ tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
diff --git a/caddytest/testing_harness.go b/caddytest/testing_harness.go
new file mode 100644
index 00000000000..581d2f7ed37
--- /dev/null
+++ b/caddytest/testing_harness.go
@@ -0,0 +1,223 @@
+package caddytest
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "net/http"
+ "regexp"
+ "testing"
+
+ "github.com/stretchr/testify/require"
+)
+
+// use the convention to replace /[certificatename].[crt|key] with the full path
+// this helps reduce the noise in test configurations and also allow this
+// to run in any path
+func prependCaddyFilePath(rawConfig string) string {
+ r := matchKey.ReplaceAllString(rawConfig, getIntegrationDir()+"$1")
+ r = matchCert.ReplaceAllString(r, getIntegrationDir()+"$1")
+ return r
+}
+
+var (
+ matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
+ matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
+)
+
+type TestHarness struct {
+ t testing.TB
+
+ tester *Tester
+}
+
+// StartHarness creates and starts a test harness environment which spans the lifetime a single caddy instance
+// This is used for the integration tests
+func StartHarness(t *testing.T) *TestHarness {
+ if testing.Short() {
+ t.SkipNow()
+ return nil
+ }
+ o := &TestHarness{t: t}
+ o.init()
+ return o
+}
+
+func (tc *TestHarness) Client() *http.Client {
+ return tc.tester.Client
+}
+
+func (tc *TestHarness) LoadConfig(rawConfig, configType string) {
+ rawConfig = prependCaddyFilePath(rawConfig)
+ err := tc.tester.LoadConfig(rawConfig, configType)
+ require.NoError(tc.t, err)
+}
+
+func (tc *TestHarness) init() {
+ // start the server
+ tester, err := NewTester()
+ if err != nil {
+ tc.t.Errorf("Failed to create caddy tester: %s", err)
+ return
+ }
+ tc.tester = tester
+ err = tc.tester.LaunchCaddy()
+ if err != nil {
+ tc.t.Errorf("Failed to launch caddy tester: %s", err)
+ }
+ // cleanup
+ tc.t.Cleanup(func() {
+ func() {
+ if tc.t.Failed() {
+ res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
+ if err != nil {
+ tc.t.Log("unable to read the current config")
+ return
+ }
+ defer res.Body.Close()
+ body, _ := io.ReadAll(res.Body)
+
+ var out bytes.Buffer
+ _ = json.Indent(&out, body, "", " ")
+ tc.t.Logf("----------- failed with config -----------\n%s", out.String())
+ }
+ }()
+ // shutdown server after extracing the config
+ err = tc.tester.CleanupCaddy()
+ if err != nil {
+ tc.t.Errorf("failed to clean up caddy instance: %s", err)
+ }
+ })
+}
+
+// AssertRedirect makes a request and asserts the redirection happens
+func (tc *TestHarness) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
+ redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
+ return http.ErrUseLastResponse
+ }
+
+ // using the existing client, we override the check redirect policy for this test
+ old := tc.tester.Client.CheckRedirect
+ tc.tester.Client.CheckRedirect = redirectPolicyFunc
+ defer func() { tc.tester.Client.CheckRedirect = old }()
+
+ resp, err := tc.tester.Client.Get(requestURI)
+ if err != nil {
+ tc.t.Errorf("failed to call server %s", err)
+ return nil
+ }
+
+ if expectedStatusCode != resp.StatusCode {
+ tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
+ }
+
+ loc, err := resp.Location()
+ if err != nil {
+ tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
+ }
+ if loc == nil && expectedToLocation != "" {
+ tc.t.Errorf("requesting \"%s\" expected a Location header, but didn't get one", requestURI)
+ }
+ if loc != nil {
+ if expectedToLocation != loc.String() {
+ tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
+ }
+ }
+
+ return resp
+}
+
+// AssertResponseCode will execute the request and verify the status code, returns a response for additional assertions
+func (tc *TestHarness) AssertResponseCode(req *http.Request, expectedStatusCode int) *http.Response {
+ resp, err := tc.tester.Client.Do(req)
+ if err != nil {
+ tc.t.Fatalf("failed to call server %s", err)
+ }
+
+ if expectedStatusCode != resp.StatusCode {
+ tc.t.Errorf("requesting \"%s\" expected status code: %d but got %d", req.URL.RequestURI(), expectedStatusCode, resp.StatusCode)
+ }
+
+ return resp
+}
+
+// AssertResponse request a URI and assert the status code and the body contains a string
+func (tc *TestHarness) AssertResponse(req *http.Request, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ resp := tc.AssertResponseCode(req, expectedStatusCode)
+
+ defer resp.Body.Close()
+ bytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ tc.t.Fatalf("unable to read the response body %s", err)
+ }
+
+ body := string(bytes)
+
+ if body != expectedBody {
+ tc.t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", req.RequestURI, expectedBody, body)
+ }
+
+ return resp, body
+}
+
+// Verb specific test functions
+
+// AssertGetResponse GET a URI and expect a statusCode and body text
+func (tc *TestHarness) AssertGetResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ req, err := http.NewRequest("GET", requestURI, nil)
+ if err != nil {
+ tc.t.Fatalf("unable to create request %s", err)
+ }
+
+ return tc.AssertResponse(req, expectedStatusCode, expectedBody)
+}
+
+// AssertDeleteResponse request a URI and expect a statusCode and body text
+func (tc *TestHarness) AssertDeleteResponse(requestURI string, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ req, err := http.NewRequest("DELETE", requestURI, nil)
+ if err != nil {
+ tc.t.Fatalf("unable to create request %s", err)
+ }
+
+ return tc.AssertResponse(req, expectedStatusCode, expectedBody)
+}
+
+// AssertPostResponseBody POST to a URI and assert the response code and body
+func (tc *TestHarness) AssertPostResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ req, err := http.NewRequest("POST", requestURI, requestBody)
+ if err != nil {
+ tc.t.Errorf("failed to create request %s", err)
+ return nil, ""
+ }
+
+ applyHeaders(tc.t, req, requestHeaders)
+
+ return tc.AssertResponse(req, expectedStatusCode, expectedBody)
+}
+
+// AssertPutResponseBody PUT to a URI and assert the response code and body
+func (tc *TestHarness) AssertPutResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ req, err := http.NewRequest("PUT", requestURI, requestBody)
+ if err != nil {
+ tc.t.Errorf("failed to create request %s", err)
+ return nil, ""
+ }
+
+ applyHeaders(tc.t, req, requestHeaders)
+
+ return tc.AssertResponse(req, expectedStatusCode, expectedBody)
+}
+
+// AssertPatchResponseBody PATCH to a URI and assert the response code and body
+func (tc *TestHarness) AssertPatchResponseBody(requestURI string, requestHeaders []string, requestBody *bytes.Buffer, expectedStatusCode int, expectedBody string) (*http.Response, string) {
+ req, err := http.NewRequest("PATCH", requestURI, requestBody)
+ if err != nil {
+ tc.t.Errorf("failed to create request %s", err)
+ return nil, ""
+ }
+
+ applyHeaders(tc.t, req, requestHeaders)
+
+ return tc.AssertResponse(req, expectedStatusCode, expectedBody)
+}
From b98c89fbb6352d98deb8ace26134d4aa9de37114 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 19:46:43 -0500
Subject: [PATCH 03/14] noot
---
caddytest/caddytest_assert.go | 10 ++++++++--
1 file changed, 8 insertions(+), 2 deletions(-)
diff --git a/caddytest/caddytest_assert.go b/caddytest/caddytest_assert.go
index d81764d667e..359538ad314 100644
--- a/caddytest/caddytest_assert.go
+++ b/caddytest/caddytest_assert.go
@@ -10,12 +10,18 @@ import (
"github.com/aryann/difflib"
"github.com/caddyserver/caddy/v2/caddyconfig"
+ "github.com/stretchr/testify/require"
)
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
- tc := StartHarness(t)
- err := tc.tester.LoadConfig(rawConfig, configType)
+ tc, err := NewTester()
+ require.NoError(t, err)
+ err = tc.LaunchCaddy()
+ require.NoError(t, err)
+ defer tc.CleanupCaddy()
+
+ err = tc.LoadConfig(rawConfig, configType)
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
From 01cb87808708ef047bb82cc8083ef19708ea3f54 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 20:08:38 -0500
Subject: [PATCH 04/14] noot
---
caddy.go | 2 +-
caddytest/caddytest.go | 75 +++++++++--------------------------
caddytest/caddytest_assert.go | 3 +-
caddytest/testing_harness.go | 16 +++++++-
4 files changed, 36 insertions(+), 60 deletions(-)
diff --git a/caddy.go b/caddy.go
index 9aba97fa415..2c18053a5c9 100644
--- a/caddy.go
+++ b/caddy.go
@@ -780,7 +780,7 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
logger.Error("unclean shutdown")
}
// check if we are in test environment, and dont call exit if we are
- if flag.Lookup("test.v") == nil || strings.Contains(os.Args[0], ".test") {
+ if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
os.Exit(exitCode)
}
}()
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index f22a09ef25e..44ae991eeb8 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -4,7 +4,6 @@ import (
"context"
"crypto/tls"
"encoding/json"
- "errors"
"fmt"
"io"
"log"
@@ -12,15 +11,11 @@ import (
"net/http"
"net/http/cookiejar"
"os"
- "path"
- "reflect"
- "runtime"
"strings"
"time"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
- "github.com/caddyserver/caddy/v2/caddyconfig"
// plug in Caddy modules here
_ "github.com/caddyserver/caddy/v2/modules/standard"
)
@@ -96,7 +91,7 @@ func (tc *Tester) CleanupCaddy() error {
}()
_, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
if err != nil {
- return fmt.Errorf("couldn't stop caddytest server")
+ return fmt.Errorf("couldn't stop caddytest server: %w", err)
}
time.Sleep(200 * time.Millisecond)
for retries := 0; retries < 10; retries++ {
@@ -112,7 +107,6 @@ func (tc *Tester) CleanupCaddy() error {
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
- originalRawConfig := rawConfig
// normalize JSON config
if configType == "json" {
var conf any
@@ -157,56 +151,32 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
}
tc.configLoaded = true
- return tc.ensureConfigRunning(originalRawConfig, configType)
+
+ // if the config is not loaded at this point, it is a bug in caddy's config.Load
+ // the contract for config.Load states that the config must be loaded before it returns, and that it will
+ // error if the config fails to apply
+ return nil
}
-func (tc *Tester) ensureConfigRunning(rawConfig string, configType string) error {
- expectedBytes := []byte(rawConfig)
- if configType != "json" {
- adapter := caddyconfig.GetAdapter(configType)
- if adapter == nil {
- return fmt.Errorf("adapter of config type is missing: %s", configType)
- }
- expectedBytes, _, _ = adapter.Adapt([]byte(rawConfig), nil)
+func (tc *Tester) GetCurrentConfig(receiver any) error {
+ client := &http.Client{
+ Timeout: Default.LoadRequestTimeout,
}
- var expected any
- err := json.Unmarshal(expectedBytes, &expected)
+ resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
if err != nil {
return err
}
-
- client := &http.Client{
- Timeout: Default.LoadRequestTimeout,
- }
-
- fetchConfig := func(client *http.Client) any {
- resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
- if err != nil {
- return nil
- }
- defer resp.Body.Close()
- actualBytes, err := io.ReadAll(resp.Body)
- if err != nil {
- return nil
- }
- var actual any
- err = json.Unmarshal(actualBytes, &actual)
- if err != nil {
- return nil
- }
- return actual
+ defer resp.Body.Close()
+ actualBytes, err := io.ReadAll(resp.Body)
+ if err != nil {
+ return err
}
-
- // TODO: does this really need to be tried more than once?
- // Caddy should block until the new config is loaded, which means needing to wait is a caddy bug
- for retries := 3; retries > 0; retries-- {
- if reflect.DeepEqual(expected, fetchConfig(client)) {
- return nil
- }
- time.Sleep(1 * time.Second)
+ err = json.Unmarshal(actualBytes, receiver)
+ if err != nil {
+ return err
}
- return errors.New("EnsureConfigRunning: POSTed configuration isn't active")
+ return nil
}
const initConfig = `{
@@ -258,15 +228,6 @@ func isCaddyAdminRunning() error {
return nil
}
-func getIntegrationDir() string {
- _, filename, _, ok := runtime.Caller(1)
- if !ok {
- panic("unable to determine the current file path")
- }
-
- return path.Dir(filename)
-}
-
// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
func CreateTestingTransport() *http.Transport {
dialer := net.Dialer{
diff --git a/caddytest/caddytest_assert.go b/caddytest/caddytest_assert.go
index 359538ad314..cfb019356aa 100644
--- a/caddytest/caddytest_assert.go
+++ b/caddytest/caddytest_assert.go
@@ -19,12 +19,13 @@ func AssertLoadError(t *testing.T, rawConfig string, configType string, expected
require.NoError(t, err)
err = tc.LaunchCaddy()
require.NoError(t, err)
- defer tc.CleanupCaddy()
err = tc.LoadConfig(rawConfig, configType)
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
+ err = tc.CleanupCaddy()
+ require.NoError(t, err)
}
// CompareAdapt adapts a config and then compares it against an expected result
diff --git a/caddytest/testing_harness.go b/caddytest/testing_harness.go
index 581d2f7ed37..f1743097b4b 100644
--- a/caddytest/testing_harness.go
+++ b/caddytest/testing_harness.go
@@ -6,7 +6,9 @@ import (
"fmt"
"io"
"net/http"
+ "path"
"regexp"
+ "runtime"
"testing"
"github.com/stretchr/testify/require"
@@ -21,6 +23,15 @@ func prependCaddyFilePath(rawConfig string) string {
return r
}
+func getIntegrationDir() string {
+ _, filename, _, ok := runtime.Caller(1)
+ if !ok {
+ panic("unable to determine the current file path")
+ }
+
+ return path.Dir(filename)
+}
+
var (
matchKey = regexp.MustCompile(`(/[\w\d\.]+\.key)`)
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
@@ -64,7 +75,9 @@ func (tc *TestHarness) init() {
tc.tester = tester
err = tc.tester.LaunchCaddy()
if err != nil {
- tc.t.Errorf("Failed to launch caddy tester: %s", err)
+ tc.t.Errorf("Failed to launch caddy server: %s", err)
+ tc.t.FailNow()
+ return
}
// cleanup
tc.t.Cleanup(func() {
@@ -87,6 +100,7 @@ func (tc *TestHarness) init() {
err = tc.tester.CleanupCaddy()
if err != nil {
tc.t.Errorf("failed to clean up caddy instance: %s", err)
+ tc.t.FailNow()
}
})
}
From b491fc5d6ce68eb4630e950fa514575965172682 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 20:11:56 -0500
Subject: [PATCH 05/14] noot
---
caddy.go | 2 +-
cmd/commandfuncs.go | 8 +++++++-
2 files changed, 8 insertions(+), 2 deletions(-)
diff --git a/caddy.go b/caddy.go
index 2c18053a5c9..e7afc13b86e 100644
--- a/caddy.go
+++ b/caddy.go
@@ -780,7 +780,7 @@ func exitProcess(ctx context.Context, logger *zap.Logger) {
logger.Error("unclean shutdown")
}
// check if we are in test environment, and dont call exit if we are
- if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
+ if flag.Lookup("test.v") == nil && !strings.Contains(os.Args[0], ".test") {
os.Exit(exitCode)
}
}()
diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go
index 746cf3da6b6..bdee24d11a8 100644
--- a/cmd/commandfuncs.go
+++ b/cmd/commandfuncs.go
@@ -20,6 +20,7 @@ import (
"crypto/rand"
"encoding/json"
"errors"
+ "flag"
"fmt"
"io"
"io/fs"
@@ -257,6 +258,7 @@ func cmdRun(fl Flags) (int, error) {
// if enabled, reload config file automatically on changes
// (this better only be used in dev!)
+ // do not enable this during tests, it will cause leaks
if watchFlag {
go watchConfigFile(configFile, configAdapterFlag)
}
@@ -280,7 +282,11 @@ func cmdRun(fl Flags) (int, error) {
}
}
- select {}
+ if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
+ return caddy.ExitCodeSuccess, nil
+ } else {
+ select {}
+ }
}
func cmdStop(fl Flags) (int, error) {
From 41a4320fd34010e53835f4e26f5729aa5f220545 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 20:14:51 -0500
Subject: [PATCH 06/14] noot
---
caddytest/caddytest.go | 5 ++---
caddytest/caddytest_assert.go | 3 +--
2 files changed, 3 insertions(+), 5 deletions(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 44ae991eeb8..a1ec970482f 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -93,7 +93,6 @@ func (tc *Tester) CleanupCaddy() error {
if err != nil {
return fmt.Errorf("couldn't stop caddytest server: %w", err)
}
- time.Sleep(200 * time.Millisecond)
for retries := 0; retries < 10; retries++ {
if isCaddyAdminRunning() != nil {
return nil
@@ -206,8 +205,8 @@ func (tc *Tester) startServer() error {
caddycmd.Main()
}()
// wait for caddy admin api to start. it should happen quickly.
- for retries := 3; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
- time.Sleep(1 * time.Second)
+ for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
+ time.Sleep(100 * time.Millisecond)
}
// one more time to return the error
diff --git a/caddytest/caddytest_assert.go b/caddytest/caddytest_assert.go
index cfb019356aa..8587abaedd9 100644
--- a/caddytest/caddytest_assert.go
+++ b/caddytest/caddytest_assert.go
@@ -24,8 +24,7 @@ func AssertLoadError(t *testing.T, rawConfig string, configType string, expected
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
- err = tc.CleanupCaddy()
- require.NoError(t, err)
+ tc.CleanupCaddy()
}
// CompareAdapt adapts a config and then compares it against an expected result
From b19feec6dcb465ceeb15241e5dc251f6231f6d29 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 21:01:15 -0500
Subject: [PATCH 07/14] noot
---
cmd/commandfuncs.go | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/cmd/commandfuncs.go b/cmd/commandfuncs.go
index bdee24d11a8..8de079bfe4b 100644
--- a/cmd/commandfuncs.go
+++ b/cmd/commandfuncs.go
@@ -283,9 +283,9 @@ func cmdRun(fl Flags) (int, error) {
}
if flag.Lookup("test.v") == nil || !strings.Contains(os.Args[0], ".test") {
- return caddy.ExitCodeSuccess, nil
- } else {
select {}
+ } else {
+ return caddy.ExitCodeSuccess, nil
}
}
From 841fe2544d7e0fe7cf938c818cd6c88085ce5879 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 21:17:51 -0500
Subject: [PATCH 08/14] noot
---
caddytest/caddytest.go | 3 +--
cmd/main.go | 19 +++++++++++++++++++
2 files changed, 20 insertions(+), 2 deletions(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index a1ec970482f..3de5b96b018 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -200,9 +200,8 @@ func (tc *Tester) startServer() error {
}
// start inprocess caddy server
- os.Args = []string{"caddy", "run", "--config", f.Name(), "--adapter", "caddyfile"}
go func() {
- caddycmd.Main()
+ caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
}()
// wait for caddy admin api to start. it should happen quickly.
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
diff --git a/cmd/main.go b/cmd/main.go
index 3c3ae627087..3e0359334a4 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -34,6 +34,7 @@ import (
"time"
"github.com/caddyserver/certmagic"
+ "github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.uber.org/automaxprocs/maxprocs"
"go.uber.org/zap"
@@ -81,6 +82,24 @@ func Main() {
}
}
+// MainForTesting implements the main function of the caddy command, used internally for testing
+func MainForTesting(args ...string) error {
+ // create a root command for testing which will not pollute the global namespace, and does not
+ // call os.Exit().
+ tmpRootCmp := cobra.Command{
+ Use: rootCmd.Use,
+ Long: rootCmd.Long,
+ Example: rootCmd.Example,
+ SilenceUsage: rootCmd.SilenceUsage,
+ Version: rootCmd.Version,
+ }
+ tmpRootCmp.SetArgs(args)
+ if err := rootCmd.Execute(); err != nil {
+ return err
+ }
+ return nil
+}
+
// handlePingbackConn reads from conn and ensures it matches
// the bytes in expect, or returns an error if it doesn't.
func handlePingbackConn(conn net.Conn, expect []byte) error {
From 926fb82f6b65fc34193f8cba46ede8ec9e34d3e2 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 21:18:07 -0500
Subject: [PATCH 09/14] noot
---
caddytest/caddytest.go | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 3de5b96b018..5585a934341 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -201,7 +201,7 @@ func (tc *Tester) startServer() error {
// start inprocess caddy server
go func() {
- caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
+ caddycmd.MainForTesting("caddy", "run", "--config", tc.configFileName, "--adapter", "caddyfile")
}()
// wait for caddy admin api to start. it should happen quickly.
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
From edf4168c8e3e9659612d4e8da71944769dae0391 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 21:18:38 -0500
Subject: [PATCH 10/14] noot
---
caddytest/caddytest.go | 2 +-
cmd/main.go | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 5585a934341..3de5b96b018 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -201,7 +201,7 @@ func (tc *Tester) startServer() error {
// start inprocess caddy server
go func() {
- caddycmd.MainForTesting("caddy", "run", "--config", tc.configFileName, "--adapter", "caddyfile")
+ caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
}()
// wait for caddy admin api to start. it should happen quickly.
for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
diff --git a/cmd/main.go b/cmd/main.go
index 3e0359334a4..4526b26b665 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -94,7 +94,7 @@ func MainForTesting(args ...string) error {
Version: rootCmd.Version,
}
tmpRootCmp.SetArgs(args)
- if err := rootCmd.Execute(); err != nil {
+ if err := tmpRootCmp.Execute(); err != nil {
return err
}
return nil
From 7bc7e1680e79863f97bc60be44d9d0199fdda2a0 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 21:57:46 -0500
Subject: [PATCH 11/14] noot
---
cmd/cobra.go | 26 ++++++++++++---------
cmd/commandfactory.go | 28 +++++++++++++++++++++++
cmd/commands.go | 53 +++++++++++++++++++++++--------------------
cmd/main.go | 15 ++++--------
4 files changed, 76 insertions(+), 46 deletions(-)
create mode 100644 cmd/commandfactory.go
diff --git a/cmd/cobra.go b/cmd/cobra.go
index 1a2509206a8..5323d2ec57e 100644
--- a/cmd/cobra.go
+++ b/cmd/cobra.go
@@ -8,9 +8,10 @@ import (
"github.com/caddyserver/caddy/v2"
)
-var rootCmd = &cobra.Command{
- Use: "caddy",
- Long: `Caddy is an extensible server platform written in Go.
+var defaultFactory = NewRootCommandFactory(func() *cobra.Command {
+ return &cobra.Command{
+ Use: "caddy",
+ Long: `Caddy is an extensible server platform written in Go.
At its core, Caddy merely manages configuration. Modules are plugged
in statically at compile-time to provide useful functionality. Caddy's
@@ -91,23 +92,26 @@ package installers: https://caddyserver.com/docs/install
Instructions for running Caddy in production are also available:
https://caddyserver.com/docs/running
`,
- Example: ` $ caddy run
+ Example: ` $ caddy run
$ caddy run --config caddy.json
$ caddy reload --config caddy.json
$ caddy stop`,
- // kind of annoying to have all the help text printed out if
- // caddy has an error provisioning its modules, for instance...
- SilenceUsage: true,
- Version: onlyVersionText(),
-}
+ // kind of annoying to have all the help text printed out if
+ // caddy has an error provisioning its modules, for instance...
+ SilenceUsage: true,
+ Version: onlyVersionText(),
+ }
+})
const fullDocsFooter = `Full documentation is available at:
https://caddyserver.com/docs/command-line`
func init() {
- rootCmd.SetVersionTemplate("{{.Version}}\n")
- rootCmd.SetHelpTemplate(rootCmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
+ defaultFactory.Use(func(cmd *cobra.Command) {
+ cmd.SetVersionTemplate("{{.Version}}\n")
+ cmd.SetHelpTemplate(cmd.HelpTemplate() + "\n" + fullDocsFooter + "\n")
+ })
}
func onlyVersionText() string {
diff --git a/cmd/commandfactory.go b/cmd/commandfactory.go
new file mode 100644
index 00000000000..49a38a4e194
--- /dev/null
+++ b/cmd/commandfactory.go
@@ -0,0 +1,28 @@
+package caddycmd
+
+import (
+ "github.com/spf13/cobra"
+)
+
+type RootCommandFactory struct {
+ constructor func() *cobra.Command
+ options []func(*cobra.Command)
+}
+
+func NewRootCommandFactory(fn func() *cobra.Command) *RootCommandFactory {
+ return &RootCommandFactory{
+ constructor: fn,
+ }
+}
+
+func (f *RootCommandFactory) Use(fn func(cmd *cobra.Command)) {
+ f.options = append(f.options, fn)
+}
+
+func (f *RootCommandFactory) Build() *cobra.Command {
+ o := f.constructor()
+ for _, v := range f.options {
+ v(o)
+ }
+ return o
+}
diff --git a/cmd/commands.go b/cmd/commands.go
index e5e1265e441..7e7af1c77d0 100644
--- a/cmd/commands.go
+++ b/cmd/commands.go
@@ -459,7 +459,8 @@ argument of --directory. If the directory does not exist, it will be created.
if err := os.MkdirAll(dir, 0o755); err != nil {
return caddy.ExitCodeFailedQuit, err
}
- if err := doc.GenManTree(rootCmd, &doc.GenManHeader{
+ ccmd := defaultFactory.Build()
+ if err := doc.GenManTree(ccmd, &doc.GenManHeader{
Title: "Caddy",
Section: "8", // https://en.wikipedia.org/wiki/Man_page#Manual_sections
}, dir); err != nil {
@@ -471,10 +472,11 @@ argument of --directory. If the directory does not exist, it will be created.
})
// source: https://github.com/spf13/cobra/blob/main/shell_completions.md
- rootCmd.AddCommand(&cobra.Command{
- Use: "completion [bash|zsh|fish|powershell]",
- Short: "Generate completion script",
- Long: fmt.Sprintf(`To load completions:
+ defaultFactory.Use(func(ccmd *cobra.Command) {
+ ccmd.AddCommand(&cobra.Command{
+ Use: "completion [bash|zsh|fish|powershell]",
+ Short: "Generate completion script",
+ Long: fmt.Sprintf(`To load completions:
Bash:
@@ -512,24 +514,25 @@ argument of --directory. If the directory does not exist, it will be created.
# To load completions for every new session, run:
PS> %[1]s completion powershell > %[1]s.ps1
# and source this file from your PowerShell profile.
- `, rootCmd.Root().Name()),
- DisableFlagsInUseLine: true,
- ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
- Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
- RunE: func(cmd *cobra.Command, args []string) error {
- switch args[0] {
- case "bash":
- return cmd.Root().GenBashCompletion(os.Stdout)
- case "zsh":
- return cmd.Root().GenZshCompletion(os.Stdout)
- case "fish":
- return cmd.Root().GenFishCompletion(os.Stdout, true)
- case "powershell":
- return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
- default:
- return fmt.Errorf("unrecognized shell: %s", args[0])
- }
- },
+ `, defaultFactory.constructor().Name()),
+ DisableFlagsInUseLine: true,
+ ValidArgs: []string{"bash", "zsh", "fish", "powershell"},
+ Args: cobra.MatchAll(cobra.ExactArgs(1), cobra.OnlyValidArgs),
+ RunE: func(cmd *cobra.Command, args []string) error {
+ switch args[0] {
+ case "bash":
+ return cmd.Root().GenBashCompletion(os.Stdout)
+ case "zsh":
+ return cmd.Root().GenZshCompletion(os.Stdout)
+ case "fish":
+ return cmd.Root().GenFishCompletion(os.Stdout, true)
+ case "powershell":
+ return cmd.Root().GenPowerShellCompletionWithDesc(os.Stdout)
+ default:
+ return fmt.Errorf("unrecognized shell: %s", args[0])
+ }
+ },
+ })
})
}
@@ -563,7 +566,9 @@ func RegisterCommand(cmd Command) {
if !commandNameRegex.MatchString(cmd.Name) {
panic("invalid command name")
}
- rootCmd.AddCommand(caddyCmdToCobra(cmd))
+ defaultFactory.Use(func(ccmd *cobra.Command) {
+ ccmd.AddCommand(caddyCmdToCobra(cmd))
+ })
}
var commandNameRegex = regexp.MustCompile(`^[a-z0-9]$|^([a-z0-9]+-?[a-z0-9]*)+[a-z0-9]$`)
diff --git a/cmd/main.go b/cmd/main.go
index 4526b26b665..6defac7568b 100644
--- a/cmd/main.go
+++ b/cmd/main.go
@@ -34,7 +34,6 @@ import (
"time"
"github.com/caddyserver/certmagic"
- "github.com/spf13/cobra"
"github.com/spf13/pflag"
"go.uber.org/automaxprocs/maxprocs"
"go.uber.org/zap"
@@ -72,7 +71,7 @@ func Main() {
if err != nil {
caddy.Log().Warn("failed to set GOMAXPROCS", zap.Error(err))
}
-
+ rootCmd := defaultFactory.Build()
if err := rootCmd.Execute(); err != nil {
var exitError *exitError
if errors.As(err, &exitError) {
@@ -86,15 +85,9 @@ func Main() {
func MainForTesting(args ...string) error {
// create a root command for testing which will not pollute the global namespace, and does not
// call os.Exit().
- tmpRootCmp := cobra.Command{
- Use: rootCmd.Use,
- Long: rootCmd.Long,
- Example: rootCmd.Example,
- SilenceUsage: rootCmd.SilenceUsage,
- Version: rootCmd.Version,
- }
- tmpRootCmp.SetArgs(args)
- if err := tmpRootCmp.Execute(); err != nil {
+ rootCmd := defaultFactory.Build()
+ rootCmd.SetArgs(args)
+ if err := rootCmd.Execute(); err != nil {
return err
}
return nil
From c0d9a2383e7c6b3f370605834fc9cf62ba3753eb Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 22:36:02 -0500
Subject: [PATCH 12/14] noot
---
caddytest/caddytest.go | 91 +++++++++++++------
caddytest/caddytest_assert.go | 5 +-
caddytest/caddytest_test.go | 2 +-
caddytest/integration/acme_test.go | 4 +-
caddytest/integration/acmeserver_test.go | 6 +-
caddytest/integration/autohttps_test.go | 12 +--
caddytest/integration/caddyfile_test.go | 43 +++++----
caddytest/integration/handler_test.go | 4 +-
caddytest/integration/intercept_test.go | 2 +-
caddytest/integration/leafcertloaders_test.go | 2 +-
caddytest/integration/listener_test.go | 2 +-
caddytest/integration/map_test.go | 6 +-
caddytest/integration/reverseproxy_test.go | 14 +--
caddytest/integration/sni_test.go | 6 +-
caddytest/integration/stream_test.go | 4 +-
caddytest/testing_harness.go | 2 +-
16 files changed, 121 insertions(+), 84 deletions(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 3de5b96b018..9a89d6b1e95 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -11,6 +11,7 @@ import (
"net/http"
"net/http/cookiejar"
"os"
+ "strconv"
"strings"
"time"
@@ -22,8 +23,6 @@ import (
// Defaults store any configuration required to make the tests run
type Defaults struct {
- // Port we expect caddy to listening on
- AdminPort int
// Certificates we expect to be loaded before attempting to run the tests
Certificates []string
// TestRequestTimeout is the time to wait for a http request to
@@ -34,7 +33,6 @@ type Defaults struct {
// Default testing values
var Default = Defaults{
- AdminPort: 2999, // different from what a real server also running on a developer's machine might be
Certificates: []string{"/caddy.localhost.crt", "/caddy.localhost.key"},
TestRequestTimeout: 5 * time.Second,
LoadRequestTimeout: 5 * time.Second,
@@ -42,9 +40,12 @@ var Default = Defaults{
// Tester represents an instance of a test client.
type Tester struct {
- Client *http.Client
+ Client *http.Client
+
+ adminPort int
configLoaded bool
configFileName string
+ envFileName string
}
// NewTester will create a new testing client with an attached cookie jar
@@ -86,26 +87,37 @@ func (tc *Tester) LaunchCaddy() error {
func (tc *Tester) CleanupCaddy() error {
// now shutdown the server, since the test is done.
defer func() {
- // try to remove the tmp config file we created
- os.Remove(tc.configFileName)
+ // try to remove pthe tmp config file we created
+ if tc.configFileName != "" {
+ os.Remove(tc.configFileName)
+ }
+ if tc.envFileName != "" {
+ os.Remove(tc.envFileName)
+ }
}()
- _, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", Default.AdminPort), "", nil)
+ resp, err := http.Post(fmt.Sprintf("http://localhost:%d/stop", tc.adminPort), "", nil)
if err != nil {
return fmt.Errorf("couldn't stop caddytest server: %w", err)
}
+ resp.Body.Close()
for retries := 0; retries < 10; retries++ {
- if isCaddyAdminRunning() != nil {
+ if tc.isCaddyAdminRunning() != nil {
return nil
}
time.Sleep(100 * time.Millisecond)
}
return fmt.Errorf("timed out waiting for caddytest server to stop")
-
}
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
+// it should not be run
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
+ if tc.adminPort == 0 {
+ return fmt.Errorf("load config called where startServer didnt succeed")
+ }
+ // replace special testing placeholders so we can have our admin api be on a random port
+ rawConfig = strings.ReplaceAll(rawConfig, "{$TESTING_ADMIN_API}", fmt.Sprintf("localhost:%d", tc.adminPort))
// normalize JSON config
if configType == "json" {
var conf any
@@ -122,7 +134,7 @@ func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
Timeout: Default.LoadRequestTimeout,
}
start := time.Now()
- req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", Default.AdminPort), strings.NewReader(rawConfig))
+ req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", tc.adminPort), strings.NewReader(rawConfig))
if err != nil {
return fmt.Errorf("failed to create request. %w", err)
}
@@ -162,7 +174,7 @@ func (tc *Tester) GetCurrentConfig(receiver any) error {
Timeout: Default.LoadRequestTimeout,
}
- resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
+ resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
if err != nil {
return err
}
@@ -178,48 +190,73 @@ func (tc *Tester) GetCurrentConfig(receiver any) error {
return nil
}
-const initConfig = `{
- admin localhost:2999
+func getFreePort() (int, error) {
+ lr, err := net.Listen("tcp", "localhost:0")
+ if err != nil {
+ return 0, err
+ }
+ port := strings.Split(lr.Addr().String(), ":")
+ if len(port) < 2 {
+ return 0, fmt.Errorf("no port available")
+ }
+ i, err := strconv.Atoi(port[1])
+ if err != nil {
+ return 0, err
+ }
+ err = lr.Close()
+ if err != nil {
+ return 0, fmt.Errorf("failed to close listener: %w", err)
+ }
+ return i, nil
}
-`
// launches caddy, and then ensures the Caddy sub-process is running.
func (tc *Tester) startServer() error {
- if isCaddyAdminRunning() == nil {
+ if tc.isCaddyAdminRunning() == nil {
return fmt.Errorf("caddy test admin port still in use")
}
- // setup the init config file, and set the cleanup afterwards
- f, err := os.CreateTemp("", "")
+ a, err := getFreePort()
if err != nil {
- return err
+ return fmt.Errorf("could not find a open port to listen on: %w", err)
}
- tc.configFileName = f.Name()
+ tc.adminPort = a
+ // setup the init config file, and set the cleanup afterwards
+ {
+ f, err := os.CreateTemp("", "")
+ if err != nil {
+ return err
+ }
+ tc.configFileName = f.Name()
- if _, err := f.WriteString(initConfig); err != nil {
- return err
+ initConfig := fmt.Sprintf(`{
+ admin localhost:%d
+}`, a)
+ if _, err := f.WriteString(initConfig); err != nil {
+ return err
+ }
}
// start inprocess caddy server
go func() {
- caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
+ _ = caddycmd.MainForTesting("run", "--config", tc.configFileName, "--adapter", "caddyfile")
}()
// wait for caddy admin api to start. it should happen quickly.
- for retries := 10; retries > 0 && isCaddyAdminRunning() != nil; retries-- {
+ for retries := 10; retries > 0 && tc.isCaddyAdminRunning() != nil; retries-- {
time.Sleep(100 * time.Millisecond)
}
// one more time to return the error
- return isCaddyAdminRunning()
+ return tc.isCaddyAdminRunning()
}
-func isCaddyAdminRunning() error {
+func (tc *Tester) isCaddyAdminRunning() error {
// assert that caddy is running
client := &http.Client{
Timeout: Default.LoadRequestTimeout,
}
- resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
+ resp, err := client.Get(fmt.Sprintf("http://localhost:%d/config/", tc.adminPort))
if err != nil {
- return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", Default.AdminPort)
+ return fmt.Errorf("caddy integration test caddy server not running. Expected to be listening on localhost:%d", tc.adminPort)
}
resp.Body.Close()
diff --git a/caddytest/caddytest_assert.go b/caddytest/caddytest_assert.go
index 8587abaedd9..23054424d87 100644
--- a/caddytest/caddytest_assert.go
+++ b/caddytest/caddytest_assert.go
@@ -9,8 +9,9 @@ import (
"testing"
"github.com/aryann/difflib"
- "github.com/caddyserver/caddy/v2/caddyconfig"
"github.com/stretchr/testify/require"
+
+ "github.com/caddyserver/caddy/v2/caddyconfig"
)
// AssertLoadError will load a config and expect an error
@@ -24,7 +25,7 @@ func AssertLoadError(t *testing.T, rawConfig string, configType string, expected
if !strings.Contains(err.Error(), expectedError) {
t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
- tc.CleanupCaddy()
+ _ = tc.CleanupCaddy()
}
// CompareAdapt adapts a config and then compares it against an expected result
diff --git a/caddytest/caddytest_test.go b/caddytest/caddytest_test.go
index 589c33ea6dc..1cca9ff0fb0 100644
--- a/caddytest/caddytest_test.go
+++ b/caddytest/caddytest_test.go
@@ -68,7 +68,7 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
},
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
diff --git a/caddytest/integration/acme_test.go b/caddytest/integration/acme_test.go
index 9697cf9551b..8cd3220dfc7 100644
--- a/caddytest/integration/acme_test.go
+++ b/caddytest/integration/acme_test.go
@@ -28,7 +28,7 @@ func TestACMEServerWithDefaults(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
local_certs
@@ -96,7 +96,7 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
local_certs
diff --git a/caddytest/integration/acmeserver_test.go b/caddytest/integration/acmeserver_test.go
index e3ce29ac094..604a59f76a5 100644
--- a/caddytest/integration/acmeserver_test.go
+++ b/caddytest/integration/acmeserver_test.go
@@ -20,7 +20,7 @@ func TestACMEServerDirectory(t *testing.T) {
{
skip_install_trust
local_certs
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
pki {
@@ -46,7 +46,7 @@ func TestACMEServerAllowPolicy(t *testing.T) {
{
skip_install_trust
local_certs
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
pki {
@@ -132,7 +132,7 @@ func TestACMEServerDenyPolicy(t *testing.T) {
{
skip_install_trust
local_certs
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
pki {
diff --git a/caddytest/integration/autohttps_test.go b/caddytest/integration/autohttps_test.go
index 993fa2b9aeb..89070b29e78 100644
--- a/caddytest/integration/autohttps_test.go
+++ b/caddytest/integration/autohttps_test.go
@@ -11,7 +11,7 @@ func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
skip_install_trust
http_port 9080
https_port 9443
@@ -28,7 +28,7 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
}
@@ -44,7 +44,7 @@ func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
}
@@ -60,7 +60,7 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
@@ -102,7 +102,7 @@ func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
local_certs
@@ -127,7 +127,7 @@ func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSit
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
local_certs
diff --git a/caddytest/integration/caddyfile_test.go b/caddytest/integration/caddyfile_test.go
index 44c28cd5850..647d6b7ce0f 100644
--- a/caddytest/integration/caddyfile_test.go
+++ b/caddytest/integration/caddyfile_test.go
@@ -13,7 +13,7 @@ func TestRespond(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -35,7 +35,7 @@ func TestRedirect(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -85,7 +85,7 @@ func TestReadCookie(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -110,7 +110,7 @@ func TestReplIndex(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -485,7 +485,7 @@ func TestUriReplace(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -502,7 +502,7 @@ func TestUriOps(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -527,7 +527,7 @@ func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -541,7 +541,7 @@ func TestSetThenAddQueryParams(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -558,7 +558,7 @@ func TestSetThenDeleteParams(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -575,7 +575,7 @@ func TestRenameAndOtherOps(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -593,7 +593,7 @@ func TestReplaceOps(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -607,7 +607,7 @@ func TestReplaceWithReplacementPlaceholder(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -615,14 +615,13 @@ func TestReplaceWithReplacementPlaceholder(t *testing.T) {
respond "{query}"`, "caddyfile")
tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
-
}
func TestReplaceWithKeyPlaceholder(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -636,7 +635,7 @@ func TestPartialReplacement(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -650,7 +649,7 @@ func TestNonExistingSearch(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -665,7 +664,7 @@ func TestReplaceAllOps(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -680,7 +679,7 @@ func TestUriOpsBlock(t *testing.T) {
tester.LoadConfig(`
{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
:9080
@@ -697,7 +696,7 @@ func TestUriOpsBlock(t *testing.T) {
func TestHandleErrorSimpleCodes(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
localhost:9080 {
@@ -717,7 +716,7 @@ func TestHandleErrorSimpleCodes(t *testing.T) {
func TestHandleErrorRange(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
localhost:9080 {
@@ -737,7 +736,7 @@ func TestHandleErrorRange(t *testing.T) {
func TestHandleErrorSort(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
localhost:9080 {
@@ -761,7 +760,7 @@ func TestHandleErrorSort(t *testing.T) {
func TestHandleErrorRangeAndCodes(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
}
localhost:9080 {
diff --git a/caddytest/integration/handler_test.go b/caddytest/integration/handler_test.go
index 62b37939f7d..11d852420f1 100644
--- a/caddytest/integration/handler_test.go
+++ b/caddytest/integration/handler_test.go
@@ -13,7 +13,7 @@ func TestBrowse(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -36,7 +36,7 @@ func TestRespondWithJSON(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
diff --git a/caddytest/integration/intercept_test.go b/caddytest/integration/intercept_test.go
index ef33f1cd8f3..2e4d780caf6 100644
--- a/caddytest/integration/intercept_test.go
+++ b/caddytest/integration/intercept_test.go
@@ -10,7 +10,7 @@ func TestIntercept(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
diff --git a/caddytest/integration/leafcertloaders_test.go b/caddytest/integration/leafcertloaders_test.go
index 39501fe5b57..90c396f48ce 100644
--- a/caddytest/integration/leafcertloaders_test.go
+++ b/caddytest/integration/leafcertloaders_test.go
@@ -11,7 +11,7 @@ func TestLeafCertLoaders(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
diff --git a/caddytest/integration/listener_test.go b/caddytest/integration/listener_test.go
index b2e327b04c6..24b86509359 100644
--- a/caddytest/integration/listener_test.go
+++ b/caddytest/integration/listener_test.go
@@ -32,7 +32,7 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
local_certs
diff --git a/caddytest/integration/map_test.go b/caddytest/integration/map_test.go
index c5e7d7d3d58..ac20af45da0 100644
--- a/caddytest/integration/map_test.go
+++ b/caddytest/integration/map_test.go
@@ -12,7 +12,7 @@ func TestMap(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -42,7 +42,7 @@ func TestMapRespondWithDefault(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
}
@@ -71,7 +71,7 @@ func TestMapAsJSON(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
diff --git a/caddytest/integration/reverseproxy_test.go b/caddytest/integration/reverseproxy_test.go
index f165c2e5cdc..fdb742e8cac 100644
--- a/caddytest/integration/reverseproxy_test.go
+++ b/caddytest/integration/reverseproxy_test.go
@@ -18,7 +18,7 @@ func TestSRVReverseProxy(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
@@ -91,7 +91,7 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
@@ -143,7 +143,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
@@ -237,7 +237,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {
@@ -331,7 +331,7 @@ func TestReverseProxyHealthCheck(t *testing.T) {
tester.LoadConfig(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -398,7 +398,7 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
@@ -456,7 +456,7 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
tester.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin localhost:2999
+ admin {$TESTING_ADMIN_API}
http_port 9080
https_port 9443
grace_period 1ns
diff --git a/caddytest/integration/sni_test.go b/caddytest/integration/sni_test.go
index 7830bf725a4..e5aced6e997 100644
--- a/caddytest/integration/sni_test.go
+++ b/caddytest/integration/sni_test.go
@@ -11,7 +11,7 @@ func TestDefaultSNI(t *testing.T) {
tester := caddytest.StartHarness(t)
tester.LoadConfig(`{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
@@ -111,7 +111,7 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
@@ -215,7 +215,7 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
diff --git a/caddytest/integration/stream_test.go b/caddytest/integration/stream_test.go
index 77c9bdfceff..e47697793c1 100644
--- a/caddytest/integration/stream_test.go
+++ b/caddytest/integration/stream_test.go
@@ -24,7 +24,7 @@ func TestH2ToH2CStream(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
@@ -208,7 +208,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
- "listen": "localhost:2999"
+ "listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {
diff --git a/caddytest/testing_harness.go b/caddytest/testing_harness.go
index f1743097b4b..c5af3f6f547 100644
--- a/caddytest/testing_harness.go
+++ b/caddytest/testing_harness.go
@@ -83,7 +83,7 @@ func (tc *TestHarness) init() {
tc.t.Cleanup(func() {
func() {
if tc.t.Failed() {
- res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
+ res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", tc.tester.adminPort))
if err != nil {
tc.t.Log("unable to read the current config")
return
From 3c591ecac96a4236d96dcb377ed542a818ff5165 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 23:45:54 -0500
Subject: [PATCH 13/14] noot
---
caddytest/caddytest.go | 39 ++-
caddytest/caddytest_test.go | 21 +-
caddytest/integration/acme_test.go | 28 +-
caddytest/integration/acmeserver_test.go | 49 +--
caddytest/integration/autohttps_test.go | 93 +++---
caddytest/integration/caddyfile_test.go | 301 ++++++++++--------
caddytest/integration/handler_test.go | 29 +-
caddytest/integration/intercept_test.go | 21 +-
caddytest/integration/leafcertloaders_test.go | 12 +-
caddytest/integration/listener_test.go | 24 +-
caddytest/integration/map_test.go | 61 ++--
caddytest/integration/reverseproxy_test.go | 92 +++---
caddytest/integration/sni_test.go | 46 +--
caddytest/integration/stream_test.go | 48 +--
caddytest/testing_harness.go | 4 +
15 files changed, 470 insertions(+), 398 deletions(-)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 9a89d6b1e95..58cbfa06b13 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -13,6 +13,7 @@ import (
"os"
"strconv"
"strings"
+ "sync/atomic"
"time"
caddycmd "github.com/caddyserver/caddy/v2/cmd"
@@ -42,7 +43,12 @@ var Default = Defaults{
type Tester struct {
Client *http.Client
- adminPort int
+ adminPort int
+
+ portOne int
+ portTwo int
+
+ started atomic.Bool
configLoaded bool
configFileName string
envFileName string
@@ -78,6 +84,9 @@ func timeElapsed(start time.Time, name string) {
// launch caddy will start the server
func (tc *Tester) LaunchCaddy() error {
+ if !tc.started.CompareAndSwap(false, true) {
+ return fmt.Errorf("already launched caddy with this tester")
+ }
if err := tc.startServer(); err != nil {
return fmt.Errorf("failed to start server: %w", err)
}
@@ -110,14 +119,32 @@ func (tc *Tester) CleanupCaddy() error {
return fmt.Errorf("timed out waiting for caddytest server to stop")
}
+func (tc *Tester) AdminPort() int {
+ return tc.adminPort
+}
+func (tc *Tester) PortOne() int {
+ return tc.portOne
+}
+func (tc *Tester) PortTwo() int {
+ return tc.portTwo
+}
+
+func (tc *Tester) ReplaceTestingPlaceholders(x string) string {
+ x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_BIND}", fmt.Sprintf("localhost:%d", tc.adminPort))
+ x = strings.ReplaceAll(x, "{$TESTING_CADDY_ADMIN_PORT}", fmt.Sprintf("%d", tc.adminPort))
+ x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_ONE}", fmt.Sprintf("%d", tc.portOne))
+ x = strings.ReplaceAll(x, "{$TESTING_CADDY_PORT_TWO}", fmt.Sprintf("%d", tc.portTwo))
+ return x
+}
+
// LoadConfig loads the config to the tester server and also ensures that the config was loaded
// it should not be run
func (tc *Tester) LoadConfig(rawConfig string, configType string) error {
if tc.adminPort == 0 {
return fmt.Errorf("load config called where startServer didnt succeed")
}
+ rawConfig = tc.ReplaceTestingPlaceholders(rawConfig)
// replace special testing placeholders so we can have our admin api be on a random port
- rawConfig = strings.ReplaceAll(rawConfig, "{$TESTING_ADMIN_API}", fmt.Sprintf("localhost:%d", tc.adminPort))
// normalize JSON config
if configType == "json" {
var conf any
@@ -220,6 +247,14 @@ func (tc *Tester) startServer() error {
return fmt.Errorf("could not find a open port to listen on: %w", err)
}
tc.adminPort = a
+ tc.portOne, err = getFreePort()
+ if err != nil {
+ return fmt.Errorf("could not find a open portOne: %w", err)
+ }
+ tc.portTwo, err = getFreePort()
+ if err != nil {
+ return fmt.Errorf("could not find a open portOne: %w", err)
+ }
// setup the init config file, and set the cleanup afterwards
{
f, err := os.CreateTemp("", "")
diff --git a/caddytest/caddytest_test.go b/caddytest/caddytest_test.go
index 1cca9ff0fb0..d788a810b5e 100644
--- a/caddytest/caddytest_test.go
+++ b/caddytest/caddytest_test.go
@@ -1,13 +1,14 @@
package caddytest
import (
+ "fmt"
"net/http"
"strings"
"testing"
)
func TestReplaceCertificatePaths(t *testing.T) {
- rawConfig := `a.caddy.localhost:9443 {
+ rawConfig := `a.caddy.localhost:9443{
tls /caddy.localhost.crt /caddy.localhost.key {
}
@@ -34,8 +35,8 @@ func TestReplaceCertificatePaths(t *testing.T) {
}
func TestLoadUnorderedJSON(t *testing.T) {
- tester := StartHarness(t)
- tester.LoadConfig(`
+ harness := StartHarness(t)
+ harness.LoadConfig(`
{
"logging": {
"logs": {
@@ -68,7 +69,7 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
},
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -79,13 +80,13 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
},
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"s_server": {
"listen": [
- ":9443",
- ":9080"
+ ":{$TESTING_CADDY_PORT_ONE}",
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -120,10 +121,10 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
}
`, "json")
- req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
+ req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
- tester.AssertResponseCode(req, 200)
+ harness.AssertResponseCode(req, 200)
}
diff --git a/caddytest/integration/acme_test.go b/caddytest/integration/acme_test.go
index 8cd3220dfc7..63cd1f81d3c 100644
--- a/caddytest/integration/acme_test.go
+++ b/caddytest/integration/acme_test.go
@@ -24,13 +24,13 @@ const acmeChallengePort = 9081
// Test the basic functionality of Caddy's ACME server
func TestACMEServerWithDefaults(t *testing.T) {
ctx := context.Background()
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
acme.localhost {
@@ -41,8 +41,8 @@ func TestACMEServerWithDefaults(t *testing.T) {
logger := caddy.Log().Named("acmeserver")
client := acmez.Client{
Client: &acme.Client{
- Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client(),
+ Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
+ HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -92,13 +92,13 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
ctx := context.Background()
logger := caddy.Log().Named("acmez")
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
acme.localhost {
@@ -110,8 +110,8 @@ func TestACMEServerWithMismatchedChallenges(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
- Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client(),
+ Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
+ HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
diff --git a/caddytest/integration/acmeserver_test.go b/caddytest/integration/acmeserver_test.go
index 604a59f76a5..4c7946ac6fd 100644
--- a/caddytest/integration/acmeserver_test.go
+++ b/caddytest/integration/acmeserver_test.go
@@ -5,6 +5,7 @@ import (
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
+ "fmt"
"strings"
"testing"
@@ -15,40 +16,40 @@ import (
)
func TestACMEServerDirectory(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
local_certs
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
}
}
}
- acme.localhost:9443 {
+ acme.localhost:{$TESTING_CADDY_PORT_TWO} {
acme_server
}
`, "caddyfile")
- tester.AssertGetResponse(
- "https://acme.localhost:9443/acme/local/directory",
+ harness.AssertGetResponse(
+ fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
200,
- `{"newNonce":"https://acme.localhost:9443/acme/local/new-nonce","newAccount":"https://acme.localhost:9443/acme/local/new-account","newOrder":"https://acme.localhost:9443/acme/local/new-order","revokeCert":"https://acme.localhost:9443/acme/local/revoke-cert","keyChange":"https://acme.localhost:9443/acme/local/key-change"}
-`)
+ fmt.Sprintf(`{"newNonce":"https://acme.localhost:%[1]d/acme/local/new-nonce","newAccount":"https://acme.localhost:%[1]d/acme/local/new-account","newOrder":"https://acme.localhost:%[1]d/acme/local/new-order","revokeCert":"https://acme.localhost:%[1]d/acme/local/revoke-cert","keyChange":"https://acme.localhost:%[1]d/acme/local/key-change"}
+`, harness.Tester().PortTwo()))
}
func TestACMEServerAllowPolicy(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
local_certs
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
@@ -70,8 +71,8 @@ func TestACMEServerAllowPolicy(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
- Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client(),
+ Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
+ HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
@@ -127,14 +128,14 @@ func TestACMEServerAllowPolicy(t *testing.T) {
}
func TestACMEServerDenyPolicy(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
local_certs
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
pki {
ca local {
name "Caddy Local Authority"
@@ -155,8 +156,8 @@ func TestACMEServerDenyPolicy(t *testing.T) {
client := acmez.Client{
Client: &acme.Client{
- Directory: "https://acme.localhost:9443/acme/local/directory",
- HTTPClient: tester.Client(),
+ Directory: fmt.Sprintf("https://acme.localhost:%d/acme/local/directory", harness.Tester().PortTwo()),
+ HTTPClient: harness.Client(),
Logger: logger,
},
ChallengeSolvers: map[string]acmez.Solver{
diff --git a/caddytest/integration/autohttps_test.go b/caddytest/integration/autohttps_test.go
index 89070b29e78..ec296400394 100644
--- a/caddytest/integration/autohttps_test.go
+++ b/caddytest/integration/autohttps_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"net/http"
"testing"
@@ -8,69 +9,69 @@ import (
)
func TestAutoHTTPtoHTTPSRedirectsImplicitPort(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
+ admin {$TESTING_CADDY_ADMIN_BIND}
skip_install_trust
- http_port 9080
- https_port 9443
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
}
localhost
respond "Yahaha! You found me!"
`, "caddyfile")
- tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
+ harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortSameAsHTTPSPort(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
}
- localhost:9443
+ localhost:{$TESTING_CADDY_PORT_TWO}
respond "Yahaha! You found me!"
`, "caddyfile")
- tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
+ harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPtoHTTPSRedirectsExplicitPortDifferentFromHTTPSPort(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
}
localhost:1234
respond "Yahaha! You found me!"
`, "caddyfile")
- tester.AssertRedirect("http://localhost:9080/", "https://localhost:1234/", http.StatusPermanentRedirect)
+ harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost:1234/", http.StatusPermanentRedirect)
}
func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"ingress_server": {
"listen": [
- ":9080",
- ":9443"
+ ":{$TESTING_CADDY_PORT_ONE}",
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -94,52 +95,52 @@ func TestAutoHTTPRedirectsWithHTTPListenerFirstInAddresses(t *testing.T) {
}
}
`, "json")
- tester.AssertRedirect("http://localhost:9080/", "https://localhost/", http.StatusPermanentRedirect)
+ harness.AssertRedirect(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), "https://localhost/", http.StatusPermanentRedirect)
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAll(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
- http://:9080 {
+ http://:{$TESTING_CADDY_PORT_ONE} {
respond "Foo"
}
- http://baz.localhost:9080 {
+ http://baz.localhost:{$TESTING_CADDY_PORT_ONE} {
respond "Baz"
}
bar.localhost {
respond "Bar"
}
`, "caddyfile")
- tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
- tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
- tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Baz")
+ harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
+ harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
+ harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Baz")
}
func TestAutoHTTPRedirectsInsertedBeforeUserDefinedCatchAllWithNoExplicitHTTPSite(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
local_certs
}
- http://:9080 {
+ http://:{$TESTING_CADDY_PORT_ONE} {
respond "Foo"
}
bar.localhost {
respond "Bar"
}
`, "caddyfile")
- tester.AssertRedirect("http://bar.localhost:9080/", "https://bar.localhost/", http.StatusPermanentRedirect)
- tester.AssertGetResponse("http://foo.localhost:9080/", 200, "Foo")
- tester.AssertGetResponse("http://baz.localhost:9080/", 200, "Foo")
+ harness.AssertRedirect(fmt.Sprintf("http://bar.localhost:%d/", harness.Tester().PortOne()), "https://bar.localhost/", http.StatusPermanentRedirect)
+ harness.AssertGetResponse(fmt.Sprintf("http://foo.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
+ harness.AssertGetResponse(fmt.Sprintf("http://baz.localhost:%d/", harness.Tester().PortOne()), 200, "Foo")
}
diff --git a/caddytest/integration/caddyfile_test.go b/caddytest/integration/caddyfile_test.go
index 647d6b7ce0f..dab5a7d1db1 100644
--- a/caddytest/integration/caddyfile_test.go
+++ b/caddytest/integration/caddyfile_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"net/http"
"net/url"
"testing"
@@ -10,16 +11,16 @@ import (
func TestRespond(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
respond /version 200 {
body "hello from localhost"
}
@@ -27,23 +28,23 @@ func TestRespond(t *testing.T) {
`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost")
}
func TestRedirect(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
- redir / http://localhost:9080/hello 301
+ redir / http://localhost:{$TESTING_CADDY_PORT_ONE}/hello 301
respond /hello 200 {
body "hello from localhost"
@@ -51,21 +52,22 @@ func TestRedirect(t *testing.T) {
}
`, "caddyfile")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
// act and assert
- tester.AssertRedirect("http://localhost:9080/", "http://localhost:9080/hello", 301)
+ harness.AssertRedirect(target, target+"hello", 301)
// follow redirect
- tester.AssertGetResponse("http://localhost:9080/", 200, "hello from localhost")
+ harness.AssertGetResponse(target, 200, "hello from localhost")
}
func TestDuplicateHosts(t *testing.T) {
// act and assert
caddytest.AssertLoadError(t,
`
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
}
`,
"caddyfile",
@@ -80,18 +82,18 @@ func TestReadCookie(t *testing.T) {
}
// arrange
- tester := caddytest.StartHarness(t)
- tester.Client().Jar.SetCookies(localhost, []*http.Cookie{&cookie})
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.Client().Jar.SetCookies(localhost, []*http.Cookie{&cookie})
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
templates {
root testdata
}
@@ -102,21 +104,22 @@ func TestReadCookie(t *testing.T) {
`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/cookie.html", 200, "Cookie.ClientName caddytest
")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"cookie.html", 200, "Cookie.ClientName caddytest
")
}
func TestReplIndex(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
templates {
root testdata
}
@@ -128,7 +131,8 @@ func TestReplIndex(t *testing.T) {
`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/", 200, "")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target, 200, "")
}
func TestInvalidPrefix(t *testing.T) {
@@ -481,31 +485,32 @@ func TestValidPrefix(t *testing.T) {
}
func TestUriReplace(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri replace "\}" %7D
uri replace "\{" %7B
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?test={%20content%20}", 200, "test=%7B%20content%20%7D")
}
func TestUriOps(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query +foo bar
uri query -baz
uri query taz test
@@ -514,7 +519,8 @@ func TestUriOps(t *testing.T) {
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest&changethis=val", 200, "changed=val&foo=bar0&foo=bar&key%3Dvalue=example&taz=test")
}
// Tests the `http.request.local.port` placeholder.
@@ -523,166 +529,176 @@ func TestUriOps(t *testing.T) {
// refer to 127.0.0.1 or ::1.
// TODO: Test each http version separately (especially http/3)
func TestHttpRequestLocalPortPlaceholder(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
respond "{http.request.local.port}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/", 200, "9080")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target, 200, fmt.Sprintf("%d", harness.Tester().PortOne()))
}
func TestSetThenAddQueryParams(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo bar
uri query +foo baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint", 200, "foo=bar&foo=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint", 200, "foo=bar&foo=baz")
}
func TestSetThenDeleteParams(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query bar foo{query.foo}
uri query -foo
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=foobar")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=foobar")
}
func TestRenameAndOtherOps(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo>bar
uri query bar taz
uri query +bar baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "bar=taz&bar=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "bar=taz&bar=baz")
}
func TestReplaceOps(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo bar baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
}
func TestReplaceWithReplacementPlaceholder(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo bar {query.placeholder}
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?placeholder=baz&foo=bar", 200, "foo=baz&placeholder=baz")
}
func TestReplaceWithKeyPlaceholder(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query {query.placeholder} bar baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?placeholder=foo&foo=bar", 200, "foo=baz&placeholder=foo")
}
func TestPartialReplacement(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo ar az
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=baz")
}
func TestNonExistingSearch(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query foo var baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar", 200, "foo=bar")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar", 200, "foo=bar")
}
func TestReplaceAllOps(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query * bar baz
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar&baz=bar", 200, "baz=baz&foo=baz")
}
func TestUriOpsBlock(t *testing.T) {
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness.LoadConfig(`
{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- :9080
+ :{$TESTING_CADDY_PORT_ONE}
uri query {
+foo bar
-baz
@@ -690,16 +706,17 @@ func TestUriOpsBlock(t *testing.T) {
}
respond "{query}"`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"endpoint?foo=bar0&baz=buz&taz=nottest", 200, "foo=bar0&foo=bar&taz=test")
}
func TestHandleErrorSimpleCodes(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
@@ -709,17 +726,18 @@ func TestHandleErrorSimpleCodes(t *testing.T) {
}
}`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/private", 410, "404 or 410 error")
- tester.AssertGetResponse("http://localhost:9080/hidden", 404, "404 or 410 error")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"private", 410, "404 or 410 error")
+ harness.AssertGetResponse(target+"hidden", 404, "404 or 410 error")
}
func TestHandleErrorRange(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
@@ -729,17 +747,18 @@ func TestHandleErrorRange(t *testing.T) {
}
}`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
- tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
+ harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
}
func TestHandleErrorSort(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /hidden* "Not found" 404
@@ -753,17 +772,18 @@ func TestHandleErrorSort(t *testing.T) {
}
}`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Fallback route: code outside the [400..499] range")
- tester.AssertGetResponse("http://localhost:9080/hidden", 404, "Error in the [400 .. 499] range")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"internalerr", 500, "Fallback route: code outside the [400..499] range")
+ harness.AssertGetResponse(target+"hidden", 404, "Error in the [400 .. 499] range")
}
func TestHandleErrorRangeAndCodes(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
- admin {$TESTING_ADMIN_API}
- http_port 9080
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
root * /srv
error /private* "Unauthorized" 410
error /threehundred* "Moved Permanently" 301
@@ -777,9 +797,10 @@ func TestHandleErrorRangeAndCodes(t *testing.T) {
}
}`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
- tester.AssertGetResponse("http://localhost:9080/threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
- tester.AssertGetResponse("http://localhost:9080/private", 410, "Error in the [400 .. 499] range")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target+"internalerr", 500, "Error code is equal to 500 or in the [300..399] range")
+ harness.AssertGetResponse(target+"threehundred", 301, "Error code is equal to 500 or in the [300..399] range")
+ harness.AssertGetResponse(target+"private", 410, "Error in the [400 .. 499] range")
}
func TestInvalidSiteAddressesAsDirectives(t *testing.T) {
diff --git a/caddytest/integration/handler_test.go b/caddytest/integration/handler_test.go
index 11d852420f1..500c0e448c0 100644
--- a/caddytest/integration/handler_test.go
+++ b/caddytest/integration/handler_test.go
@@ -2,6 +2,7 @@ package integration
import (
"bytes"
+ "fmt"
"net/http"
"testing"
@@ -9,36 +10,36 @@ import (
)
func TestBrowse(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- http://localhost:9080 {
+ http://localhost:{$TESTING_CADDY_PORT_ONE} {
file_server browse
}
`, "caddyfile")
- req, err := http.NewRequest(http.MethodGet, "http://localhost:9080/", nil)
+ req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
- tester.AssertResponseCode(req, 200)
+ harness.AssertResponseCode(req, 200)
}
func TestRespondWithJSON(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
localhost {
@@ -46,7 +47,7 @@ func TestRespondWithJSON(t *testing.T) {
}
`, "caddyfile")
- res, _ := tester.AssertPostResponseBody("https://localhost:9443/",
+ res, _ := harness.AssertPostResponseBody(fmt.Sprintf("https://localhost:%d/", harness.Tester().PortTwo()),
nil,
bytes.NewBufferString(`{
"greeting": "Hello, world!"
diff --git a/caddytest/integration/intercept_test.go b/caddytest/integration/intercept_test.go
index 2e4d780caf6..f52ac5ffac5 100644
--- a/caddytest/integration/intercept_test.go
+++ b/caddytest/integration/intercept_test.go
@@ -1,22 +1,23 @@
package integration
import (
+ "fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
)
func TestIntercept(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
-
- localhost:9080 {
+
+ localhost:{$TESTING_CADDY_PORT_ONE} {
respond /intercept "I'm a teapot" 408
respond /no-intercept "I'm not a teapot"
@@ -25,10 +26,10 @@ func TestIntercept(t *testing.T) {
handle_response @teapot {
respond /intercept "I'm a combined coffee/tea pot that is temporarily out of coffee" 503
}
- }
+ }
}
`, "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/intercept", 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
- tester.AssertGetResponse("http://localhost:9080/no-intercept", 200, "I'm not a teapot")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/intercept", harness.Tester().PortOne()), 503, "I'm a combined coffee/tea pot that is temporarily out of coffee")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/no-intercept", harness.Tester().PortOne()), 200, "I'm not a teapot")
}
diff --git a/caddytest/integration/leafcertloaders_test.go b/caddytest/integration/leafcertloaders_test.go
index 90c396f48ce..502674edbc2 100644
--- a/caddytest/integration/leafcertloaders_test.go
+++ b/caddytest/integration/leafcertloaders_test.go
@@ -7,21 +7,21 @@ import (
)
func TestLeafCertLoaders(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
diff --git a/caddytest/integration/listener_test.go b/caddytest/integration/listener_test.go
index 24b86509359..f22f5133d8f 100644
--- a/caddytest/integration/listener_test.go
+++ b/caddytest/integration/listener_test.go
@@ -28,15 +28,15 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
_ = srv.Close()
_ = l.Close()
})
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(fmt.Sprintf(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
local_certs
- servers :9443 {
+ servers :{$TESTING_CADDY_PORT_TWO} {
listener_wrappers {
http_redirect
tls
@@ -47,7 +47,7 @@ func setupListenerWrapperTest(t *testing.T, handlerFunc http.HandlerFunc) *caddy
reverse_proxy %s
}
`, l.Addr().String()), "caddyfile")
- return tester
+ return harness
}
func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
@@ -56,7 +56,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
body := make([]byte, uploadSize)
rand.New(rand.NewSource(0)).Read(body)
- tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
+ harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
buf := new(bytes.Buffer)
_, err := buf.ReadFrom(request.Body)
if err != nil {
@@ -69,7 +69,7 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
writer.WriteHeader(http.StatusNoContent)
})
- resp, err := tester.Client().Post("https://localhost:9443", "application/octet-stream", bytes.NewReader(body))
+ resp, err := harness.Client().Post(fmt.Sprintf("https://localhost:%d", harness.Tester().PortTwo()), "application/octet-stream", bytes.NewReader(body))
if err != nil {
t.Fatalf("failed to post: %s", err)
}
@@ -80,14 +80,14 @@ func TestHTTPRedirectWrapperWithLargeUpload(t *testing.T) {
}
func TestLargeHttpRequest(t *testing.T) {
- tester := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
+ harness := setupListenerWrapperTest(t, func(writer http.ResponseWriter, request *http.Request) {
t.Fatal("not supposed to handle a request")
})
// We never read the body in any way, set an extra long header instead.
- req, _ := http.NewRequest("POST", "http://localhost:9443", nil)
+ req, _ := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d", harness.Tester().PortTwo()), nil)
req.Header.Set("Long-Header", strings.Repeat("X", 1024*1024))
- _, err := tester.Client().Do(req)
+ _, err := harness.Client().Do(req)
if err == nil {
t.Fatal("not supposed to succeed")
}
diff --git a/caddytest/integration/map_test.go b/caddytest/integration/map_test.go
index ac20af45da0..88ceb9e3638 100644
--- a/caddytest/integration/map_test.go
+++ b/caddytest/integration/map_test.go
@@ -2,6 +2,7 @@ package integration
import (
"bytes"
+ "fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
@@ -9,16 +10,16 @@ import (
func TestMap(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- localhost:9080 {
+ localhost:{$TESTING_CADDY_PORT_ONE} {
map {http.request.method} {dest-1} {dest-2} {
default unknown1 unknown2
@@ -28,50 +29,50 @@ func TestMap(t *testing.T) {
respond /version 200 {
body "hello from localhost {dest-1} {dest-2}"
- }
+ }
}
`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost GET-called unknown2")
- tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost GET-called unknown2")
+ harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called foobar")
}
func TestMapRespondWithDefault(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
}
-
- localhost:9080 {
-
+
+ localhost:{$TESTING_CADDY_PORT_ONE} {
+
map {http.request.method} {dest-name} {
default unknown
GET get-called
}
-
+
respond /version 200 {
body "hello from localhost {dest-name}"
- }
+ }
}
`, "caddyfile")
// act and assert
- tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
- tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), 200, "hello from localhost get-called")
+ harness.AssertPostResponseBody(fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne()), []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost unknown")
}
func TestMapAsJSON(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -82,12 +83,12 @@ func TestMapAsJSON(t *testing.T) {
}
},
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"servers": {
"srv0": {
"listen": [
- ":9080"
+ ":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -145,7 +146,7 @@ func TestMapAsJSON(t *testing.T) {
}
}
}`, "json")
-
- tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost get-called")
- tester.AssertPostResponseBody("http://localhost:9080/version", []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
+ target := fmt.Sprintf("http://localhost:%d/version", harness.Tester().PortOne())
+ harness.AssertGetResponse(target, 200, "hello from localhost get-called")
+ harness.AssertPostResponseBody(target, []string{}, bytes.NewBuffer([]byte{}), 200, "hello from localhost post-called")
}
diff --git a/caddytest/integration/reverseproxy_test.go b/caddytest/integration/reverseproxy_test.go
index fdb742e8cac..f141f6130f0 100644
--- a/caddytest/integration/reverseproxy_test.go
+++ b/caddytest/integration/reverseproxy_test.go
@@ -14,11 +14,11 @@ import (
)
func TestSRVReverseProxy(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -87,11 +87,11 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -135,15 +135,15 @@ func TestDialWithPlaceholderUnix(t *testing.T) {
return
}
req.Header.Set("X-Caddy-Upstream-Dial", socketName)
- tester.AssertResponse(req, 200, "Hello, World!")
+ harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -186,7 +186,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
},
"srv1": {
"listen": [
- ":9080"
+ ":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -199,7 +199,7 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
],
"handle": [
{
-
+
"handler": "reverse_proxy",
"upstreams": [
{
@@ -223,21 +223,21 @@ func TestReverseProxyWithPlaceholderDialAddress(t *testing.T) {
}
`, "json")
- req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
+ req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
req.Header.Set("X-Caddy-Upstream-Dial", "localhost:18080")
- tester.AssertResponse(req, 200, "Hello, World!")
+ harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"pki": {
@@ -280,7 +280,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
},
"srv1": {
"listen": [
- ":9080"
+ ":{$TESTING_CADDY_PORT_ONE}"
],
"routes": [
{
@@ -293,7 +293,7 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
],
"handle": [
{
-
+
"handler": "reverse_proxy",
"upstreams": [
{
@@ -317,23 +317,23 @@ func TestReverseProxyWithPlaceholderTCPDialAddress(t *testing.T) {
}
`, "json")
- req, err := http.NewRequest(http.MethodGet, "http://localhost:9080", nil)
+ req, err := http.NewRequest(http.MethodGet, fmt.Sprintf("http://localhost:%d", harness.Tester().PortOne()), nil)
if err != nil {
t.Fail()
return
}
req.Header.Set("X-Caddy-Upstream-Dial", "localhost")
- tester.AssertResponse(req, 200, "Hello, World!")
+ harness.AssertResponse(req, 200, "Hello, World!")
}
func TestReverseProxyHealthCheck(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
http://localhost:2020 {
@@ -342,10 +342,10 @@ func TestReverseProxyHealthCheck(t *testing.T) {
http://localhost:2021 {
respond "ok"
}
- http://localhost:9080 {
+ http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to localhost:2020
-
+
health_uri /health
health_port 2021
health_interval 10ms
@@ -357,14 +357,15 @@ func TestReverseProxyHealthCheck(t *testing.T) {
`, "caddyfile")
time.Sleep(100 * time.Millisecond) // TODO: for some reason this test seems particularly flaky, getting 503 when it should be 200, unless we wait
- tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target, 200, "Hello, World!")
}
func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -395,18 +396,18 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester.LoadConfig(fmt.Sprintf(`
+ harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- http://localhost:9080 {
+ http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to unix/%s
-
+
health_uri /health
health_port 2021
health_interval 2s
@@ -415,14 +416,15 @@ func TestReverseProxyHealthCheckUnixSocket(t *testing.T) {
}
`, socketName), "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
+ target := fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne())
+ harness.AssertGetResponse(target, 200, "Hello, World!")
}
func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
if runtime.GOOS == "windows" {
t.SkipNow()
}
- tester := caddytest.StartHarness(t)
+ harness := caddytest.StartHarness(t)
f, err := os.CreateTemp("", "*.sock")
if err != nil {
t.Errorf("failed to create TempFile: %s", err)
@@ -453,18 +455,18 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
})
runtime.Gosched() // Allow other goroutines to run
- tester.LoadConfig(fmt.Sprintf(`
+ harness.LoadConfig(fmt.Sprintf(`
{
skip_install_trust
- admin {$TESTING_ADMIN_API}
- http_port 9080
- https_port 9443
+ admin {$TESTING_CADDY_ADMIN_BIND}
+ http_port {$TESTING_CADDY_PORT_ONE}
+ https_port {$TESTING_CADDY_PORT_TWO}
grace_period 1ns
}
- http://localhost:9080 {
+ http://localhost:{$TESTING_CADDY_PORT_ONE} {
reverse_proxy {
to unix/%s
-
+
health_uri /health
health_interval 2s
health_timeout 5s
@@ -472,5 +474,5 @@ func TestReverseProxyHealthCheckUnixSocketWithoutPort(t *testing.T) {
}
`, socketName), "caddyfile")
- tester.AssertGetResponse("http://localhost:9080/", 200, "Hello, World!")
+ harness.AssertGetResponse(fmt.Sprintf("http://localhost:%d/", harness.Tester().PortOne()), 200, "Hello, World!")
}
diff --git a/caddytest/integration/sni_test.go b/caddytest/integration/sni_test.go
index e5aced6e997..8a5cfb73628 100644
--- a/caddytest/integration/sni_test.go
+++ b/caddytest/integration/sni_test.go
@@ -1,6 +1,7 @@
package integration
import (
+ "fmt"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
@@ -8,20 +9,20 @@ import (
func TestDefaultSNI(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`{
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -102,26 +103,27 @@ func TestDefaultSNI(t *testing.T) {
// act and assert
// makes a request with no sni
- tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
+ target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
+ harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
}
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -206,26 +208,27 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// act and assert
// makes a request with no sni
- tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
+ target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
+ harness.AssertGetResponse(target+"version", 200, "hello from a")
}
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// arrange
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
"grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -282,7 +285,8 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// act and assert
// makes a request with no sni
- tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a.caddy.localhost")
+ target := fmt.Sprintf("https://127.0.0.1:%d/", harness.Tester().PortTwo())
+ harness.AssertGetResponse(target+"version", 200, "hello from a.caddy.localhost")
}
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
diff --git a/caddytest/integration/stream_test.go b/caddytest/integration/stream_test.go
index e47697793c1..e23c4f3d8ec 100644
--- a/caddytest/integration/stream_test.go
+++ b/caddytest/integration/stream_test.go
@@ -20,21 +20,21 @@ import (
// (see https://github.com/caddyserver/caddy/issues/3556 for use case)
func TestH2ToH2CStream(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
- "grace_period": 1,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
+ "grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -102,7 +102,7 @@ func TestH2ToH2CStream(t *testing.T) {
expectedBody := "some data to be echoed"
// start the server
- server := testH2ToH2CStreamServeH2C(t)
+ server := testH2ToH2CStreamServeH2C(harness, t)
go server.ListenAndServe()
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
@@ -116,7 +116,7 @@ func TestH2ToH2CStream(t *testing.T) {
Body: io.NopCloser(r),
URL: &url.URL{
Scheme: "https",
- Host: "127.0.0.1:9443",
+ Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
Path: "/tov2ray",
},
Proto: "HTTP/2",
@@ -127,7 +127,7 @@ func TestH2ToH2CStream(t *testing.T) {
// Disable any compression method from server.
req.Header.Set("Accept-Encoding", "identity")
- resp := tester.AssertResponseCode(req, http.StatusOK)
+ resp := harness.AssertResponseCode(req, http.StatusOK)
if resp.StatusCode != http.StatusOK {
return
}
@@ -149,7 +149,7 @@ func TestH2ToH2CStream(t *testing.T) {
}
}
-func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
+func testH2ToH2CStreamServeH2C(harness *caddytest.TestHarness, t *testing.T) *http.Server {
h2s := &http2.Server{}
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
rstring, err := httputil.DumpRequest(r, false)
@@ -163,7 +163,7 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
return
}
- if r.Host != "127.0.0.1:9443" {
+ if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
t.Errorf("r.Host doesn't match, %v!", r.Host)
w.WriteHeader(http.StatusNotFound)
return
@@ -204,21 +204,21 @@ func testH2ToH2CStreamServeH2C(t *testing.T) *http.Server {
// (see https://github.com/caddyserver/caddy/issues/3606 for use case)
func TestH2ToH1ChunkedResponse(t *testing.T) {
- tester := caddytest.StartHarness(t)
- tester.LoadConfig(`
+ harness := caddytest.StartHarness(t)
+ harness.LoadConfig(`
{
"admin": {
- "listen": "{$TESTING_ADMIN_API}"
+ "listen": "{$TESTING_CADDY_ADMIN_BIND}"
},
"apps": {
"http": {
- "http_port": 9080,
- "https_port": 9443,
- "grace_period": 1,
+ "http_port": {$TESTING_CADDY_PORT_ONE},
+ "https_port": {$TESTING_CADDY_PORT_TWO},
+ "grace_period": 1,
"servers": {
"srv0": {
"listen": [
- ":9443"
+ ":{$TESTING_CADDY_PORT_TWO}"
],
"routes": [
{
@@ -305,7 +305,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
}
// start the server
- server := testH2ToH1ChunkedResponseServeH1(t)
+ server := testH2ToH1ChunkedResponseServeH1(harness, t)
go server.ListenAndServe()
defer func() {
ctx, cancel := context.WithTimeout(context.Background(), time.Nanosecond)
@@ -319,7 +319,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
Body: io.NopCloser(r),
URL: &url.URL{
Scheme: "https",
- Host: "127.0.0.1:9443",
+ Host: fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()),
Path: "/tov2ray",
},
Proto: "HTTP/2",
@@ -333,7 +333,7 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
fmt.Fprint(w, expectedBody)
w.Close()
}()
- resp := tester.AssertResponseCode(req, http.StatusOK)
+ resp := harness.AssertResponseCode(req, http.StatusOK)
if resp.StatusCode != http.StatusOK {
return
}
@@ -351,9 +351,9 @@ func TestH2ToH1ChunkedResponse(t *testing.T) {
}
}
-func testH2ToH1ChunkedResponseServeH1(t *testing.T) *http.Server {
+func testH2ToH1ChunkedResponseServeH1(harness *caddytest.TestHarness, t *testing.T) *http.Server {
handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
- if r.Host != "127.0.0.1:9443" {
+ if r.Host != fmt.Sprintf("127.0.0.1:%d", harness.Tester().PortTwo()) {
t.Errorf("r.Host doesn't match, %v!", r.Host)
w.WriteHeader(http.StatusNotFound)
return
diff --git a/caddytest/testing_harness.go b/caddytest/testing_harness.go
index c5af3f6f547..c9d7a70f462 100644
--- a/caddytest/testing_harness.go
+++ b/caddytest/testing_harness.go
@@ -55,6 +55,10 @@ func StartHarness(t *testing.T) *TestHarness {
return o
}
+func (tc *TestHarness) Tester() *Tester {
+ return tc.tester
+}
+
func (tc *TestHarness) Client() *http.Client {
return tc.tester.Client
}
From 3f1ff118f8bd4fad73e09d6374f5cbfe68b5a4c3 Mon Sep 17 00:00:00 2001
From: a
Date: Tue, 18 Jun 2024 23:48:21 -0500
Subject: [PATCH 14/14] noot
---
caddytest/caddytest.go | 2 ++
1 file changed, 2 insertions(+)
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index 58cbfa06b13..c67b9cbb898 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -122,9 +122,11 @@ func (tc *Tester) CleanupCaddy() error {
func (tc *Tester) AdminPort() int {
return tc.adminPort
}
+
func (tc *Tester) PortOne() int {
return tc.portOne
}
+
func (tc *Tester) PortTwo() int {
return tc.portTwo
}