Files
KanzlAI-mGMT/backend/internal/handlers/ai_handler_test.go
m 325fbeb5de test: comprehensive E2E and API test suite for full KanzlAI stack
Backend (Go):
- Expanded integration_test.go: health, auth middleware (expired/invalid/wrong-secret JWT),
  tenant CRUD, case CRUD (create/list/get/update/delete + filters + validation),
  deadline CRUD (create/list/update/complete/delete), appointment CRUD,
  dashboard (verifies all sections), deadline calculator (valid/invalid/unknown type),
  proceeding types & rules, document endpoints, AI extraction (no-key path),
  and full critical path E2E (auth -> case -> deadline -> appointment -> dashboard -> complete)
- New handler unit tests: case (10), appointment (11), dashboard (1), calculate (5),
  document (10), AI (4) — all testing validation, auth guards, and error paths without DB
- Total: ~80 backend tests (unit + integration)

Frontend (TypeScript/Vitest):
- Installed vitest 2.x, @testing-library/react, @testing-library/jest-dom, jsdom 24, msw
- vitest.config.ts with jsdom env, esbuild JSX automatic, path aliases
- API client tests (13): URL construction, no double /api/, auth header, tenant header,
  POST/PUT/PATCH/DELETE methods, error handling, 204 responses
- DeadlineTrafficLights tests (5): renders cards, correct counts, zero state, onFilter callback
- CaseOverviewGrid tests (4): renders categories, counts, header, zero state
- LoginPage tests (8): form rendering, mode toggle, password login, redirect, error display,
  magic link, registration link
- Total: 30 frontend tests

Makefile: test-frontend target now runs vitest instead of placeholder echo.
2026-03-25 16:19:00 +01:00

75 lines
1.8 KiB
Go

package handlers
import (
"bytes"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
)
func TestAIExtractDeadlines_EmptyInput(t *testing.T) {
h := &AIHandler{}
body := `{"text":""}`
r := httptest.NewRequest("POST", "/api/ai/extract-deadlines", bytes.NewBufferString(body))
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
h.ExtractDeadlines(w, r)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
var resp map[string]string
json.NewDecoder(w.Body).Decode(&resp)
if resp["error"] != "provide either a PDF file or text" {
t.Errorf("unexpected error: %s", resp["error"])
}
}
func TestAIExtractDeadlines_InvalidJSON(t *testing.T) {
h := &AIHandler{}
r := httptest.NewRequest("POST", "/api/ai/extract-deadlines", bytes.NewBufferString(`{broken`))
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
h.ExtractDeadlines(w, r)
if w.Code != http.StatusBadRequest {
t.Errorf("expected 400, got %d", w.Code)
}
}
func TestAISummarizeCase_MissingCaseID(t *testing.T) {
h := &AIHandler{}
body := `{"case_id":""}`
r := httptest.NewRequest("POST", "/api/ai/summarize-case", bytes.NewBufferString(body))
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
h.SummarizeCase(w, r)
// Without auth context, the resolveTenant will fail first
if w.Code != http.StatusUnauthorized {
t.Errorf("expected 401, got %d", w.Code)
}
}
func TestAISummarizeCase_InvalidJSON(t *testing.T) {
h := &AIHandler{}
r := httptest.NewRequest("POST", "/api/ai/summarize-case", bytes.NewBufferString(`not-json`))
r.Header.Set("Content-Type", "application/json")
w := httptest.NewRecorder()
h.SummarizeCase(w, r)
// Without auth context, the resolveTenant will fail first
if w.Code != http.StatusUnauthorized {
t.Errorf("expected 401, got %d", w.Code)
}
}