Browse Source

Split integration tests into their own files and outside of root.

multi-app
Benjamin Shelton 4 months ago
parent
commit
b03cece446
14 changed files with 904 additions and 721 deletions
  1. +0
    -367
      capstan_test.go
  2. +140
    -0
      tests/custom_base_route_test.go
  3. +22
    -0
      tests/dependency_test.go
  4. +10
    -0
      tests/doc.go
  5. +134
    -0
      tests/index_endpoint_test.go
  6. +20
    -0
      tests/main_test.go
  7. +107
    -0
      tests/method_endpoint_test.go
  8. +160
    -0
      tests/method_path_override_test.go
  9. +80
    -0
      tests/params_test.go
  10. +104
    -0
      tests/redirection_test.go
  11. +22
    -0
      tests/return_types_test.go
  12. +46
    -354
      tests/server.go
  13. +33
    -0
      tests/special_endpoints_test.go
  14. +26
    -0
      tests/types.go

+ 0
- 367
capstan_test.go View File

@@ -1,368 +1 @@
// This is the unit test package for Capstan. It provides a high level series of
// usage tests that exercise its exposed functionality as per endpoints and
// their respective configurations. Consequently, this test may be used in
// addition to Capstan's documentation as each test is segregated from the
// others and performed (mostly) in isolation. A single server instance is spun
// up to run internally, but each test has its own endpoint.
//
// Other packages provide more fine-grained unit tests to validate correctness;
// this package should be considered a test of the public API.

package capstan_test

import (
"bytes"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"

"git.pluggableideas.com/destrealm/go/capstan/tests"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

var server *httptest.Server

type testMethods struct {
HasBody bool
Method func(string) *please.ClientRequest
}

type testBody struct {
Code int
Body []byte
}

type testBodyMethod struct {
Code int
Body []byte
Method string
}

func TestMain(m *testing.M) {
server = tests.Server()
defer server.Close()

os.Exit(m.Run())
}

// Test_MethodEndpoints tests all method endpoints:
//
// - Get
// - Head
// - Post
// - Put
// - Patch
// - Delete
// - Options
// - Trace
// - Connect
func Test_MethodEndpoints(t *testing.T) {
var out []byte
client := server.Client()
client.CheckRedirect = nil

requester := please.MakeRequest(server.URL)
requester.SetClient(server.Client())

d := map[string]*testMethods{
"GET": &testMethods{true, requester.Get},
"POST": &testMethods{true, requester.Post},
"PUT": &testMethods{true, requester.Put},
"PATCH": &testMethods{true, requester.Patch},
"DELETE": &testMethods{true, requester.Delete},
"HEAD": &testMethods{false, requester.Head},
}

for k, v := range d {
req := v.Method("/methods")
response, err := req.Commit()
if err != nil {
t.Fatalf("%s response error: %v", k, errors.Unfurl(err))
}
out, err = response.ReadAll()
if err != nil {
t.Fatalf("%s read body error: %v", k, err)
}
if v.HasBody && bytes.Compare(out, []byte(k)) != 0 {
t.Errorf("%s returned unexpected body: %v", k, string(out))
}
if response.Headers().Get("X-Test-Method") != k {
t.Errorf("%s did not return correct X-Test-Method header", k)
}
}
}

func Test_Redirects(t *testing.T) {
client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

requester := please.MakeRequest(server.URL)
requester.SetClient(client)

req := requester.Get("/redirect/external")
response, err := req.Commit()
if err != nil {
t.Fatalf("response error during redirect: %v", errors.Unfurl(err))
}

if response.StatusCode != 301 {
t.Fatalf("received non-redirect status code %d", response.StatusCode)
}

if h := response.Headers().Get("location"); h == "" {
t.Fatal("expected location header missing")
} else {
if !strings.Contains(h, server.URL+"/methods") {
t.Fatalf("unexpected redirection path: %s", h)
}
}

req = requester.Get("/redirect/internal")
response, err = req.Commit()
if err != nil {
t.Fatalf("response error during redirect: %v", errors.Unfurl(err))
}

if response.StatusCode != 301 {
t.Fatalf("received non-redirect status code %d", response.StatusCode)
}

if h := response.Headers().Get("location"); h == "" {
t.Fatal("expected location header missing")
} else {
if !strings.Contains(h, "/methods") {
t.Fatalf("unexpected redirection path: %s", h)
}
}
}

func Test_IndexController(t *testing.T) {
client := server.Client()
client.CheckRedirect = nil

tests := map[string]*testBody{
// Optional trailing slash ("/?").
"/index": &testBody{200, []byte("INDEX")},
"/index/": &testBody{200, []byte("INDEX")},
"/index/testing": &testBody{200, []byte("testing")},
"/index/testing/": &testBody{200, []byte("testing")},

// Mandatory slash ("/").
"/index-slash": &testBody{404, nil},
"/index-slash/": &testBody{200, []byte("INDEX")},
"/index-slash/testing": &testBody{404, nil},
"/index-slash/testing/": &testBody{200, []byte("testing")},

// Mandatory no-slash exclamation ("!").
"/index-no-slash": &testBody{200, []byte("INDEX")},
"/index-no-slash/": &testBody{404, nil},
"/index-no-slash/testing": &testBody{200, []byte("testing")},
"/index-no-slash/testing/": &testBody{404, nil},

// Mandatory no-slash exclamation plus slash ("/!").
"/index-no-slash-slash": &testBody{200, []byte("INDEX")},
"/index-no-slash-slash/": &testBody{404, nil},
"/index-no-slash-slash/testing": &testBody{200, []byte("testing")},
"/index-no-slash-slash/testing/": &testBody{404, nil},

// Test index-specific route.
"/index-sub-index/index": &testBody{200, []byte("INDEX")},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}

out, err := response.ReadAll()
if err != nil {
t.Fatalf("%s: error reading body: %v", url,
errors.Unfurl(err))
}

// TODO: A nil body indicates we're not going to do a complete
// comparison at this time.
if body.Body != nil && bytes.Compare(out, body.Body) != 0 {
t.Fatalf(`%s: unexpected body: "%v"`, url,
string(out))
}
}
}

