2024-03-22 14:08:24 +00:00
|
|
|
package web_test
|
2023-07-18 16:02:57 +00:00
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
2023-07-22 10:32:52 +00:00
|
|
|
"fmt"
|
2023-07-18 16:02:57 +00:00
|
|
|
"io"
|
2024-03-12 21:31:45 +00:00
|
|
|
"net"
|
2023-07-18 16:02:57 +00:00
|
|
|
"net/http"
|
|
|
|
"net/http/httptest"
|
2023-07-19 11:01:51 +00:00
|
|
|
"strings"
|
2024-03-12 21:31:45 +00:00
|
|
|
"syscall"
|
2023-07-18 16:02:57 +00:00
|
|
|
"testing"
|
|
|
|
|
|
|
|
"git.akyoto.dev/go/assert"
|
2024-03-22 14:08:24 +00:00
|
|
|
"git.akyoto.dev/go/web"
|
2023-07-18 16:02:57 +00:00
|
|
|
)
|
|
|
|
|
2023-07-19 10:31:33 +00:00
|
|
|
func TestRouter(t *testing.T) {
|
2024-03-22 14:08:24 +00:00
|
|
|
s := web.NewServer()
|
2023-07-18 16:02:57 +00:00
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/", func(ctx web.Context) error {
|
2023-07-18 16:02:57 +00:00
|
|
|
return ctx.Bytes([]byte("Hello"))
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/string", func(ctx web.Context) error {
|
2023-07-22 09:48:35 +00:00
|
|
|
return ctx.String("Hello")
|
2023-07-18 16:02:57 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/write", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
_, err := ctx.Response().Write([]byte("Hello"))
|
2024-03-14 11:52:03 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/writestring", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
_, err := io.WriteString(ctx.Response(), "Hello")
|
2024-03-13 21:49:57 +00:00
|
|
|
return err
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/error", func(ctx web.Context) error {
|
2024-03-13 15:57:36 +00:00
|
|
|
return ctx.Status(http.StatusUnauthorized).Error("Not logged in")
|
2023-07-21 21:23:49 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/error2", func(ctx web.Context) error {
|
2024-03-13 15:57:36 +00:00
|
|
|
return ctx.Status(http.StatusUnauthorized).Error("Not logged in", errors.New("Missing auth token"))
|
2023-07-18 16:02:57 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/reader", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
return ctx.Copy(strings.NewReader("Hello"))
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/file", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
return ctx.File("testdata/file.txt")
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/flush", func(ctx web.Context) error {
|
2024-03-22 13:37:21 +00:00
|
|
|
ctx.Response().WriteString("Hello 1\n")
|
|
|
|
ctx.Response().WriteString("Hello 2\n")
|
|
|
|
ctx.Response().Flush()
|
|
|
|
return nil
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/echo", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
return ctx.Copy(ctx.Request())
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/context", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
return ctx.Request().Context().Err()
|
2023-07-19 11:01:51 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/redirect", func(ctx web.Context) error {
|
2024-03-20 20:17:50 +00:00
|
|
|
return ctx.Redirect(http.StatusTemporaryRedirect, "/")
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/request/data", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
request := ctx.Request()
|
|
|
|
method := request.Method()
|
|
|
|
protocol := request.Protocol()
|
|
|
|
host := request.Host()
|
|
|
|
path := request.Path()
|
2023-07-22 10:32:52 +00:00
|
|
|
return ctx.String(fmt.Sprintf("%s %s %s %s", method, protocol, host, path))
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/request/header", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
acceptEncoding := ctx.Request().Header("Accept-Encoding")
|
2023-07-22 10:32:52 +00:00
|
|
|
return ctx.String(acceptEncoding)
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/response/header", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
ctx.Response().SetHeader("Content-Type", "text/plain")
|
|
|
|
contentType := ctx.Response().Header("Content-Type")
|
2023-07-22 10:32:52 +00:00
|
|
|
return ctx.String(contentType)
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/blog/:article", func(ctx web.Context) error {
|
2023-07-22 09:48:35 +00:00
|
|
|
article := ctx.Get("article")
|
|
|
|
return ctx.String(article)
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/missing-parameter", func(ctx web.Context) error {
|
2023-07-22 09:48:35 +00:00
|
|
|
missing := ctx.Get("missing")
|
|
|
|
return ctx.String(missing)
|
2023-07-19 11:01:51 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Get("/scheme", func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
return ctx.String(ctx.Request().Scheme())
|
2024-03-14 11:52:03 +00:00
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Post("/", func(ctx web.Context) error {
|
2023-07-22 09:36:28 +00:00
|
|
|
return ctx.String("Post")
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Delete("/", func(ctx web.Context) error {
|
2023-07-22 09:36:28 +00:00
|
|
|
return ctx.String("Delete")
|
|
|
|
})
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Put("/", func(ctx web.Context) error {
|
2023-07-22 09:36:28 +00:00
|
|
|
return ctx.String("Put")
|
|
|
|
})
|
|
|
|
|
2023-07-18 16:02:57 +00:00
|
|
|
tests := []struct {
|
2024-03-16 14:22:47 +00:00
|
|
|
Method string
|
|
|
|
URL string
|
|
|
|
Body string
|
|
|
|
Status int
|
|
|
|
Response string
|
2023-07-18 16:02:57 +00:00
|
|
|
}{
|
2024-03-16 14:22:47 +00:00
|
|
|
{Method: "GET", URL: "/", Body: "", Status: http.StatusOK, Response: "Hello"},
|
|
|
|
{Method: "GET", URL: "/context", Body: "", Status: http.StatusOK, Response: ""},
|
|
|
|
{Method: "GET", URL: "/echo", Body: "Echo", Status: http.StatusOK, Response: "Echo"},
|
|
|
|
{Method: "GET", URL: "/error", Body: "", Status: http.StatusUnauthorized, Response: "Not logged in"},
|
|
|
|
{Method: "GET", URL: "/error2", Body: "", Status: http.StatusUnauthorized, Response: "Not logged in\nMissing auth token"},
|
|
|
|
{Method: "GET", URL: "/file", Body: "", Status: http.StatusOK, Response: "Hello File"},
|
2024-03-22 13:37:21 +00:00
|
|
|
{Method: "GET", URL: "/flush", Body: "", Status: http.StatusOK, Response: "Hello 1\nHello 2\n"},
|
2024-03-16 14:22:47 +00:00
|
|
|
{Method: "GET", URL: "/not-found", Body: "", Status: http.StatusNotFound, Response: http.StatusText(http.StatusNotFound)},
|
|
|
|
{Method: "GET", URL: "/request/data", Body: "", Status: http.StatusOK, Response: "GET HTTP/1.1 example.com /request/data"},
|
|
|
|
{Method: "GET", URL: "/request/header", Body: "", Status: http.StatusOK, Response: ""},
|
|
|
|
{Method: "GET", URL: "/response/header", Body: "", Status: http.StatusOK, Response: "text/plain"},
|
|
|
|
{Method: "GET", URL: "/reader", Body: "", Status: http.StatusOK, Response: "Hello"},
|
2024-03-20 20:17:50 +00:00
|
|
|
{Method: "GET", URL: "/redirect", Body: "", Status: http.StatusTemporaryRedirect, Response: ""},
|
2024-03-16 14:22:47 +00:00
|
|
|
{Method: "GET", URL: "/string", Body: "", Status: http.StatusOK, Response: "Hello"},
|
|
|
|
{Method: "GET", URL: "/scheme", Body: "", Status: http.StatusOK, Response: "http"},
|
|
|
|
{Method: "GET", URL: "/write", Body: "", Status: http.StatusOK, Response: "Hello"},
|
|
|
|
{Method: "GET", URL: "/writestring", Body: "", Status: http.StatusOK, Response: "Hello"},
|
|
|
|
{Method: "GET", URL: "/blog/testing-my-router", Body: "", Status: http.StatusOK, Response: "testing-my-router"},
|
|
|
|
{Method: "GET", URL: "/missing-parameter", Body: "", Status: http.StatusOK, Response: ""},
|
|
|
|
{Method: "POST", URL: "/", Body: "", Status: http.StatusOK, Response: "Post"},
|
|
|
|
{Method: "DELETE", URL: "/", Body: "", Status: http.StatusOK, Response: "Delete"},
|
|
|
|
{Method: "PUT", URL: "/", Body: "", Status: http.StatusOK, Response: "Put"},
|
2023-07-18 16:02:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for _, test := range tests {
|
|
|
|
t.Run("example.com"+test.URL, func(t *testing.T) {
|
2024-03-16 14:22:47 +00:00
|
|
|
request := httptest.NewRequest(test.Method, "http://example.com"+test.URL, strings.NewReader(test.Body))
|
2023-07-18 16:02:57 +00:00
|
|
|
response := httptest.NewRecorder()
|
2023-07-18 19:48:38 +00:00
|
|
|
s.ServeHTTP(response, request)
|
2023-07-18 16:02:57 +00:00
|
|
|
|
|
|
|
result := response.Result()
|
|
|
|
assert.Equal(t, result.StatusCode, test.Status)
|
|
|
|
|
|
|
|
body, err := io.ReadAll(result.Body)
|
|
|
|
assert.Nil(t, err)
|
2024-03-16 14:22:47 +00:00
|
|
|
assert.Equal(t, string(body), test.Response)
|
2023-07-18 16:02:57 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
2023-07-19 10:31:33 +00:00
|
|
|
|
2024-03-13 19:18:01 +00:00
|
|
|
func TestMiddleware(t *testing.T) {
|
2024-03-22 14:08:24 +00:00
|
|
|
s := web.NewServer()
|
2024-03-13 19:18:01 +00:00
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Use(func(ctx web.Context) error {
|
2024-03-16 14:22:47 +00:00
|
|
|
ctx.Response().SetHeader("Middleware", "true")
|
2024-03-13 19:18:01 +00:00
|
|
|
return ctx.Next()
|
|
|
|
})
|
|
|
|
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
|
|
|
response := httptest.NewRecorder()
|
|
|
|
s.ServeHTTP(response, request)
|
|
|
|
|
|
|
|
assert.Equal(t, response.Header().Get("Middleware"), "true")
|
|
|
|
}
|
|
|
|
|
2023-07-19 10:31:33 +00:00
|
|
|
func TestPanic(t *testing.T) {
|
2024-03-22 14:08:24 +00:00
|
|
|
s := web.NewServer()
|
2023-07-19 10:31:33 +00:00
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s.Router().Add(http.MethodGet, "/panic", func(ctx web.Context) error {
|
2023-07-19 10:31:33 +00:00
|
|
|
panic("Something unbelievable happened")
|
|
|
|
})
|
|
|
|
|
|
|
|
t.Run("example.com/panic", func(t *testing.T) {
|
|
|
|
defer func() {
|
|
|
|
r := recover()
|
|
|
|
|
|
|
|
if r == nil {
|
|
|
|
t.Error("Didn't panic")
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
|
|
|
|
request := httptest.NewRequest(http.MethodGet, "/panic", nil)
|
|
|
|
response := httptest.NewRecorder()
|
|
|
|
s.ServeHTTP(response, request)
|
|
|
|
})
|
|
|
|
}
|
2024-03-12 21:31:45 +00:00
|
|
|
|
|
|
|
func TestRun(t *testing.T) {
|
2024-03-22 14:08:24 +00:00
|
|
|
s := web.NewServer()
|
2024-03-12 21:31:45 +00:00
|
|
|
|
|
|
|
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()
|
|
|
|
|
2024-03-22 14:08:24 +00:00
|
|
|
s := web.NewServer()
|
2024-03-12 21:31:45 +00:00
|
|
|
s.Run(":8080")
|
|
|
|
}
|