mirror of
https://github.com/lukaszraczylo/gohoarder.git
synced 2026-10-12 00:45:16 +00:00
The automated update changed only go.mod, so the build failed on the old v2 imports. This commit moves the code to the v3 API. The commit replaces BodyParser with Bind().Body, Static with the static middleware, and ListenTLS with ListenConfig. It also updates the tests for TestConfig. Fixes #179
287 lines
7.9 KiB
Go
287 lines
7.9 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"encoding/json"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/gofiber/fiber/v3"
|
|
"github.com/lukaszraczylo/gohoarder/pkg/auth"
|
|
"github.com/stretchr/testify/suite"
|
|
)
|
|
|
|
type AuthHandlersTestSuite struct {
|
|
suite.Suite
|
|
app *fiber.App
|
|
appInst *App
|
|
authManager *auth.Manager
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) SetupTest() {
|
|
// Create auth manager
|
|
s.authManager = auth.New()
|
|
|
|
// Create app instance
|
|
s.appInst = &App{
|
|
authManager: s.authManager,
|
|
}
|
|
|
|
// Create Fiber app
|
|
s.app = fiber.New()
|
|
|
|
// Register routes
|
|
s.app.Post("/api/admin/keys", s.appInst.handleGenerateAPIKey)
|
|
s.app.Get("/api/admin/keys", s.appInst.handleListAPIKeys)
|
|
s.app.Delete("/api/admin/keys/:key_id", s.appInst.handleRevokeAPIKey)
|
|
}
|
|
|
|
func TestAuthHandlersTestSuite(t *testing.T) {
|
|
suite.Run(t, new(AuthHandlersTestSuite))
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) TestHandleGenerateAPIKey() {
|
|
tests := []struct {
|
|
requestBody map[string]string
|
|
name string
|
|
expectedRole string
|
|
expectedStatus int
|
|
expectKey bool
|
|
}{
|
|
{
|
|
name: "generate read-only key",
|
|
requestBody: map[string]string{
|
|
"role": "readonly",
|
|
"name": "test-readonly-key",
|
|
},
|
|
expectedStatus: 201,
|
|
expectedRole: "readonly",
|
|
expectKey: true,
|
|
},
|
|
{
|
|
name: "generate read-write key",
|
|
requestBody: map[string]string{
|
|
"role": "readwrite",
|
|
"name": "test-readwrite-key",
|
|
},
|
|
expectedStatus: 201,
|
|
expectedRole: "readwrite",
|
|
expectKey: true,
|
|
},
|
|
{
|
|
name: "generate admin key",
|
|
requestBody: map[string]string{
|
|
"role": "admin",
|
|
"name": "test-admin-key",
|
|
},
|
|
expectedStatus: 201,
|
|
expectedRole: "admin",
|
|
expectKey: true,
|
|
},
|
|
{
|
|
name: "invalid role",
|
|
requestBody: map[string]string{
|
|
"role": "invalid-role",
|
|
"name": "test-key",
|
|
},
|
|
expectedStatus: 400,
|
|
expectKey: false,
|
|
},
|
|
{
|
|
name: "missing role defaults to readonly",
|
|
requestBody: map[string]string{
|
|
"name": "test-key-default-role",
|
|
},
|
|
expectedStatus: 201,
|
|
expectedRole: "readonly", // Role defaults to readonly when not specified
|
|
expectKey: true,
|
|
},
|
|
{
|
|
name: "missing name",
|
|
requestBody: map[string]string{
|
|
"role": "read-only",
|
|
},
|
|
expectedStatus: 400,
|
|
expectKey: false,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
s.Run(tt.name, func() {
|
|
bodyBytes, err := json.Marshal(tt.requestBody)
|
|
s.Require().NoError(err)
|
|
|
|
req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(tt.expectedStatus, resp.StatusCode)
|
|
|
|
if tt.expectKey {
|
|
var result struct {
|
|
Key string `json:"key"`
|
|
KeyID string `json:"key_id"`
|
|
Role string `json:"role"`
|
|
Name string `json:"name"`
|
|
Message string `json:"message"`
|
|
}
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
s.NoError(err)
|
|
s.NotEmpty(result.Key)
|
|
s.NotEmpty(result.KeyID)
|
|
s.Equal(tt.expectedRole, result.Role)
|
|
s.Equal(tt.requestBody["name"], result.Name)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) TestHandleListAPIKeys() {
|
|
// Generate some test keys first
|
|
_, _, _ = s.authManager.GenerateAPIKey("test-key-1", auth.RoleReadOnly, nil)
|
|
_, _, _ = s.authManager.GenerateAPIKey("test-key-2", auth.RoleReadWrite, nil)
|
|
_, _, _ = s.authManager.GenerateAPIKey("test-key-3", auth.RoleAdmin, nil)
|
|
|
|
req := httptest.NewRequest("GET", "/api/admin/keys", nil)
|
|
resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(200, resp.StatusCode)
|
|
|
|
var result struct {
|
|
Keys []map[string]interface{} `json:"keys"`
|
|
Total int `json:"total"`
|
|
}
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
s.NoError(err)
|
|
s.GreaterOrEqual(result.Total, 3)
|
|
|
|
// Verify keys don't include the actual key value
|
|
for _, key := range result.Keys {
|
|
s.NotEmpty(key["id"])
|
|
s.NotEmpty(key["role"])
|
|
s.NotEmpty(key["name"])
|
|
s.NotEmpty(key["created_at"])
|
|
}
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) TestHandleRevokeAPIKey() {
|
|
// Generate a test key
|
|
keyInfo, _, _ := s.authManager.GenerateAPIKey("test-revoke-key", auth.RoleReadOnly, nil)
|
|
|
|
tests := []struct {
|
|
name string
|
|
keyID string
|
|
expectedStatus int
|
|
}{
|
|
{
|
|
name: "revoke existing key",
|
|
keyID: keyInfo.ID,
|
|
expectedStatus: 200,
|
|
},
|
|
{
|
|
name: "revoke non-existent key",
|
|
keyID: "non-existent-key-id",
|
|
expectedStatus: 404,
|
|
},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
s.Run(tt.name, func() {
|
|
req := httptest.NewRequest("DELETE", "/api/admin/keys/"+tt.keyID, nil)
|
|
resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(tt.expectedStatus, resp.StatusCode)
|
|
|
|
if tt.expectedStatus == 200 {
|
|
var result map[string]string
|
|
err = json.NewDecoder(resp.Body).Decode(&result)
|
|
s.NoError(err)
|
|
s.Contains(result, "message")
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) TestHandleGenerateAPIKeyInvalidJSON() {
|
|
req := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader([]byte("invalid json")))
|
|
req.Header.Set("Content-Type", "application/json")
|
|
|
|
resp, err := s.app.Test(req, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(400, resp.StatusCode)
|
|
}
|
|
|
|
func (s *AuthHandlersTestSuite) TestGenerateAndRevokeKeyFlow() {
|
|
// Generate a key
|
|
bodyBytes, _ := json.Marshal(map[string]string{
|
|
"role": "readonly",
|
|
"name": "integration-test-key",
|
|
})
|
|
|
|
req1 := httptest.NewRequest("POST", "/api/admin/keys", bytes.NewReader(bodyBytes))
|
|
req1.Header.Set("Content-Type", "application/json")
|
|
resp1, err := s.app.Test(req1, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(201, resp1.StatusCode)
|
|
|
|
var createResult struct {
|
|
Key string `json:"key"`
|
|
KeyID string `json:"key_id"`
|
|
}
|
|
err = json.NewDecoder(resp1.Body).Decode(&createResult)
|
|
s.Require().NoError(err)
|
|
keyID := createResult.KeyID
|
|
|
|
// List keys - should include our new key
|
|
req2 := httptest.NewRequest("GET", "/api/admin/keys", nil)
|
|
resp2, err := s.app.Test(req2, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(200, resp2.StatusCode)
|
|
|
|
var listResult struct {
|
|
Keys []map[string]interface{} `json:"keys"`
|
|
Total int `json:"total"`
|
|
}
|
|
err = json.NewDecoder(resp2.Body).Decode(&listResult)
|
|
s.Require().NoError(err)
|
|
|
|
found := false
|
|
for _, key := range listResult.Keys {
|
|
if key["id"].(string) == keyID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
s.True(found, "newly created key should be in the list")
|
|
|
|
// Revoke the key
|
|
req3 := httptest.NewRequest("DELETE", "/api/admin/keys/"+keyID, nil)
|
|
resp3, err := s.app.Test(req3, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(200, resp3.StatusCode)
|
|
|
|
// List keys again - should not include the revoked key
|
|
req4 := httptest.NewRequest("GET", "/api/admin/keys", nil)
|
|
resp4, err := s.app.Test(req4, fiber.TestConfig{Timeout: 5 * time.Second, FailOnTimeout: true}) // 5 second timeout for CI environments
|
|
s.Require().NoError(err)
|
|
s.Equal(200, resp4.StatusCode)
|
|
|
|
var listResult2 struct {
|
|
Keys []map[string]interface{} `json:"keys"`
|
|
Total int `json:"total"`
|
|
}
|
|
err = json.NewDecoder(resp4.Body).Decode(&listResult2)
|
|
s.Require().NoError(err)
|
|
|
|
found = false
|
|
for _, key := range listResult2.Keys {
|
|
if key["id"].(string) == keyID {
|
|
found = true
|
|
break
|
|
}
|
|
}
|
|
s.False(found, "revoked key should not be in the list")
|
|
}
|