func Test_ParameterizedRedirects(t *testing.T) {
client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBody{
"/param-redirect-slash/testing1": &testBody{301, nil},
"/param-redirect-slash/testing1/": &testBody{200, []byte("testing1")},
"/param-redirect-no-slash/testing2": &testBody{200, []byte("testing2")},
"/param-redirect-no-slash/testing2/": &testBody{301, nil},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

func Test_CustomBaseRoutes(t *testing.T) {
client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBody{
"/custom-base-route/demo": &testBody{200, []byte("DEMO")},
"/custom-base-route/custom-route-1": &testBody{200, []byte("CUSTOM-1")},
"/custom-base-route/custom-route-2": &testBody{200, []byte("CUSTOM-N")},
"/custom-base-route/custom-route-2/": &testBody{404, nil},
"/custom-base-route/custom-route-3": &testBody{301, nil},
"/custom-base-route/custom-route-3/": &testBody{200, []byte("CUSTOM-O")},
"/custom-base-route/custom-route-4": &testBody{404, nil},
"/custom-base-route/custom-route-4/": &testBody{200, []byte("CUSTOM-S")},
"/custom-base-route/custom-route-underscore": &testBody{200, []byte("CUSTOM_UNDERSCORE")},
"/custom-base-route/custom-base-route-abuse-1-a": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-2a": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-3-z": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-4z": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-5-n": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-5-n/": &testBody{404, nil},
"/custom-base-route/custom-base-route-abuse-6-o": &testBody{301, nil},
"/custom-base-route/custom-base-route-abuse-6-o/": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-7-s": &testBody{404, nil},
"/custom-base-route/custom-base-route-abuse-7-s/": &testBody{200, []byte("ABUSE")},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

func Test_MethodOverride(t *testing.T) {
client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBodyMethod{
"/method-override/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override/new": &testBodyMethod{200, []byte("POST"), "POST"},
"/method-override/remove": &testBodyMethod{200, []byte("DELETE"), "DELETE"},
"/method-override/replace": &testBodyMethod{200, []byte("PUT"), "PUT"},
"/method-override/update": &testBodyMethod{200, []byte("PATCH"), "PATCH"},
"/method-override/summary": &testBodyMethod{200, []byte("HEAD"), "HEAD"},

"/method-override-no-slash/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override-no-slash/read/": &testBodyMethod{404, nil, "GET"},
"/method-override-no-slash/new": &testBodyMethod{200, []byte("POST"), "POST"},
"/method-override-no-slash/new/": &testBodyMethod{404, nil, "POST"},
"/method-override-no-slash/remove": &testBodyMethod{200, []byte("DELETE"), "DELETE"},
"/method-override-no-slash/remove/": &testBodyMethod{404, nil, "DELETE"},
"/method-override-no-slash/replace": &testBodyMethod{200, []byte("PUT"), "PUT"},
"/method-override-no-slash/replace/": &testBodyMethod{404, nil, "PUT"},
"/method-override-no-slash/update": &testBodyMethod{200, []byte("PATCH"), "PATCH"},
"/method-override-no-slash/update/": &testBodyMethod{404, nil, "PATCH"},
"/method-override-no-slash/summary": &testBodyMethod{200, []byte("HEAD"), "HEAD"},
"/method-override-no-slash/summary/": &testBodyMethod{404, nil, "HEAD"},

"/method-override-slash/read": &testBodyMethod{404, nil, "GET"},
"/method-override-slash/read/": &testBodyMethod{200, []byte("GET"), "GET"},

"/method-override-optional/read": &testBodyMethod{301, nil, "GET"},
"/method-override-optional/read/": &testBodyMethod{200, []byte("GET"), "GET"},

"/method-override-optional-none/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override-optional-none/read/": &testBodyMethod{301, nil, "GET"},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Method(body.Method, url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

// Test_SpecialEndpoints tests specially named endpoints:
//
// - Index
func Test_SpecialEndpoints(t *testing.T) {

}

// Test_WebSocket tests the websocket endpoint.
func Test_WebSocketEndpoint(t *testing.T) {

}

// Test_NamedEndpoints tests named suffix endpoints. These endpoints contain a
// camel-to-hyphen-case converted endpoint with the method as its suffix (plus a
// single character modifier indicating the trailing slash state). As an
// example, a controller endpoint named ListEmailsGetN would be translated to
// the endpoint name "list-emails" using the GET method and no trailing slash
// ("N" suffix).
//
// This unit test tests naming and most common methods, plus the suffix
// characters N (no trailing slash), O (optional trailing slash), and S
// (mandatory trailing slash).
func Test_NamedEndpoints(t *testing.T) {

}

+ 140
- 0
tests/custom_base_route_test.go View File

@@ -0,0 +1,140 @@
// +build integration

// Server configuration for unit testing.

package tests

import (
"net/http"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

type CustomBaseRouteController struct {
capstan.BaseController
}

func (c *CustomBaseRouteController) Demo(ctx capstan.Context) error {
_, err := ctx.Write([]byte("DEMO"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute1(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-1"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute2N(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-N"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute3O(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-O"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute4S(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-S"))
return err
}

func (c *CustomBaseRouteController) Get_Custom_Route_Underscore(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM_UNDERSCORE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse1A(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse2a(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse3Z(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse4z(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse5NN(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse6OO(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse7SS(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}


func Test_CustomBaseRoutes(t *testing.T) {
server := NewServer(func (app capstan.Server){
app.BindRoute(&CustomBaseRouteController{
capstan.BaseController{
Path: "/custom-base-route",
},
})
})
defer server.Close()

client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBody{
"/custom-base-route/demo": &testBody{200, []byte("DEMO")},
"/custom-base-route/custom-route-1": &testBody{200, []byte("CUSTOM-1")},
"/custom-base-route/custom-route-2": &testBody{200, []byte("CUSTOM-N")},
"/custom-base-route/custom-route-2/": &testBody{404, nil},
"/custom-base-route/custom-route-3": &testBody{301, nil},
"/custom-base-route/custom-route-3/": &testBody{200, []byte("CUSTOM-O")},
"/custom-base-route/custom-route-4": &testBody{404, nil},
"/custom-base-route/custom-route-4/": &testBody{200, []byte("CUSTOM-S")},
"/custom-base-route/custom-route-underscore": &testBody{200, []byte("CUSTOM_UNDERSCORE")},
"/custom-base-route/custom-base-route-abuse-1-a": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-2a": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-3-z": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-4z": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-5-n": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-5-n/": &testBody{404, nil},
"/custom-base-route/custom-base-route-abuse-6-o": &testBody{301, nil},
"/custom-base-route/custom-base-route-abuse-6-o/": &testBody{200, []byte("ABUSE")},
"/custom-base-route/custom-base-route-abuse-7-s": &testBody{404, nil},
"/custom-base-route/custom-base-route-abuse-7-s/": &testBody{200, []byte("ABUSE")},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

+ 22
- 0
tests/dependency_test.go View File

@@ -0,0 +1,22 @@
// +build integration

package tests

import (
"git.pluggableideas.com/destrealm/go/capstan"
)

type DependencyTestController struct {
capstan.BaseController
App capstan.Server `inject:"app"`
Dep1 *Dep `inject:""`
Dep2 *Dep `inject:"dep2"`
Dep3 *Dep `inject:"dep3"`
}

// FailDependencyTestController attempts to inject a dependency that doesn't
// exist. This should generate a panic when the endpoint is loaded.
type FailDependencyTestController struct {
capstan.BaseController
None *Dep `inject:"does-not-exist"`
}

+ 10
- 0
tests/doc.go View File

@@ -0,0 +1,10 @@
// +build integration
// This directory contains integration tests validating the user-facing Capstan
// API.
//
// These tests may include directives that communicate with other services or
// require spinning up multiple instances of the Capstan server. Consequently,
// these tests require the use of the `integration` tag. These tests will
// not run unless you enable the flag or use `make test`.

package tests

+ 134
- 0
tests/index_endpoint_test.go View File

@@ -0,0 +1,134 @@
// +build integration

package tests

import (
"bytes"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

type IndexController struct {
capstan.BaseController
}

func (c *IndexController) Index(ctx capstan.Context) error {
_, err := ctx.Write([]byte("INDEX"))
return err
}

func (c *IndexController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte(ctx.Param("param")))
return err
}

func Test_IndexController(t *testing.T) {
server := NewServer(func(app capstan.Server) {
app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index",
Path: "/index/<param:string>/?",
Index: "/?",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-slash",
Path: "/index-slash/<param:string>/",
Index: "/+", // Implicit mandatory slashes don't work here.
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-no-slash",
Path: "/index-no-slash/<param:string>!",
Index: "!",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-no-slash-slash",
Path: "/index-no-slash-slash/<param:string>/!",
Index: "/!",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-sub-index",
Path: "/index-sub-index/<param:string>/!",
Index: "/index",
},
})
})
defer server.Close()

client := server.Client()
client.CheckRedirect = nil

tests := map[string]*testBody{
// Optional trailing slash ("/?").
"/index": &testBody{200, []byte("INDEX")},
"/index/": &testBody{200, []byte("INDEX")},
"/index/testing": &testBody{200, []byte("testing")},
"/index/testing/": &testBody{200, []byte("testing")},

// Mandatory slash ("/").
"/index-slash": &testBody{404, nil},
"/index-slash/": &testBody{200, []byte("INDEX")},
"/index-slash/testing": &testBody{404, nil},
"/index-slash/testing/": &testBody{200, []byte("testing")},

// Mandatory no-slash exclamation ("!").
"/index-no-slash": &testBody{200, []byte("INDEX")},
"/index-no-slash/": &testBody{404, nil},
"/index-no-slash/testing": &testBody{200, []byte("testing")},
"/index-no-slash/testing/": &testBody{404, nil},

// Mandatory no-slash exclamation plus slash ("/!").
"/index-no-slash-slash": &testBody{200, []byte("INDEX")},
"/index-no-slash-slash/": &testBody{404, nil},
"/index-no-slash-slash/testing": &testBody{200, []byte("testing")},
"/index-no-slash-slash/testing/": &testBody{404, nil},

// Test index-specific route.
"/index-sub-index/index": &testBody{200, []byte("INDEX")},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}

out, err := response.ReadAll()
if err != nil {
t.Fatalf("%s: error reading body: %v", url,
errors.Unfurl(err))
}

// TODO: A nil body indicates we're not going to do a complete
// comparison at this time.
if body.Body != nil && bytes.Compare(out, body.Body) != 0 {
t.Fatalf(`%s: unexpected body: "%v"`, url,
string(out))
}
}
}

+ 20
- 0
tests/main_test.go View File

@@ -0,0 +1,20 @@
// This is the unit test package for Capstan. It provides a high level series of
// usage tests that exercise its exposed functionality as per endpoints and
// their respective configurations. Consequently, this test may be used in
// addition to Capstan's documentation as each test is segregated from the
// others and performed (mostly) in isolation. A single server instance is spun
// up to run internally, but each test has its own endpoint.
//
// Other packages provide more fine-grained unit tests to validate correctness;
// this package should be considered a test of the public API.

package tests

import (
"os"
"testing"
)

func TestMain(m *testing.M) {
os.Exit(m.Run())
}

+ 107
- 0
tests/method_endpoint_test.go View File

@@ -0,0 +1,107 @@
// +build integration

package tests

import (
"bytes"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

type MethodEndpointController struct {
capstan.BaseController
}

func (m *MethodEndpointController) Get(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "GET")
ctx.Write([]byte("GET"))
return nil
}

func (m *MethodEndpointController) Post(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "POST")
ctx.Write([]byte("POST"))
return nil
}

func (m *MethodEndpointController) Put(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "PUT")
ctx.Write([]byte("PUT"))
return nil
}

func (m *MethodEndpointController) Patch(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "PATCH")
ctx.Write([]byte("PATCH"))
return nil
}

func (m *MethodEndpointController) Delete(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "DELETE")
ctx.Write([]byte("DELETE"))
return nil
}

func (m *MethodEndpointController) Head(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "HEAD")
return nil
}

// Test_MethodEndpoints tests all method endpoints (see controller above):
//
// - Get
// - Head
// - Post
// - Put
// - Patch
// - Delete
// - Options
// - Trace
// - Connect
func Test_MethodEndpoints(t *testing.T) {
server := NewServer(func(app capstan.Server) {
app.BindRoute(&MethodEndpointController{
capstan.BaseController{
Path: "/methods",
},
})
})
defer server.Close()

var out []byte
client := server.Client()
client.CheckRedirect = nil

requester := please.MakeRequest(server.URL)
requester.SetClient(server.Client())

d := map[string]*testMethods{
"GET": &testMethods{true, requester.Get},
"POST": &testMethods{true, requester.Post},
"PUT": &testMethods{true, requester.Put},
"PATCH": &testMethods{true, requester.Patch},
"DELETE": &testMethods{true, requester.Delete},
"HEAD": &testMethods{false, requester.Head},
}

for k, v := range d {
req := v.Method("/methods")
response, err := req.Commit()
if err != nil {
t.Fatalf("%s response error: %v", k, errors.Unfurl(err))
}
out, err = response.ReadAll()
if err != nil {
t.Fatalf("%s read body error: %v", k, err)
}
if v.HasBody && bytes.Compare(out, []byte(k)) != 0 {
t.Errorf("%s returned unexpected body: %v", k, string(out))
}
if response.Headers().Get("X-Test-Method") != k {
t.Errorf("%s did not return correct X-Test-Method header", k)
}
}
}

+ 160
- 0
tests/method_path_override_test.go View File

@@ -0,0 +1,160 @@
// +build integration

// Server configuration for unit testing.

package tests

import (
"net/http"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

type MethodOverrideController struct {
capstan.BaseController
}

func (c *MethodOverrideController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte("GET"))
return err
}

func (c *MethodOverrideController) Post(ctx capstan.Context) error {
_, err := ctx.Write([]byte("POST"))
return err
}

func (c *MethodOverrideController) Delete(ctx capstan.Context) error {
_, err := ctx.Write([]byte("DELETE"))
return err
}

func (c *MethodOverrideController) Put(ctx capstan.Context) error {
_, err := ctx.Write([]byte("PUT"))
return err
}

func (c *MethodOverrideController) Patch(ctx capstan.Context) error {
_, err := ctx.Write([]byte("PATCH"))
return err
}

func (c *MethodOverrideController) Head(ctx capstan.Context) error {
_, err := ctx.Write([]byte("HEAD"))
return err
}

func Test_MethodOverride(t *testing.T) {
server := NewServer(func(app capstan.Server) {
app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override",
Get: "/read",
Post: "/new",
Put: "/replace",
Patch: "/update",
Delete: "/remove",
Head: "/summary",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-no-slash",
Get: "/read/!",
Post: "/new/!",
Put: "/replace/!",
Patch: "/update/!",
Delete: "/remove/!",
Head: "/summary/!",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-slash",
Get: "/read/",
Post: "/new/",
Put: "/replace/",
Patch: "/update/",
Delete: "/remove/",
Head: "/summary/",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-optional",
Get: "/read/?",
Head: "/summary/?",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-optional-none",
Get: "/read?",
Head: "/summary?",
},
})

})
defer server.Close()

client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBodyMethod{
"/method-override/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override/new": &testBodyMethod{200, []byte("POST"), "POST"},
"/method-override/remove": &testBodyMethod{200, []byte("DELETE"), "DELETE"},
"/method-override/replace": &testBodyMethod{200, []byte("PUT"), "PUT"},
"/method-override/update": &testBodyMethod{200, []byte("PATCH"), "PATCH"},
"/method-override/summary": &testBodyMethod{200, []byte("HEAD"), "HEAD"},

"/method-override-no-slash/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override-no-slash/read/": &testBodyMethod{404, nil, "GET"},
"/method-override-no-slash/new": &testBodyMethod{200, []byte("POST"), "POST"},
"/method-override-no-slash/new/": &testBodyMethod{404, nil, "POST"},
"/method-override-no-slash/remove": &testBodyMethod{200, []byte("DELETE"), "DELETE"},
"/method-override-no-slash/remove/": &testBodyMethod{404, nil, "DELETE"},
"/method-override-no-slash/replace": &testBodyMethod{200, []byte("PUT"), "PUT"},
"/method-override-no-slash/replace/": &testBodyMethod{404, nil, "PUT"},
"/method-override-no-slash/update": &testBodyMethod{200, []byte("PATCH"), "PATCH"},
"/method-override-no-slash/update/": &testBodyMethod{404, nil, "PATCH"},
"/method-override-no-slash/summary": &testBodyMethod{200, []byte("HEAD"), "HEAD"},
"/method-override-no-slash/summary/": &testBodyMethod{404, nil, "HEAD"},

"/method-override-slash/read": &testBodyMethod{404, nil, "GET"},
"/method-override-slash/read/": &testBodyMethod{200, []byte("GET"), "GET"},

"/method-override-optional/read": &testBodyMethod{301, nil, "GET"},
"/method-override-optional/read/": &testBodyMethod{200, []byte("GET"), "GET"},

"/method-override-optional-none/read": &testBodyMethod{200, []byte("GET"), "GET"},
"/method-override-optional-none/read/": &testBodyMethod{301, nil, "GET"},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Method(body.Method, url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

+ 80
- 0
tests/params_test.go View File

@@ -0,0 +1,80 @@
// +build integration

package tests

import (
"net/http"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

type ParamsController struct {
capstan.BaseController
}

func (c *ParamsController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte(ctx.Param("from")))
return err
}

func Test_ParameterizedRedirects(t *testing.T) {
server := NewServer(func(app capstan.Server) {
app.BindRoute(&ParamsController{
capstan.BaseController{
Path: "/params/<from:string>!",
},
})

// Used to test optional trailing slash with a default redirect preference
// terminating with a slash.
app.BindRoute(&ParamsController{
capstan.BaseController{
Name: "parameterized-redirects-slash",
Path: "/param-redirect-slash/<from:string>/?",
},
})

// Used to test optional trailing slash with a default redirect preference
// terminating without a slash.
app.BindRoute(&ParamsController{
capstan.BaseController{
Name: "parameterized-redirects-no-slash",
Path: "/param-redirect-no-slash/<from:string>?",
},
})
})
defer server.Close()

client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

tests := map[string]*testBody{
"/param-redirect-slash/testing1": &testBody{301, nil},
"/param-redirect-slash/testing1/": &testBody{200, []byte("testing1")},
"/param-redirect-no-slash/testing2": &testBody{200, []byte("testing2")},
"/param-redirect-no-slash/testing2/": &testBody{301, nil},
}

for url, body := range tests {
requester := please.MakeRequest(server.URL)
requester.SetClient(client)

// Test index response.
req := requester.Get(url)
response, err := req.Commit()
if err != nil {
t.Fatalf("%s: response error during redirect: %v", url,
errors.Unfurl(err))
}

if response.StatusCode != body.Code {
t.Fatalf("%s: received non-%d status code: %d", url,
body.Code, response.StatusCode)
}
}
}

+ 104
- 0
tests/redirection_test.go View File

@@ -0,0 +1,104 @@
// +build integration

package tests

import (
"net/http"
"net/url"
"strings"
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/capstan/status"
"git.pluggableideas.com/destrealm/go/please"
"gitlab.com/destrealm/go/errors"
)

// ExternalRedirectController tests redirections, both local and external. In
// this case, we simply use MethodEndpointController:get.
type ExternalRedirectController struct {
capstan.BaseController
}

func (c *ExternalRedirectController) Get(ctx capstan.Context) error {
url := ctx.URLFor("MethodEndpointController:get")
url.External = true
err := status.Redirect(url.Encode(), 301)
return err
}

type InternalRedirectController struct {
capstan.BaseController
}

func (c *InternalRedirectController) Get(ctx capstan.Context) error {
return status.Internal("MethodEndpointController:get",
url.Values{"from": []string{"redirect"}})
}

func Test_Redirects(t *testing.T) {
server := NewServer(func(app capstan.Server) {

app.BindRoute(&ExternalRedirectController{
capstan.BaseController{
Path: "/redirect/external",
},
})
app.BindRoute(&InternalRedirectController{
capstan.BaseController{
Path: "/redirect/internal",
},
})

app.BindRoute(&MethodEndpointController{
capstan.BaseController{
Path: "/methods",
},
})
})
defer server.Close()

client := server.Client()
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
}

requester := please.MakeRequest(server.URL)
requester.SetClient(client)

req := requester.Get("/redirect/external")
response, err := req.Commit()
if err != nil {
t.Fatalf("response error during redirect: %v", errors.Unfurl(err))
}

if response.StatusCode != 301 {
t.Fatalf("received non-redirect status code %d", response.StatusCode)
}

if h := response.Headers().Get("location"); h == "" {
t.Fatal("expected location header missing")
} else {
if !strings.Contains(h, server.URL+"/methods") {
t.Fatalf("unexpected redirection path: %s", h)
}
}

req = requester.Get("/redirect/internal")
response, err = req.Commit()
if err != nil {
t.Fatalf("response error during redirect: %v", errors.Unfurl(err))
}

if response.StatusCode != 301 {
t.Fatalf("received non-redirect status code %d", response.StatusCode)
}

if h := response.Headers().Get("location"); h == "" {
t.Fatal("expected location header missing")
} else {
if !strings.Contains(h, "/methods") {
t.Fatalf("unexpected redirection path: %s", h)
}
}
}

+ 22
- 0
tests/return_types_test.go View File

@@ -0,0 +1,22 @@
// +build integration

package tests

import (
"testing"

"git.pluggableideas.com/destrealm/go/capstan"
)

func Test_ReturnTypes(t *testing.T) {
server := NewServer(func(app capstan.Server) {

app.BindRoute(&ReturnTypesController{
capstan.BaseController{
Path: "/return",
},
})

})
defer server.Close()
}

+ 46
- 354
tests/server.go View File

@@ -3,219 +3,14 @@
package tests

import (
"net/http"
"net/http/httptest"
"net/url"

"git.pluggableideas.com/destrealm/go/capstan"
"git.pluggableideas.com/destrealm/go/capstan/status"
"git.pluggableideas.com/destrealm/go/capstan/config"
)

type Dep struct {
Value1 string
Value2 int
}

type DependencyTestController struct {
capstan.BaseController
App capstan.Server `inject:"app"`
Dep1 *Dep `inject:""`
Dep2 *Dep `inject:"dep2"`
Dep3 *Dep `inject:"dep3"`
}

// FailDependencyTestController attempts to inject a dependency that doesn't
// exist. This should generate a panic when the endpoint is loaded.
type FailDependencyTestController struct {
capstan.BaseController
None *Dep `inject:"does-not-exist"`
}

type MethodEndpointController struct {
capstan.BaseController
}

func (m *MethodEndpointController) Get(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "GET")
ctx.Write([]byte("GET"))
return nil
}

func (m *MethodEndpointController) Post(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "POST")
ctx.Write([]byte("POST"))
return nil
}

func (m *MethodEndpointController) Put(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "PUT")
ctx.Write([]byte("PUT"))
return nil
}

func (m *MethodEndpointController) Patch(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "PATCH")
ctx.Write([]byte("PATCH"))
return nil
}

func (m *MethodEndpointController) Delete(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "DELETE")
ctx.Write([]byte("DELETE"))
return nil
}

func (m *MethodEndpointController) Head(ctx capstan.Context) error {
ctx.Response().Header().Add("X-Test-Method", "HEAD")
return nil
}

type IndexController struct {
capstan.BaseController
}

func (c *IndexController) Index(ctx capstan.Context) error {
_, err := ctx.Write([]byte("INDEX"))
return err
}

func (c *IndexController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte(ctx.Param("param")))
return err
}

type ParamsController struct {
capstan.BaseController
}

func (c *ParamsController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte(ctx.Param("from")))
return err
}

type CustomBaseRouteController struct {
capstan.BaseController
}

func (c *CustomBaseRouteController) Demo(ctx capstan.Context) error {
_, err := ctx.Write([]byte("DEMO"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute1(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-1"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute2N(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-N"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute3O(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-O"))
return err
}

func (c *CustomBaseRouteController) GetCustomRoute4S(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM-S"))
return err
}

func (c *CustomBaseRouteController) Get_Custom_Route_Underscore(ctx capstan.Context) error {
_, err := ctx.Write([]byte("CUSTOM_UNDERSCORE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse1A(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse2a(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse3Z(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse4z(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse5NN(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse6OO(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

func (c *CustomBaseRouteController) GetCustomBaseRouteAbuse7SS(ctx capstan.Context) error {
_, err := ctx.Write([]byte("ABUSE"))
return err
}

type MethodOverrideController struct {
capstan.BaseController
}

func (c *MethodOverrideController) Get(ctx capstan.Context) error {
_, err := ctx.Write([]byte("GET"))
return err
}

func (c *MethodOverrideController) Post(ctx capstan.Context) error {
_, err := ctx.Write([]byte("POST"))
return err
}

func (c *MethodOverrideController) Delete(ctx capstan.Context) error {
_, err := ctx.Write([]byte("DELETE"))
return err
}

func (c *MethodOverrideController) Put(ctx capstan.Context) error {
_, err := ctx.Write([]byte("PUT"))
return err
}

func (c *MethodOverrideController) Patch(ctx capstan.Context) error {
_, err := ctx.Write([]byte("PATCH"))
return err
}

func (c *MethodOverrideController) Head(ctx capstan.Context) error {
_, err := ctx.Write([]byte("HEAD"))
return err
}

// ExternalRedirectController tests redirections, both local and external. In
// this case, we simply use MethodEndpointController:get.
type ExternalRedirectController struct {
capstan.BaseController
}

func (c *ExternalRedirectController) Get(ctx capstan.Context) error {
url := ctx.URLFor("MethodEndpointController:get")
url.External = true
err := status.Redirect(url.Encode(), 301)
return err
}

type InternalRedirectController struct {
capstan.BaseController
}

func (c *InternalRedirectController) Get(ctx capstan.Context) error {
return status.Internal("MethodEndpointController:get",
url.Values{"from": []string{"redirect"}})
}

// ReturnTypesController tests various return types, including direct `error`
// types, capstan.Context as an error return type, etc.
@@ -223,153 +18,50 @@ type ReturnTypesController struct {
capstan.BaseController
}

func Server() *httptest.Server {
app := capstan.NewServer()

app.BindRoute(&MethodEndpointController{
capstan.BaseController{
Path: "/methods",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index",
Path: "/index/<param:string>/?",
Index: "/?",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-slash",
Path: "/index-slash/<param:string>/",
Index: "/+", // Implicit mandatory slashes don't work here.
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-no-slash",
Path: "/index-no-slash/<param:string>!",
Index: "!",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-no-slash-slash",
Path: "/index-no-slash-slash/<param:string>/!",
Index: "/!",
},
})

app.BindRoute(&IndexController{
capstan.BaseController{
Name: "index-sub-index",
Path: "/index-sub-index/<param:string>/!",
Index: "/index",
},
})

app.BindRoute(&ParamsController{
capstan.BaseController{
Path: "/params/<from:string>!",
},
})

// Used to test optional trailing slash with a default redirect preference
// terminating with a slash.
app.BindRoute(&ParamsController{
capstan.BaseController{
Name: "parameterized-redirects-slash",
Path: "/param-redirect-slash/<from:string>/?",
},
})

// Used to test optional trailing slash with a default redirect preference
// terminating without a slash.
app.BindRoute(&ParamsController{
capstan.BaseController{
Name: "parameterized-redirects-no-slash",
Path: "/param-redirect-no-slash/<from:string>?",
},
})

app.BindRoute(&CustomBaseRouteController{
capstan.BaseController{
Path: "/custom-base-route",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override",
Get: "/read",
Post: "/new",
Put: "/replace",
Patch: "/update",
Delete: "/remove",
Head: "/summary",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-no-slash",
Get: "/read/!",
Post: "/new/!",
Put: "/replace/!",
Patch: "/update/!",
Delete: "/remove/!",
Head: "/summary/!",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-slash",
Get: "/read/",
Post: "/new/",
Put: "/replace/",
Patch: "/update/",
Delete: "/remove/",
Head: "/summary/",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-optional",
Get: "/read/?",
Head: "/summary/?",
},
})

app.BindRoute(&MethodOverrideController{
capstan.BaseController{
Path: "/method-override-optional-none",
Get: "/read?",
Head: "/summary?",
},
})

app.BindRoute(&ExternalRedirectController{
capstan.BaseController{
Path: "/redirect/external",
},
})
app.BindRoute(&InternalRedirectController{
capstan.BaseController{
Path: "/redirect/internal",
},
})

app.BindRoute(&ReturnTypesController{
capstan.BaseController{
Path: "/return",
},
})
func NewServer(bind func(capstan.Server)) *httptest.Server {
app := capstan.NewServer(&capstan.ServerConfig{
Session: &capstan.SessionConfig{
Backend: config.SessionCookieBackend,
PlainText: true,
CookieOptions: &http.Cookie{
Name: "test_session",
},
// Key data from KeyStar crypto/init_test.go. Do not re-use; this is
// public.
Key: []byte{
0x45, 0x5c, 0x9a, 0x7c,
0xa1, 0x3e, 0x33, 0x70,
0x23, 0x35, 0x3b, 0x71,
0xed, 0xd3, 0xe0, 0x14,
0xf0, 0x8e, 0x45, 0x7f,
0xcf, 0x2b, 0x9f, 0xa6,
0x66, 0xbd, 0xc1, 0x43,
0xae, 0x08, 0x49, 0x4c,
},
// HMAC key from KeyStar crypto/init_test.go. Do not reuse; this is
// public.
HMACKey: []byte{
0x93, 0x31, 0x67, 0x11,
0xf4, 0xec, 0x18, 0x45,
0xd6, 0xdf, 0x5f, 0x86,
0x06, 0x74, 0x44, 0xf9,
0x3c, 0xe5, 0xc8, 0x16,
0x79, 0x2b, 0x46, 0xa6,
0x9b, 0x7d, 0x06, 0x66,
0x9d, 0xda, 0x90, 0xb9,
0x7e, 0x45, 0xfa, 0x7a,
0x69, 0xe8, 0x5c, 0x1a,
0xb3, 0xd4, 0xab, 0x66,
0x3e, 0x47, 0xc7, 0x02,
0x87, 0x79, 0x89, 0xcf,
0x2c, 0x21, 0x2f, 0xb2,
0x5e, 0xc9, 0xa9, 0x8c,
0xa9, 0xa8, 0xa8, 0xdd,
},
},
})

bind(app)

server := httptest.NewServer(app.Router().Mux())
parsed, _ := url.Parse(server.URL)


+ 33
- 0
tests/special_endpoints_test.go View File

@@ -0,0 +1,33 @@
// +build integration

package tests

import (
"testing"
)

// Test_SpecialEndpoints tests specially named endpoints:
//
// - Index
func Test_SpecialEndpoints(t *testing.T) {

}

// Test_WebSocket tests the websocket endpoint.
func Test_WebSocketEndpoint(t *testing.T) {

}

// Test_NamedEndpoints tests named suffix endpoints. These endpoints contain a
// camel-to-hyphen-case converted endpoint with the method as its suffix (plus a
// single character modifier indicating the trailing slash state). As an
// example, a controller endpoint named ListEmailsGetN would be translated to
// the endpoint name "list-emails" using the GET method and no trailing slash
// ("N" suffix).
//
// This unit test tests naming and most common methods, plus the suffix
// characters N (no trailing slash), O (optional trailing slash), and S
// (mandatory trailing slash).
func Test_NamedEndpoints(t *testing.T) {

}

+ 26
- 0
tests/types.go View File

@@ -0,0 +1,26 @@
package tests

import (
"git.pluggableideas.com/destrealm/go/please"
)

type Dep struct {
Value1 string
Value2 int
}

type testMethods struct {
HasBody bool
Method func(string) *please.ClientRequest
}

type testBody struct {
Code int
Body []byte
}

type testBodyMethod struct {
Code int
Body []byte
Method string
}

Loading…
Cancel
Save