summaryrefslogtreecommitdiff
path: root/caddytest
diff options
context:
space:
mode:
authorMark Sargent <99003+sarge@users.noreply.github.com>2020-04-27 13:23:46 +1200
committerGitHub <noreply@github.com>2020-04-27 13:23:46 +1200
commit570d84f7d3642b85f96906ceab6402678177949e (patch)
treee16c2693b8ec69bb95163fc389f4939ecc9150dc /caddytest
parenta6761153cbbf4547b9c0a76656d74f1401067205 (diff)
refactored caddytest helpers (#3285)
* refactored caddytest helpers * added cookie jar support. Added support for more http verbs
Diffstat (limited to 'caddytest')
-rw-r--r--caddytest/caddytest.go242
-rw-r--r--caddytest/integration/caddyfile_test.go45
-rw-r--r--caddytest/integration/sni_test.go15
-rw-r--r--caddytest/integration/testdata/cookie.html1
4 files changed, 227 insertions, 76 deletions
diff --git a/caddytest/caddytest.go b/caddytest/caddytest.go
index c94ef6c..b0f23d0 100644
--- a/caddytest/caddytest.go
+++ b/caddytest/caddytest.go
@@ -11,6 +11,7 @@ import (
"log"
"net"
"net/http"
+ "net/http/cookiejar"
"os"
"path"
"regexp"
@@ -46,6 +47,30 @@ var (
matchCert = regexp.MustCompile(`(/[\w\d\.]+\.crt)`)
)
+// Tester represents an instance of a test client.
+type Tester struct {
+ Client *http.Client
+ t *testing.T
+}
+
+// NewTester will create a new testing client with an attached cookie jar
+func NewTester(t *testing.T) *Tester {
+
+ jar, err := cookiejar.New(nil)
+ if err != nil {
+ t.Fatalf("failed to create cookiejar: %s", err)
+ }
+
+ return &Tester{
+ Client: &http.Client{
+ Transport: CreateTestingTransport(),
+ Jar: jar,
+ Timeout: 5 * time.Second,
+ },
+ t: t,
+ }
+}
+
type configLoadError struct {
Response string
}
@@ -59,41 +84,42 @@ func timeElapsed(start time.Time, name string) {
// InitServer this will configure the server with a configurion of a specific
// type. The configType must be either "json" or the adapter type.
-func InitServer(t *testing.T, rawConfig string, configType string) {
+func (tc *Tester) InitServer(rawConfig string, configType string) {
- if err := initServer(t, rawConfig, configType); err != nil {
- t.Logf("failed to load config: %s", err)
- t.Fail()
+ if err := tc.initServer(rawConfig, configType); err != nil {
+ tc.t.Logf("failed to load config: %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 initServer(t *testing.T, rawConfig string, configType string) error {
+func (tc *Tester) initServer(rawConfig string, configType string) error {
if testing.Short() {
- t.SkipNow()
+ tc.t.SkipNow()
return nil
}
err := validateTestPrerequisites()
if err != nil {
- t.Skipf("skipping tests as failed integration prerequisites. %s", err)
+ tc.t.Skipf("skipping tests as failed integration prerequisites. %s", err)
return nil
}
- t.Cleanup(func() {
- if t.Failed() {
+ tc.t.Cleanup(func() {
+ if tc.t.Failed() {
res, err := http.Get(fmt.Sprintf("http://localhost:%d/config/", Default.AdminPort))
if err != nil {
- t.Log("unable to read the current config")
+ tc.t.Log("unable to read the current config")
+ return
}
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
var out bytes.Buffer
json.Indent(&out, body, "", " ")
- t.Logf("----------- failed with config -----------\n%s", out.String())
+ tc.t.Logf("----------- failed with config -----------\n%s", out.String())
}
})
@@ -101,11 +127,10 @@ func initServer(t *testing.T, rawConfig string, configType string) error {
client := &http.Client{
Timeout: time.Second * 2,
}
-
start := time.Now()
req, err := http.NewRequest("POST", fmt.Sprintf("http://localhost:%d/load", Default.AdminPort), strings.NewReader(rawConfig))
if err != nil {
- t.Errorf("failed to create request. %s", err)
+ tc.t.Errorf("failed to create request. %s", err)
return err
}
@@ -117,7 +142,7 @@ func initServer(t *testing.T, rawConfig string, configType string) error {
res, err := client.Do(req)
if err != nil {
- t.Errorf("unable to contact caddy server. %s", err)
+ tc.t.Errorf("unable to contact caddy server. %s", err)
return err
}
timeElapsed(start, "caddytest: config load time")
@@ -125,7 +150,7 @@ func initServer(t *testing.T, rawConfig string, configType string) error {
defer res.Body.Close()
body, err := ioutil.ReadAll(res.Body)
if err != nil {
- t.Errorf("unable to read response. %s", err)
+ tc.t.Errorf("unable to read response. %s", err)
return err
}
@@ -213,8 +238,8 @@ func prependCaddyFilePath(rawConfig string) string {
return r
}
-// creates a testing transport that forces call dialing connections to happen locally
-func createTestingTransport() *http.Transport {
+// CreateTestingTransport creates a testing transport that forces call dialing connections to happen locally
+func CreateTestingTransport() *http.Transport {
dialer := net.Dialer{
Timeout: 5 * time.Second,
@@ -243,78 +268,44 @@ func createTestingTransport() *http.Transport {
// AssertLoadError will load a config and expect an error
func AssertLoadError(t *testing.T, rawConfig string, configType string, expectedError string) {
- err := initServer(t, rawConfig, configType)
- if !strings.Contains(err.Error(), expectedError) {
- t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
- }
-}
-
-// AssertGetResponse request a URI and assert the status code and the body contains a string
-func AssertGetResponse(t *testing.T, requestURI string, statusCode int, expectedBody string) (*http.Response, string) {
- resp, body := AssertGetResponseBody(t, requestURI, statusCode)
- if !strings.Contains(body, expectedBody) {
- t.Errorf("requesting \"%s\" expected response body \"%s\" but got \"%s\"", requestURI, expectedBody, body)
- }
- return resp, body
-}
-
-// AssertGetResponseBody request a URI and assert the status code matches
-func AssertGetResponseBody(t *testing.T, requestURI string, expectedStatusCode int) (*http.Response, string) {
- client := &http.Client{
- Transport: createTestingTransport(),
- }
-
- resp, err := client.Get(requestURI)
- if err != nil {
- t.Errorf("failed to call server %s", err)
- return nil, ""
- }
+ tc := NewTester(t)
- defer resp.Body.Close()
-
- if expectedStatusCode != resp.StatusCode {
- t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, expectedStatusCode, resp.StatusCode)
- }
-
- body, err := ioutil.ReadAll(resp.Body)
- if err != nil {
- t.Errorf("unable to read the response body %s", err)
- return nil, ""
+ err := tc.initServer(rawConfig, configType)
+ if !strings.Contains(err.Error(), expectedError) {
+ t.Errorf("expected error \"%s\" but got \"%s\"", expectedError, err.Error())
}
-
- return resp, string(body)
}
// AssertRedirect makes a request and asserts the redirection happens
-func AssertRedirect(t *testing.T, requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
+func (tc *Tester) AssertRedirect(requestURI string, expectedToLocation string, expectedStatusCode int) *http.Response {
redirectPolicyFunc := func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}
- client := &http.Client{
- CheckRedirect: redirectPolicyFunc,
- Transport: createTestingTransport(),
- }
+ // 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 := client.Get(requestURI)
+ resp, err := tc.Client.Get(requestURI)
if err != nil {
- t.Errorf("failed to call server %s", err)
+ tc.t.Errorf("failed to call server %s", err)
return nil
}
if expectedStatusCode != resp.StatusCode {
- t.Errorf("requesting \"%s\" expected status code: %d but got %d", requestURI, 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 {
- t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
+ tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got error: %s", requestURI, expectedToLocation, err)
}
if expectedToLocation != loc.String() {
- t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
+ tc.t.Errorf("requesting \"%s\" expected location: \"%s\" but got \"%s\"", requestURI, expectedToLocation, loc.String())
}
return resp
@@ -371,3 +362,124 @@ func AssertAdapt(t *testing.T, rawConfig string, adapterName string, expectedRes
t.Fail()
}
}
+
+// Generic request functions
+
+func applyHeaders(t *testing.T, 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.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 := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ tc.t.Fatalf("unable to read the response body %s", err)
+ }
+
+ body := string(bytes)
+
+ if !strings.Contains(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/integration/caddyfile_test.go b/caddytest/integration/caddyfile_test.go
index dd3dcba..4e9bdd9 100644
--- a/caddytest/integration/caddyfile_test.go
+++ b/caddytest/integration/caddyfile_test.go
@@ -1,6 +1,8 @@
package integration
import (
+ "net/http"
+ "net/url"
"testing"
"github.com/caddyserver/caddy/v2/caddytest"
@@ -9,7 +11,8 @@ import (
func TestRespond(t *testing.T) {
// arrange
- caddytest.InitServer(t, `
+ tester := caddytest.NewTester(t)
+ tester.InitServer(`
{
http_port 9080
https_port 9443
@@ -23,13 +26,14 @@ func TestRespond(t *testing.T) {
`, "caddyfile")
// act and assert
- caddytest.AssertGetResponse(t, "http://localhost:9080/version", 200, "hello from localhost")
+ tester.AssertGetResponse("http://localhost:9080/version", 200, "hello from localhost")
}
func TestRedirect(t *testing.T) {
// arrange
- caddytest.InitServer(t, `
+ tester := caddytest.NewTester(t)
+ tester.InitServer(`
{
http_port 9080
https_port 9443
@@ -46,10 +50,10 @@ func TestRedirect(t *testing.T) {
`, "caddyfile")
// act and assert
- caddytest.AssertRedirect(t, "http://localhost:9080/", "http://localhost:9080/hello", 301)
+ tester.AssertRedirect("http://localhost:9080/", "http://localhost:9080/hello", 301)
// follow redirect
- caddytest.AssertGetResponse(t, "http://localhost:9080/", 200, "hello from localhost")
+ tester.AssertGetResponse("http://localhost:9080/", 200, "hello from localhost")
}
func TestDuplicateHosts(t *testing.T) {
@@ -66,3 +70,34 @@ func TestDuplicateHosts(t *testing.T) {
"caddyfile",
"duplicate site address not allowed")
}
+
+func TestReadCookie(t *testing.T) {
+
+ localhost, _ := url.Parse("http://localhost")
+ cookie := http.Cookie{
+ Name: "clientname",
+ Value: "caddytest",
+ }
+
+ // arrange
+ tester := caddytest.NewTester(t)
+ tester.Client.Jar.SetCookies(localhost, []*http.Cookie{&cookie})
+ tester.InitServer(`
+ {
+ http_port 9080
+ https_port 9443
+ }
+
+ localhost:9080 {
+ templates {
+ root testdata
+ }
+ file_server {
+ root testdata
+ }
+ }
+ `, "caddyfile")
+
+ // act and assert
+ tester.AssertGetResponse("http://localhost:9080/cookie.html", 200, "<h2>Cookie.ClientName caddytest</h2>")
+}
diff --git a/caddytest/integration/sni_test.go b/caddytest/integration/sni_test.go
index f26131e..46f7c83 100644
--- a/caddytest/integration/sni_test.go
+++ b/caddytest/integration/sni_test.go
@@ -9,7 +9,8 @@ import (
func TestDefaultSNI(t *testing.T) {
// arrange
- caddytest.InitServer(t, `{
+ tester := caddytest.NewTester(t)
+ tester.InitServer(`{
"apps": {
"http": {
"http_port": 9080,
@@ -98,13 +99,14 @@ func TestDefaultSNI(t *testing.T) {
// act and assert
// makes a request with no sni
- caddytest.AssertGetResponse(t, "https://127.0.0.1:9443/version", 200, "hello from a")
+ tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
}
func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// arrange
- caddytest.InitServer(t, `
+ tester := caddytest.NewTester(t)
+ tester.InitServer(`
{
"apps": {
"http": {
@@ -198,13 +200,14 @@ func TestDefaultSNIWithNamedHostAndExplicitIP(t *testing.T) {
// act and assert
// makes a request with no sni
- caddytest.AssertGetResponse(t, "https://127.0.0.1:9443/version", 200, "hello from a")
+ tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
}
func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// arrange
- caddytest.InitServer(t, `
+ tester := caddytest.NewTester(t)
+ tester.InitServer(`
{
"apps": {
"http": {
@@ -270,7 +273,7 @@ func TestDefaultSNIWithPortMappingOnly(t *testing.T) {
// act and assert
// makes a request with no sni
- caddytest.AssertGetResponse(t, "https://127.0.0.1:9443/version", 200, "hello from a")
+ tester.AssertGetResponse("https://127.0.0.1:9443/version", 200, "hello from a")
}
func TestHttpOnlyOnDomainWithSNI(t *testing.T) {
diff --git a/caddytest/integration/testdata/cookie.html b/caddytest/integration/testdata/cookie.html
new file mode 100644
index 0000000..fa53a39
--- /dev/null
+++ b/caddytest/integration/testdata/cookie.html
@@ -0,0 +1 @@
+<h2>Cookie.ClientName {{.Cookie "clientname"}}</h2> \ No newline at end of file