This commit is contained in:
a 2024-06-18 22:36:02 -05:00
parent 7bc7e1680e
commit c0d9a2383e
No known key found for this signature in database
GPG key ID: 374BC539FE795AF0
16 changed files with 121 additions and 84 deletions

View file

@ -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()

View file

@ -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

View file

@ -68,7 +68,7 @@ func TestLoadUnorderedJSON(t *testing.T) {
}
},
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"pki": {

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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 {

View file

@ -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

View file

@ -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

View file

@ -11,7 +11,7 @@ func TestLeafCertLoaders(t *testing.T) {
tester.LoadConfig(`
{
"admin": {
"listen": "localhost:2999"
"listen": "{$TESTING_ADMIN_API}"
},
"apps": {
"http": {

View file

@ -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

View file

@ -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": {

View file

@ -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

View file

@ -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": {

View file

@ -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": {

View file

@ -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