init push
This commit is contained in:
208
internal/app/api/audit_middleware_test.go
Normal file
208
internal/app/api/audit_middleware_test.go
Normal file
@@ -0,0 +1,208 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gofiber/fiber/v2"
|
||||
|
||||
"wucher/internal/domain/audit"
|
||||
"wucher/internal/service"
|
||||
sharedconst "wucher/internal/shared/const"
|
||||
)
|
||||
|
||||
type mockAuditMiddlewareRepo struct {
|
||||
mu sync.Mutex
|
||||
entries []*audit.AuditLog
|
||||
}
|
||||
|
||||
func (m *mockAuditMiddlewareRepo) Create(_ context.Context, entry *audit.AuditLog) error {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
cp := *entry
|
||||
m.entries = append(m.entries, &cp)
|
||||
return nil
|
||||
}
|
||||
|
||||
func TestAuditMiddleware_SkipsGETRequests(t *testing.T) {
|
||||
repo := &mockAuditMiddlewareRepo{}
|
||||
auditSvc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 16, Workers: 1})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(AuditMiddleware(auditSvc))
|
||||
app.Get("/api/v1/ping", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(http.StatusCreated)
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, "/api/v1/ping", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(500 * time.Millisecond)
|
||||
for time.Now().Before(deadline) {
|
||||
repo.mu.Lock()
|
||||
n := len(repo.entries)
|
||||
repo.mu.Unlock()
|
||||
if n > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if len(repo.entries) != 0 {
|
||||
t.Fatalf("expected no audit log entry for GET, got=%d", len(repo.entries))
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuditMiddleware_LogsMutationRequests(t *testing.T) {
|
||||
repo := &mockAuditMiddlewareRepo{}
|
||||
auditSvc := service.NewAuditLogService(repo, service.AuditLogServiceDependencies{QueueSize: 16, Workers: 1})
|
||||
|
||||
app := fiber.New()
|
||||
app.Use(AuditMiddleware(auditSvc))
|
||||
app.Post("/api/v1/helicopters/create", func(c *fiber.Ctx) error {
|
||||
return c.SendStatus(http.StatusCreated)
|
||||
})
|
||||
|
||||
req, _ := http.NewRequest(http.MethodPost, "/api/v1/helicopters/create", nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("expected 201, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
deadline := time.Now().Add(500 * time.Millisecond)
|
||||
for time.Now().Before(deadline) {
|
||||
repo.mu.Lock()
|
||||
n := len(repo.entries)
|
||||
repo.mu.Unlock()
|
||||
if n > 0 {
|
||||
break
|
||||
}
|
||||
time.Sleep(10 * time.Millisecond)
|
||||
}
|
||||
|
||||
repo.mu.Lock()
|
||||
defer repo.mu.Unlock()
|
||||
if len(repo.entries) == 0 {
|
||||
t.Fatalf("expected audit log entry for mutation request")
|
||||
}
|
||||
got := repo.entries[0]
|
||||
if got.Layer != "api" || got.Module != sharedconst.AuditModuleHelicopter || got.Action != sharedconst.AuditActionCreate {
|
||||
t.Fatalf("unexpected audit entry: layer=%s module=%s action=%s", got.Layer, got.Module, got.Action)
|
||||
}
|
||||
if got.StatusCode != http.StatusCreated {
|
||||
t.Fatalf("expected status 201, got %d", got.StatusCode)
|
||||
}
|
||||
}
|
||||
|
||||
func TestModuleFromPathSegment(t *testing.T) {
|
||||
cases := []struct {
|
||||
segment string
|
||||
want string
|
||||
}{
|
||||
{segment: "helicopters", want: sharedconst.AuditModuleHelicopter},
|
||||
{segment: "bases", want: sharedconst.AuditModuleBase},
|
||||
{segment: "health-insurance-companies", want: sharedconst.AuditModuleHealthInsuranceCompany},
|
||||
{segment: "hems-roster", want: sharedconst.AuditModuleUnknown},
|
||||
{segment: "dul", want: sharedconst.AuditModuleDUL},
|
||||
{segment: "reserve-acs", want: sharedconst.AuditModuleReserveAC},
|
||||
{segment: "not-registered", want: sharedconst.AuditModuleUnknown},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.segment, func(t *testing.T) {
|
||||
got := moduleFromPathSegment(tc.segment)
|
||||
if got != tc.want {
|
||||
t.Fatalf("moduleFromPathSegment(%q) = %q, want %q", tc.segment, got, tc.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveAuditModuleAction(t *testing.T) {
|
||||
cases := []struct {
|
||||
name string
|
||||
method string
|
||||
target string
|
||||
wantModule string
|
||||
wantAction string
|
||||
}{
|
||||
{
|
||||
name: "datatable list",
|
||||
method: http.MethodGet,
|
||||
target: "/api/v1/helicopters/get-all/dt",
|
||||
wantModule: sharedconst.AuditModuleHelicopter,
|
||||
wantAction: sharedconst.AuditActionList,
|
||||
},
|
||||
{
|
||||
name: "explicit create",
|
||||
method: http.MethodPost,
|
||||
target: "/api/v1/helicopters/create",
|
||||
wantModule: sharedconst.AuditModuleHelicopter,
|
||||
wantAction: sharedconst.AuditActionCreate,
|
||||
},
|
||||
{
|
||||
name: "fallback method update",
|
||||
method: http.MethodPatch,
|
||||
target: "/api/v1/helicopters/019dbeb6-4c37-7495-b0d0-e02ed134f87d",
|
||||
wantModule: sharedconst.AuditModuleHelicopter,
|
||||
wantAction: sharedconst.AuditActionUpdate,
|
||||
},
|
||||
{
|
||||
name: "base regular variant from query",
|
||||
method: http.MethodGet,
|
||||
target: "/api/v1/bases/get-all?category_type=regular",
|
||||
wantModule: sharedconst.AuditModuleBaseRegular,
|
||||
wantAction: sharedconst.AuditActionList,
|
||||
},
|
||||
{
|
||||
name: "base hems variant from query alias",
|
||||
method: http.MethodGet,
|
||||
target: "/api/v1/bases/get-all?base_category=hems",
|
||||
wantModule: sharedconst.AuditModuleBaseHEMS,
|
||||
wantAction: sharedconst.AuditActionList,
|
||||
},
|
||||
{
|
||||
name: "unknown module",
|
||||
method: http.MethodGet,
|
||||
target: "/api/v1/ping",
|
||||
wantModule: sharedconst.AuditModuleUnknown,
|
||||
wantAction: sharedconst.AuditActionGet,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
app := fiber.New()
|
||||
app.All("/*", func(c *fiber.Ctx) error {
|
||||
gotModule, gotAction := resolveAuditModuleAction(c)
|
||||
if gotModule != tc.wantModule || gotAction != tc.wantAction {
|
||||
t.Fatalf("resolveAuditModuleAction(%q, %q) = (%q, %q), want (%q, %q)",
|
||||
tc.method, tc.target, gotModule, gotAction, tc.wantModule, tc.wantAction)
|
||||
}
|
||||
return c.SendStatus(http.StatusNoContent)
|
||||
})
|
||||
req, _ := http.NewRequest(tc.method, tc.target, nil)
|
||||
resp, err := app.Test(req)
|
||||
if err != nil {
|
||||
t.Fatalf("app.Test: %v", err)
|
||||
}
|
||||
if resp.StatusCode != http.StatusNoContent {
|
||||
t.Fatalf("expected 204, got %d", resp.StatusCode)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user