mirror of
https://github.com/stefanprodan/podinfo.git
synced 2026-05-23 09:52:46 +00:00
Merge pull request #486 from stefanprodan/test-http-api
Improve test coverage of the HTTP API
This commit is contained in:
@@ -22,19 +22,19 @@ type TokenServer struct {
|
||||
|
||||
type jwtCustomClaims struct {
|
||||
Name string `json:"name"`
|
||||
jwt.StandardClaims
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
func (s *TokenServer) TokenGenerate(ctx context.Context, req *pb.TokenRequest) (*pb.TokenResponse, error) {
|
||||
|
||||
user := "anonymous"
|
||||
expiresAt := time.Now().Add(time.Minute * 1).Unix()
|
||||
expiresAt := time.Now().Add(time.Minute * 1)
|
||||
|
||||
claims := &jwtCustomClaims{
|
||||
user,
|
||||
jwt.StandardClaims{
|
||||
jwt.RegisteredClaims{
|
||||
Issuer: "podinfo",
|
||||
ExpiresAt: expiresAt,
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -48,7 +48,7 @@ func (s *TokenServer) TokenGenerate(ctx context.Context, req *pb.TokenRequest) (
|
||||
|
||||
var result = pb.TokenResponse{
|
||||
Token: t,
|
||||
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0).String(),
|
||||
ExpiresAt: claims.ExpiresAt.Time.String(),
|
||||
Message: "Token generated successfully",
|
||||
}
|
||||
|
||||
@@ -88,12 +88,12 @@ func (s *TokenServer) TokenValidate(ctx context.Context, req *pb.TokenRequest) (
|
||||
}
|
||||
|
||||
if parsed_token.Valid {
|
||||
if claims.StandardClaims.Issuer != "podinfo" {
|
||||
if claims.Issuer != "podinfo" {
|
||||
return nil, status.Errorf(codes.OK, "Invalid issuer")
|
||||
} else {
|
||||
var result = pb.TokenResponse{
|
||||
Token: claims.Name,
|
||||
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0).String(),
|
||||
ExpiresAt: claims.ExpiresAt.Time.String(),
|
||||
}
|
||||
return &result, nil
|
||||
}
|
||||
|
||||
50
pkg/api/http/cache_test.go
Normal file
50
pkg/api/http/cache_test.go
Normal file
@@ -0,0 +1,50 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCacheWriteHandler_NoPool(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.pool = nil
|
||||
srv.router.HandleFunc("/cache/{key}", srv.cacheWriteHandler)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/cache/key1", strings.NewReader("value1"))
|
||||
rr := httptest.NewRecorder()
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "cache server is offline") {
|
||||
t.Errorf("expected offline error, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheDeleteHandler_NoPool(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.pool = nil
|
||||
srv.router.HandleFunc("/cache/{key}", srv.cacheDeleteHandler).Methods("DELETE")
|
||||
|
||||
req, _ := http.NewRequest("DELETE", "/cache/key1", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "cache server is offline") {
|
||||
t.Errorf("expected offline error, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheReadHandler_NoPool(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.pool = nil
|
||||
srv.router.HandleFunc("/cache/{key}", srv.cacheReadHandler).Methods("GET")
|
||||
|
||||
req, _ := http.NewRequest("GET", "/cache/key1", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "cache server is offline") {
|
||||
t.Errorf("expected offline error, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
22
pkg/api/http/configs_test.go
Normal file
22
pkg/api/http/configs_test.go
Normal file
@@ -0,0 +1,22 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestConfigReadHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
req, _ := http.NewRequest("GET", "/configs", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.configReadHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "{}") {
|
||||
t.Errorf("expected empty JSON object, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
@@ -33,3 +33,66 @@ func TestDelayHandler(t *testing.T) {
|
||||
rr.Body.String(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomDelayMiddleware(t *testing.T) {
|
||||
m := NewRandomDelayMiddleware(0, 1, "ms")
|
||||
called := false
|
||||
handler := m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
called = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
if !called {
|
||||
t.Error("next handler was not called")
|
||||
}
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomDelayMiddleware_Milliseconds(t *testing.T) {
|
||||
m := NewRandomDelayMiddleware(0, 1, "ms")
|
||||
handler := m.Handler(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Errorf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestRandomErrorMiddleware(t *testing.T) {
|
||||
handler := randomErrorMiddleware(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
gotOK := false
|
||||
gotError := false
|
||||
for i := 0; i < 50; i++ {
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
if rr.Code == http.StatusOK {
|
||||
gotOK = true
|
||||
} else {
|
||||
gotError = true
|
||||
}
|
||||
if gotOK && gotError {
|
||||
break
|
||||
}
|
||||
}
|
||||
if !gotOK {
|
||||
t.Error("never got a successful response")
|
||||
}
|
||||
if !gotError {
|
||||
t.Error("never got an error response")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -74,3 +74,112 @@ func TestEchoHandler_ContentType(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestCopyTracingHeaders(t *testing.T) {
|
||||
from, _ := http.NewRequest("GET", "/", nil)
|
||||
from.Header.Set("x-request-id", "abc123")
|
||||
from.Header.Set("x-b3-traceid", "trace-1")
|
||||
from.Header.Set("x-b3-spanid", "span-1")
|
||||
|
||||
to, _ := http.NewRequest("POST", "/echo", nil)
|
||||
copyTracingHeaders(from, to)
|
||||
|
||||
if to.Header.Get("x-request-id") != "abc123" {
|
||||
t.Errorf("x-request-id not copied")
|
||||
}
|
||||
if to.Header.Get("x-b3-traceid") != "trace-1" {
|
||||
t.Errorf("x-b3-traceid not copied")
|
||||
}
|
||||
if to.Header.Get("x-b3-spanid") != "span-1" {
|
||||
t.Errorf("x-b3-spanid not copied")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoHandler_WithBackend(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusOK)
|
||||
w.Write([]byte(`"backend response"`))
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
srv := NewMockServer()
|
||||
srv.config.BackendURL = []string{backend.URL}
|
||||
|
||||
req, _ := http.NewRequest("POST", "/echo", strings.NewReader("hello"))
|
||||
req.Header.Set("x-request-id", "test-123")
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.echoHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "backend response") {
|
||||
t.Errorf("expected backend response in body, got: %s", rr.Body.String())
|
||||
}
|
||||
if rr.Header().Get("X-Color") != "blue" {
|
||||
t.Errorf("X-Color = %q, want %q", rr.Header().Get("X-Color"), "blue")
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoHandler_BackendError(t *testing.T) {
|
||||
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
}))
|
||||
defer backend.Close()
|
||||
|
||||
srv := NewMockServer()
|
||||
srv.config.BackendURL = []string{backend.URL}
|
||||
|
||||
req, _ := http.NewRequest("POST", "/echo", strings.NewReader("hello"))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.echoHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "response status code 500") {
|
||||
t.Errorf("expected error message in body, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoHandler_BackendUnreachable(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.BackendURL = []string{"http://127.0.0.1:1"}
|
||||
|
||||
req, _ := http.NewRequest("POST", "/echo", strings.NewReader("hello"))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.echoHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "call failed") {
|
||||
t.Errorf("expected call failed in body, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestEchoHandler_MultipleBackends(t *testing.T) {
|
||||
backend1 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`"resp1"`))
|
||||
}))
|
||||
defer backend1.Close()
|
||||
|
||||
backend2 := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
w.Write([]byte(`"resp2"`))
|
||||
}))
|
||||
defer backend2.Close()
|
||||
|
||||
srv := NewMockServer()
|
||||
srv.config.BackendURL = []string{backend1.URL, backend2.URL}
|
||||
|
||||
req, _ := http.NewRequest("POST", "/echo", strings.NewReader("hello"))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.echoHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "resp1") || !strings.Contains(rr.Body.String(), "resp2") {
|
||||
t.Errorf("expected both backend responses, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
36
pkg/api/http/echows_test.go
Normal file
36
pkg/api/http/echows_test.go
Normal file
@@ -0,0 +1,36 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/gorilla/websocket"
|
||||
)
|
||||
|
||||
func TestEchoWsHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.router.HandleFunc("/ws/echo", srv.echoWsHandler)
|
||||
server := httptest.NewServer(srv.router)
|
||||
defer server.Close()
|
||||
|
||||
wsURL := "ws" + strings.TrimPrefix(server.URL, "http") + "/ws/echo"
|
||||
ws, _, err := websocket.DefaultDialer.Dial(wsURL, nil)
|
||||
if err != nil {
|
||||
t.Fatalf("websocket dial failed: %v", err)
|
||||
}
|
||||
defer ws.Close()
|
||||
|
||||
msg := "hello websocket"
|
||||
if err := ws.WriteMessage(websocket.TextMessage, []byte(msg)); err != nil {
|
||||
t.Fatalf("write failed: %v", err)
|
||||
}
|
||||
|
||||
_, p, err := ws.ReadMessage()
|
||||
if err != nil {
|
||||
t.Fatalf("read failed: %v", err)
|
||||
}
|
||||
if len(p) == 0 {
|
||||
t.Error("received empty message")
|
||||
}
|
||||
}
|
||||
@@ -33,3 +33,17 @@ func TestEnvHandler(t *testing.T) {
|
||||
rr.Body.String(), expected)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnvHandler_Actual(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
req, _ := http.NewRequest("GET", "/env", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.envHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if rr.Header().Get("Content-Type") != "application/json; charset=utf-8" {
|
||||
t.Errorf("Content-Type = %q, want application/json", rr.Header().Get("Content-Type"))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package http
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
"testing"
|
||||
)
|
||||
|
||||
@@ -43,3 +45,67 @@ func TestReadyzHandler(t *testing.T) {
|
||||
status, http.StatusServiceUnavailable)
|
||||
}
|
||||
}
|
||||
|
||||
func TestHealthzHandler_Healthy(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
atomic.StoreInt32(&healthy, 1)
|
||||
defer atomic.StoreInt32(&healthy, 0)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/healthz", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.healthzHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
if !strings.Contains(rr.Body.String(), "OK") {
|
||||
t.Errorf("expected OK in body, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestReadyzHandler_Ready(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
atomic.StoreInt32(&ready, 1)
|
||||
defer atomic.StoreInt32(&ready, 0)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/readyz", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.readyzHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
}
|
||||
|
||||
func TestEnableReadyHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
atomic.StoreInt32(&ready, 0)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/readyz/enable", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.enableReadyHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusAccepted {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusAccepted)
|
||||
}
|
||||
if atomic.LoadInt32(&ready) != 1 {
|
||||
t.Error("ready flag not set to 1")
|
||||
}
|
||||
atomic.StoreInt32(&ready, 0)
|
||||
}
|
||||
|
||||
func TestDisableReadyHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
atomic.StoreInt32(&ready, 1)
|
||||
|
||||
req, _ := http.NewRequest("POST", "/readyz/disable", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.disableReadyHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusAccepted {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusAccepted)
|
||||
}
|
||||
if atomic.LoadInt32(&ready) != 0 {
|
||||
t.Error("ready flag not set to 0")
|
||||
}
|
||||
}
|
||||
|
||||
23
pkg/api/http/http_test.go
Normal file
23
pkg/api/http/http_test.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestVersionMiddleware(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
handler := versionMiddleware(http.HandlerFunc(srv.infoHandler))
|
||||
|
||||
req, _ := http.NewRequest("GET", "/", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
|
||||
if req.Header.Get("X-API-Version") == "" {
|
||||
t.Error("X-API-Version not set by middleware")
|
||||
}
|
||||
if req.Header.Get("X-API-Revision") == "" {
|
||||
t.Error("X-API-Revision not set by middleware")
|
||||
}
|
||||
}
|
||||
@@ -24,3 +24,28 @@ func TestStatusHandler(t *testing.T) {
|
||||
status, http.StatusNotFound)
|
||||
}
|
||||
}
|
||||
|
||||
func TestStatusHandler_Various(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.router.HandleFunc("/status/{code}", srv.statusHandler)
|
||||
|
||||
cases := []struct {
|
||||
code string
|
||||
expected int
|
||||
}{
|
||||
{"200", 200},
|
||||
{"201", 201},
|
||||
{"500", 500},
|
||||
{"503", 503},
|
||||
}
|
||||
|
||||
for _, c := range cases {
|
||||
req, _ := http.NewRequest("GET", "/status/"+c.code, nil)
|
||||
rr := httptest.NewRecorder()
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != c.expected {
|
||||
t.Errorf("/status/%s: got %d, want %d", c.code, rr.Code, c.expected)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
@@ -80,3 +81,50 @@ func TestStoreReadHandler_PathTraversal(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreWriteHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.DataPath = t.TempDir()
|
||||
|
||||
payload := "hello world"
|
||||
req, _ := http.NewRequest("POST", "/store", strings.NewReader(payload))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.storeWriteHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusAccepted {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusAccepted)
|
||||
}
|
||||
|
||||
var result map[string]string
|
||||
json.Unmarshal(rr.Body.Bytes(), &result)
|
||||
if result["hash"] != hash(payload) {
|
||||
t.Errorf("hash = %q, want %q", result["hash"], hash(payload))
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreWriteHandler_InvalidPath(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.DataPath = "/nonexistent/path/that/does/not/exist"
|
||||
|
||||
req, _ := http.NewRequest("POST", "/store", strings.NewReader("data"))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.storeWriteHandler).ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "writing file failed") {
|
||||
t.Errorf("expected write error, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
func TestStoreReadHandler_NotFound(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.DataPath = t.TempDir()
|
||||
srv.router.HandleFunc("/store/{hash}", srv.storeReadHandler)
|
||||
|
||||
req, _ := http.NewRequest("GET", "/store/aabbccddee11223344556677889900aabbccddee", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
srv.router.ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "reading file failed") {
|
||||
t.Errorf("expected read error, got: %s", rr.Body.String())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ import (
|
||||
|
||||
type jwtCustomClaims struct {
|
||||
Name string `json:"name"`
|
||||
jwt.StandardClaims
|
||||
jwt.RegisteredClaims
|
||||
}
|
||||
|
||||
// Token godoc
|
||||
@@ -44,9 +44,9 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
expiresAt := time.Now().Add(time.Minute * 1)
|
||||
claims := &jwtCustomClaims{
|
||||
user,
|
||||
jwt.StandardClaims{
|
||||
jwt.RegisteredClaims{
|
||||
Issuer: "podinfo",
|
||||
ExpiresAt: expiresAt.Unix(),
|
||||
ExpiresAt: jwt.NewNumericDate(expiresAt),
|
||||
},
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ func (s *Server) tokenGenerateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
var result = TokenResponse{
|
||||
Token: t,
|
||||
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
|
||||
ExpiresAt: claims.ExpiresAt.Time,
|
||||
}
|
||||
|
||||
s.JSONResponse(w, r, result)
|
||||
@@ -104,12 +104,12 @@ func (s *Server) tokenValidateHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
if token.Valid {
|
||||
if claims.StandardClaims.Issuer != "podinfo" {
|
||||
if claims.Issuer != "podinfo" {
|
||||
s.ErrorResponse(w, r, span, "invalid issuer", http.StatusUnauthorized)
|
||||
} else {
|
||||
var result = TokenValidationResponse{
|
||||
TokenName: claims.Name,
|
||||
ExpiresAt: time.Unix(claims.StandardClaims.ExpiresAt, 0),
|
||||
ExpiresAt: claims.ExpiresAt.Time,
|
||||
}
|
||||
s.JSONResponse(w, r, result)
|
||||
}
|
||||
|
||||
@@ -34,3 +34,97 @@ func TestTokenHandler(t *testing.T) {
|
||||
t.Error("handler returned no token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenGenerateHandler_EmptyBody(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.JWTSecret = "test-secret"
|
||||
|
||||
req, _ := http.NewRequest("POST", "/token", strings.NewReader(""))
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenGenerateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
|
||||
var result TokenResponse
|
||||
json.Unmarshal(rr.Body.Bytes(), &result)
|
||||
if result.Token == "" {
|
||||
t.Error("expected non-empty token")
|
||||
}
|
||||
}
|
||||
|
||||
func TestTokenValidateHandler(t *testing.T) {
|
||||
srv := NewMockServer()
|
||||
srv.config.JWTSecret = "test-secret"
|
||||
|
||||
// Generate a token first
|
||||
genReq, _ := http.NewRequest("POST", "/token", strings.NewReader("test-user"))
|
||||
genRR := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenGenerateHandler).ServeHTTP(genRR, genReq)
|
||||
|
||||
var tokenResp TokenResponse
|
||||
json.Unmarshal(genRR.Body.Bytes(), &tokenResp)
|
||||
|
||||
t.Run("valid token", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/token/validate", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenResp.Token)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenValidateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d, want %d", rr.Code, http.StatusOK)
|
||||
}
|
||||
var result TokenValidationResponse
|
||||
json.Unmarshal(rr.Body.Bytes(), &result)
|
||||
if result.TokenName != "test-user" {
|
||||
t.Errorf("token_name = %q, want %q", result.TokenName, "test-user")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("missing authorization header", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/token/validate", nil)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenValidateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "authorization bearer header required") {
|
||||
t.Errorf("unexpected body: %s", rr.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("malformed authorization header", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/token/validate", nil)
|
||||
req.Header.Set("Authorization", "InvalidFormat")
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenValidateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "authorization bearer header required") {
|
||||
t.Errorf("unexpected body: %s", rr.Body.String())
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("invalid token", func(t *testing.T) {
|
||||
req, _ := http.NewRequest("GET", "/token/validate", nil)
|
||||
req.Header.Set("Authorization", "Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZmFrZSIsImlzcyI6InBvZGluZm8iLCJleHAiOjk5OTk5OTk5OTl9.invalidsig")
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(srv.tokenValidateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("got status %d", rr.Code)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("wrong signing secret", func(t *testing.T) {
|
||||
otherSrv := NewMockServer()
|
||||
otherSrv.config.JWTSecret = "different-secret"
|
||||
|
||||
req, _ := http.NewRequest("GET", "/token/validate", nil)
|
||||
req.Header.Set("Authorization", "Bearer "+tokenResp.Token)
|
||||
rr := httptest.NewRecorder()
|
||||
http.HandlerFunc(otherSrv.tokenValidateHandler).ServeHTTP(rr, req)
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "signature is invalid") {
|
||||
t.Errorf("expected signature error, got: %s", rr.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user