package web_test import ( "context" "net/http" "net/http/httptest" "net/url" "os" "strings" "testing" "github.com/netcell-it/deklarix/internal/extract" "github.com/netcell-it/deklarix/internal/rules" "github.com/netcell-it/deklarix/internal/web" ) type fakeExtractor struct { facts rules.Facts err error } func (f fakeExtractor) Extract(ctx context.Context, in extract.Input) (rules.Facts, error) { return f.facts, f.err } func loadRealRules(t *testing.T) []rules.Rule { t.Helper() rs, err := rules.Load(os.DirFS("../../rules")) if err != nil { t.Fatalf("rules.Load: %v", err) } return rs } func newTestServer(t *testing.T, ex web.Extractor) *web.Server { t.Helper() s, err := web.NewServer(ex, loadRealRules(t)) if err != nil { t.Fatalf("NewServer: %v", err) } return s } func TestHandleHealth(t *testing.T) { s := newTestServer(t, fakeExtractor{}) req := httptest.NewRequest(http.MethodGet, "/health", nil) w := httptest.NewRecorder() s.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } if w.Body.String() != `{"ok":true}` { t.Fatalf("body = %q, want {\"ok\":true}", w.Body.String()) } } func TestHandleIndexRendersForm(t *testing.T) { s := newTestServer(t, fakeExtractor{}) req := httptest.NewRequest(http.MethodGet, "/", nil) w := httptest.NewRecorder() s.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } body := w.Body.String() for _, want := range []string{`name="platform"`, `name="caption"`, `hx-post="/pruefen"`} { if !strings.Contains(body, want) { t.Errorf("index body missing %q\nbody: %s", want, body) } } } func TestHandleStaticServesHTMX(t *testing.T) { s := newTestServer(t, fakeExtractor{}) req := httptest.NewRequest(http.MethodGet, "/static/htmx.min.js", nil) w := httptest.NewRecorder() s.ServeHTTP(w, req) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } if w.Body.Len() < 1000 { t.Fatalf("htmx.min.js suspiciously small: %d bytes", w.Body.Len()) } } func postCheck(t *testing.T, s *web.Server, form url.Values) *httptest.ResponseRecorder { t.Helper() req := httptest.NewRequest(http.MethodPost, "/pruefen", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") w := httptest.NewRecorder() s.ServeHTTP(w, req) return w } func TestHandleCheckRejectsMissingFields(t *testing.T) { s := newTestServer(t, fakeExtractor{}) w := postCheck(t, s, url.Values{"platform": {"instagram"}}) if w.Code != http.StatusBadRequest { t.Fatalf("status = %d, want 400 for missing caption", w.Code) } } func TestHandleCheckPropagatesExtractionError(t *testing.T) { s := newTestServer(t, fakeExtractor{err: errTest}) w := postCheck(t, s, url.Values{"platform": {"instagram"}, "caption": {"x"}}) if w.Code != http.StatusBadGateway { t.Fatalf("status = %d, want 502 when extraction fails", w.Code) } } func TestHandleCheckRendersFinding(t *testing.T) { s := newTestServer(t, fakeExtractor{facts: rules.Facts{ Platform: "instagram", Jurisdiction: "DE", Consideration: rules.ConsiderationPaid, DisclosurePresent: true, DisclosureWording: "Werbung", DisclosureBeforeCut: false, }}) w := postCheck(t, s, url.Values{"platform": {"instagram"}, "caption": {"Werbung, schaut mal..."}}) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } body := w.Body.String() if !strings.Contains(body, "WK-004") { t.Errorf("expected WK-004 finding in result, got: %s", body) } } func TestHandleCheckRendersNeedsClarification(t *testing.T) { s := newTestServer(t, fakeExtractor{facts: rules.Facts{ Platform: "instagram", Jurisdiction: "DE", Consideration: rules.ConsiderationUnclear, }}) w := postCheck(t, s, url.Values{"platform": {"instagram"}, "caption": {"..."}}) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } body := w.Body.String() if strings.Contains(body, "WK-") { t.Errorf("expected no rule findings for an unclear extraction, got: %s", body) } if !strings.Contains(body, "nicht sicher bestimmt") { t.Errorf("expected a clarification message, got: %s", body) } } func TestHandleCheckCleanCaseHasNoFindings(t *testing.T) { s := newTestServer(t, fakeExtractor{facts: rules.Facts{ Platform: "instagram", Jurisdiction: "DE", Consideration: rules.ConsiderationNone, }}) w := postCheck(t, s, url.Values{"platform": {"instagram"}, "caption": {"ein ganz normaler Post"}}) if w.Code != http.StatusOK { t.Fatalf("status = %d, want 200", w.Code) } body := w.Body.String() if strings.Contains(body, "WK-") { t.Errorf("expected no findings for an organic post, got: %s", body) } if !strings.Contains(body, "Keine Kennzeichnungsrisiken") { t.Errorf("expected the no-findings message, got: %s", body) } } var errTest = extractError("simulierter Extraktionsfehler") type extractError string func (e extractError) Error() string { return string(e) }