Added server tests
This commit is contained in:
parent
245a085020
commit
805f92468a
@ -15,7 +15,7 @@ func TestBytes(t *testing.T) {
|
|||||||
return ctx.Bytes([]byte("Hello"))
|
return ctx.Bytes([]byte("Hello"))
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "Hello")
|
assert.Equal(t, string(response.Body()), "Hello")
|
||||||
}
|
}
|
||||||
@ -27,7 +27,7 @@ func TestString(t *testing.T) {
|
|||||||
return ctx.String("Hello")
|
return ctx.String("Hello")
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "Hello")
|
assert.Equal(t, string(response.Body()), "Hello")
|
||||||
}
|
}
|
||||||
@ -39,7 +39,7 @@ func TestError(t *testing.T) {
|
|||||||
return ctx.Status(401).Error("Not logged in")
|
return ctx.Status(401).Error("Not logged in")
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 401)
|
assert.Equal(t, response.Status(), 401)
|
||||||
assert.Equal(t, string(response.Body()), "")
|
assert.Equal(t, string(response.Body()), "")
|
||||||
}
|
}
|
||||||
@ -51,7 +51,7 @@ func TestErrorMultiple(t *testing.T) {
|
|||||||
return ctx.Status(401).Error("Not logged in", errors.New("Missing auth token"))
|
return ctx.Status(401).Error("Not logged in", errors.New("Missing auth token"))
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 401)
|
assert.Equal(t, response.Status(), 401)
|
||||||
assert.Equal(t, string(response.Body()), "")
|
assert.Equal(t, string(response.Body()), "")
|
||||||
}
|
}
|
||||||
|
7
Header.go
Normal file
7
Header.go
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
package web
|
||||||
|
|
||||||
|
// Header is used to store HTTP headers.
|
||||||
|
type Header struct {
|
||||||
|
Key string
|
||||||
|
Value string
|
||||||
|
}
|
16
README.md
16
README.md
@ -40,12 +40,22 @@ s.Run(":8080")
|
|||||||
## Tests
|
## Tests
|
||||||
|
|
||||||
```
|
```
|
||||||
PASS: TestRouter
|
PASS: TestBytes
|
||||||
PASS: TestMiddleware
|
PASS: TestString
|
||||||
|
PASS: TestError
|
||||||
|
PASS: TestErrorMultiple
|
||||||
|
PASS: TestRequest
|
||||||
|
PASS: TestRequestHeader
|
||||||
|
PASS: TestRequestParam
|
||||||
|
PASS: TestWrite
|
||||||
|
PASS: TestWriteString
|
||||||
|
PASS: TestResponseCompression
|
||||||
|
PASS: TestResponseHeader
|
||||||
|
PASS: TestResponseHeaderOverwrite
|
||||||
PASS: TestPanic
|
PASS: TestPanic
|
||||||
PASS: TestRun
|
PASS: TestRun
|
||||||
PASS: TestUnavailablePort
|
PASS: TestUnavailablePort
|
||||||
coverage: 100.0% of statements
|
coverage: 95.5% of statements
|
||||||
```
|
```
|
||||||
|
|
||||||
## Benchmarks
|
## Benchmarks
|
||||||
|
@ -24,7 +24,7 @@ type request struct {
|
|||||||
method string
|
method string
|
||||||
path string
|
path string
|
||||||
query string
|
query string
|
||||||
headers []header
|
headers []Header
|
||||||
body []byte
|
body []byte
|
||||||
params []router.Parameter
|
params []router.Parameter
|
||||||
}
|
}
|
||||||
|
@ -20,20 +20,35 @@ func TestRequest(t *testing.T) {
|
|||||||
return ctx.String(fmt.Sprintf("%s %s %s %s", method, scheme, host, path))
|
return ctx.String(fmt.Sprintf("%s %s %s %s", method, scheme, host, path))
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "http://example.com/request?x=1", nil)
|
response := s.Request("GET", "http://example.com/request?x=1", []web.Header{{"Accept", "*/*"}}, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "GET http example.com /request")
|
assert.Equal(t, string(response.Body()), "GET http example.com /request")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func TestRequestHeader(t *testing.T) {
|
||||||
|
s := web.NewServer()
|
||||||
|
|
||||||
|
s.Get("/", func(ctx web.Context) error {
|
||||||
|
accept := ctx.Request().Header("Accept")
|
||||||
|
empty := ctx.Request().Header("")
|
||||||
|
return ctx.String(accept + empty)
|
||||||
|
})
|
||||||
|
|
||||||
|
response := s.Request("GET", "/", []web.Header{{"Accept", "*/*"}}, nil)
|
||||||
|
assert.Equal(t, response.Status(), 200)
|
||||||
|
assert.Equal(t, string(response.Body()), "*/*")
|
||||||
|
}
|
||||||
|
|
||||||
func TestRequestParam(t *testing.T) {
|
func TestRequestParam(t *testing.T) {
|
||||||
s := web.NewServer()
|
s := web.NewServer()
|
||||||
|
|
||||||
s.Get("/blog/:article", func(ctx web.Context) error {
|
s.Get("/blog/:article", func(ctx web.Context) error {
|
||||||
article := ctx.Request().Param("article")
|
article := ctx.Request().Param("article")
|
||||||
return ctx.String(article)
|
empty := ctx.Request().Param("")
|
||||||
|
return ctx.String(article + empty)
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/blog/my-article", nil)
|
response := s.Request("GET", "/blog/my-article", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "my-article")
|
assert.Equal(t, string(response.Body()), "my-article")
|
||||||
}
|
}
|
||||||
|
@ -20,7 +20,7 @@ type Response interface {
|
|||||||
// response represents the HTTP response used in the given context.
|
// response represents the HTTP response used in the given context.
|
||||||
type response struct {
|
type response struct {
|
||||||
body []byte
|
body []byte
|
||||||
headers []header
|
headers []Header
|
||||||
status uint16
|
status uint16
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -49,7 +49,7 @@ func (res *response) SetHeader(key string, value string) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
res.headers = append(res.headers, header{Key: key, Value: value})
|
res.headers = append(res.headers, Header{Key: key, Value: value})
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetBody replaces the response body with the new contents.
|
// SetBody replaces the response body with the new contents.
|
||||||
|
@ -18,7 +18,7 @@ func TestWrite(t *testing.T) {
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "Hello")
|
assert.Equal(t, string(response.Body()), "Hello")
|
||||||
}
|
}
|
||||||
@ -31,7 +31,7 @@ func TestWriteString(t *testing.T) {
|
|||||||
return err
|
return err
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, string(response.Body()), "Hello")
|
assert.Equal(t, string(response.Body()), "Hello")
|
||||||
}
|
}
|
||||||
@ -41,20 +41,22 @@ func TestResponseCompression(t *testing.T) {
|
|||||||
uncompressed := bytes.Repeat([]byte("This text should be compressed to a size smaller than the original."), 5)
|
uncompressed := bytes.Repeat([]byte("This text should be compressed to a size smaller than the original."), 5)
|
||||||
|
|
||||||
s.Use(func(ctx web.Context) error {
|
s.Use(func(ctx web.Context) error {
|
||||||
err := ctx.Next()
|
defer func() {
|
||||||
body := ctx.Response().Body()
|
body := ctx.Response().Body()
|
||||||
ctx.Response().SetBody(nil)
|
ctx.Response().SetBody(nil)
|
||||||
zip := gzip.NewWriter(ctx.Response())
|
zip := gzip.NewWriter(ctx.Response())
|
||||||
zip.Write(body)
|
zip.Write(body)
|
||||||
zip.Close()
|
zip.Close()
|
||||||
return err
|
}()
|
||||||
|
|
||||||
|
return ctx.Next()
|
||||||
})
|
})
|
||||||
|
|
||||||
s.Get("/", func(ctx web.Context) error {
|
s.Get("/", func(ctx web.Context) error {
|
||||||
return ctx.Bytes(uncompressed)
|
return ctx.Bytes(uncompressed)
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.True(t, len(response.Body()) < len(uncompressed))
|
assert.True(t, len(response.Body()) < len(uncompressed))
|
||||||
|
|
||||||
@ -75,7 +77,7 @@ func TestResponseHeader(t *testing.T) {
|
|||||||
return ctx.String(contentType)
|
return ctx.String(contentType)
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, response.Header("Content-Type"), "text/plain")
|
assert.Equal(t, response.Header("Content-Type"), "text/plain")
|
||||||
assert.Equal(t, response.Header("Non existent header"), "")
|
assert.Equal(t, response.Header("Non existent header"), "")
|
||||||
@ -91,7 +93,7 @@ func TestResponseHeaderOverwrite(t *testing.T) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
response := s.Request("GET", "/", nil)
|
response := s.Request("GET", "/", nil, nil)
|
||||||
assert.Equal(t, response.Status(), 200)
|
assert.Equal(t, response.Status(), 200)
|
||||||
assert.Equal(t, response.Header("Content-Type"), "text/html")
|
assert.Equal(t, response.Header("Content-Type"), "text/html")
|
||||||
assert.Equal(t, string(response.Body()), "")
|
assert.Equal(t, string(response.Body()), "")
|
||||||
|
11
Server.go
11
Server.go
@ -18,7 +18,7 @@ import (
|
|||||||
// Server is the interface for an HTTP server.
|
// Server is the interface for an HTTP server.
|
||||||
type Server interface {
|
type Server interface {
|
||||||
Get(path string, handler Handler)
|
Get(path string, handler Handler)
|
||||||
Request(method string, path string, body io.Reader) Response
|
Request(method string, path string, headers []Header, body io.Reader) Response
|
||||||
Router() *router.Router[Handler]
|
Router() *router.Router[Handler]
|
||||||
Run(address string) error
|
Run(address string) error
|
||||||
Use(handlers ...Handler)
|
Use(handlers ...Handler)
|
||||||
@ -67,8 +67,9 @@ func (s *server) Get(path string, handler Handler) {
|
|||||||
// Request performs a synthetic request and returns the response.
|
// Request performs a synthetic request and returns the response.
|
||||||
// This function keeps the response in memory so it's slightly slower than a real request.
|
// This function keeps the response in memory so it's slightly slower than a real request.
|
||||||
// However it is very useful inside tests where you don't want to spin up a real web server.
|
// However it is very useful inside tests where you don't want to spin up a real web server.
|
||||||
func (s *server) Request(method string, url string, body io.Reader) Response {
|
func (s *server) Request(method string, url string, headers []Header, body io.Reader) Response {
|
||||||
ctx := s.newContext()
|
ctx := s.newContext()
|
||||||
|
ctx.request.headers = headers
|
||||||
s.handleRequest(ctx, method, url, io.Discard)
|
s.handleRequest(ctx, method, url, io.Discard)
|
||||||
return ctx.Response()
|
return ctx.Response()
|
||||||
}
|
}
|
||||||
@ -178,7 +179,7 @@ func (s *server) handleConnection(conn net.Conn) {
|
|||||||
key := message[:colon]
|
key := message[:colon]
|
||||||
value := message[colon+2 : len(message)-2]
|
value := message[colon+2 : len(message)-2]
|
||||||
|
|
||||||
ctx.request.headers = append(ctx.request.headers, header{
|
ctx.request.headers = append(ctx.request.headers, Header{
|
||||||
Key: key,
|
Key: key,
|
||||||
Value: value,
|
Value: value,
|
||||||
})
|
})
|
||||||
@ -219,12 +220,12 @@ func (s *server) newContext() *context {
|
|||||||
request: request{
|
request: request{
|
||||||
reader: bufio.NewReader(nil),
|
reader: bufio.NewReader(nil),
|
||||||
body: make([]byte, 0),
|
body: make([]byte, 0),
|
||||||
headers: make([]header, 0, 8),
|
headers: make([]Header, 0, 8),
|
||||||
params: make([]router.Parameter, 0, 8),
|
params: make([]router.Parameter, 0, 8),
|
||||||
},
|
},
|
||||||
response: response{
|
response: response{
|
||||||
body: make([]byte, 0, 1024),
|
body: make([]byte, 0, 1024),
|
||||||
headers: make([]header, 0, 8),
|
headers: make([]Header, 0, 8),
|
||||||
status: 200,
|
status: 200,
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
51
Server_test.go
Normal file
51
Server_test.go
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
package web_test
|
||||||
|
|
||||||
|
import (
|
||||||
|
"net"
|
||||||
|
"net/http"
|
||||||
|
"syscall"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"git.akyoto.dev/go/assert"
|
||||||
|
"git.akyoto.dev/go/web"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestPanic(t *testing.T) {
|
||||||
|
s := web.NewServer()
|
||||||
|
|
||||||
|
s.Get("/panic", func(ctx web.Context) error {
|
||||||
|
panic("Something unbelievable happened")
|
||||||
|
})
|
||||||
|
|
||||||
|
defer func() {
|
||||||
|
r := recover()
|
||||||
|
|
||||||
|
if r == nil {
|
||||||
|
t.Error("Didn't panic")
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.Request("GET", "/panic", nil, nil)
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestRun(t *testing.T) {
|
||||||
|
s := web.NewServer()
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
_, err := http.Get("http://127.0.0.1:8080/")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
err = syscall.Kill(syscall.Getpid(), syscall.SIGTERM)
|
||||||
|
assert.Nil(t, err)
|
||||||
|
}()
|
||||||
|
|
||||||
|
s.Run(":8080")
|
||||||
|
}
|
||||||
|
|
||||||
|
func TestUnavailablePort(t *testing.T) {
|
||||||
|
listener, err := net.Listen("tcp", ":8080")
|
||||||
|
assert.Nil(t, err)
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
s := web.NewServer()
|
||||||
|
s.Run(":8080")
|
||||||
|
}
|
6
http.go
6
http.go
@ -2,12 +2,6 @@ package web
|
|||||||
|
|
||||||
import "strings"
|
import "strings"
|
||||||
|
|
||||||
// header is used to store HTTP headers.
|
|
||||||
type header struct {
|
|
||||||
Key string
|
|
||||||
Value string
|
|
||||||
}
|
|
||||||
|
|
||||||
// isRequestMethod returns true if the given string is a valid HTTP request method.
|
// isRequestMethod returns true if the given string is a valid HTTP request method.
|
||||||
func isRequestMethod(method string) bool {
|
func isRequestMethod(method string) bool {
|
||||||
switch method {
|
switch method {
|
||||||
|
Loading…
Reference in New Issue
Block a user