Files
ai-disclosure-standard/internal/app/server_test.go
T
jbergner 662afb3acc
release-tag / release-image (push) Failing after 4m50s
RC-1
2026-07-20 21:42:37 +02:00

274 lines
8.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package app
import (
"context"
"encoding/json"
"io"
"log/slog"
"net/http"
"net/http/httptest"
"strings"
"testing"
)
func testHandler(t *testing.T) http.Handler {
t.Helper()
return testHandlerConfig(t, Config{ListenAddress: ":0", BaseURL: "https://example.org", PublicName: "Test", DefaultLanguage: "de"})
}
func testHandlerConfig(t *testing.T, cfg Config) http.Handler {
t.Helper()
h, err := New(context.Background(), cfg, slog.New(slog.NewTextHandler(io.Discard, nil)))
if err != nil {
t.Fatal(err)
}
return h
}
func TestPresetBadgeFrench(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/badge/research.svg?lang=fr", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
if ct := w.Header().Get("Content-Type"); !strings.Contains(ct, "image/svg+xml") {
t.Fatalf("content type %q", ct)
}
if !strings.Contains(w.Body.String(), "Aide à la recherche") {
t.Fatal("missing localized preset")
}
}
func TestValidate(t *testing.T) {
body := `{"@context":"https://example.org/context/v1","@type":"AIUsageDeclaration","schemaVersion":"1.1","language":"de","components":{"text":{"aiExtent":"assisted","activities":["research"],"humanReview":"editorial"}},"assurance":"selfDeclared"}`
r := httptest.NewRequest(http.MethodPost, "/v1/validate", strings.NewReader(body))
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
var result map[string]any
if err := json.Unmarshal(w.Body.Bytes(), &result); err != nil {
t.Fatal(err)
}
if result["valid"] != true {
t.Fatalf("unexpected response: %#v", result)
}
}
func TestCommunityRejectsCustomBadge(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/v1/badge.svg?lang=en&badgeMessage=Custom", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusForbidden {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "pro_feature_required") {
t.Fatalf("unexpected response: %s", w.Body.String())
}
}
func TestCapabilities(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/v1/capabilities", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d", w.Code)
}
if !strings.Contains(w.Body.String(), `"edition":"community"`) {
t.Fatalf("unexpected response: %s", w.Body.String())
}
}
func TestMarketingPageGerman(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/product?lang=de", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
body := w.Body.String()
for _, expected := range []string{"KI-Nutzung transparent kennzeichnen", "Docker Compose", "JSON-LD"} {
if !strings.Contains(body, expected) {
t.Fatalf("marketing page missing %q", expected)
}
}
}
func TestMarketingPageLanguageNegotiation(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/product", nil)
r.Header.Set("Accept-Language", "fr-FR,fr;q=0.9,en;q=0.8")
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "Déclarez l’usage de l’IA") {
t.Fatal("missing French marketing content")
}
}
func TestInstallAlias(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/install?lang=en", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusTemporaryRedirect {
t.Fatalf("status %d", w.Code)
}
if location := w.Header().Get("Location"); location != "/product?lang=en#install" {
t.Fatalf("unexpected redirect %q", location)
}
}
func TestPricingRouteRemoved(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/pricing", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusNotFound {
t.Fatalf("status %d", w.Code)
}
}
func TestDeclarationLanguageSwitcherPreservesQuery(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/declaration?mode=article&textExtent=partial&textReview=expert&coverImageExtent=partial&coverImageReview=editorial&lang=de&assurance=selfDeclared", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
body := w.Body.String()
for _, expected := range []string{
`id="declaration-language"`,
`lang=en`,
`textExtent=partial`,
`textReview=expert`,
`coverImageExtent=partial`,
`hreflang="en"`,
`hreflang="x-default"`,
} {
if !strings.Contains(body, expected) {
t.Fatalf("language switcher missing %q", expected)
}
}
if !strings.Contains(body, `<option value="/declaration?`) || !strings.Contains(body, `lang=de`) || !strings.Contains(body, ` selected`) {
t.Fatal("current language is not selected")
}
}
func TestArticleBadgeUsesCalmVioletDefault(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/v1/badge.svg?mode=article&textExtent=none&textReview=none&imageExtent=full&imageReview=editorial&lang=de", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("unexpected status %d: %s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), `fill="#7c3aed"`) {
t.Fatalf("article badge does not use violet default: %s", w.Body.String())
}
}
func TestGeneratorExposesAssuranceSelection(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/?lang=de", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
body := w.Body.String()
for _, expected := range []string{
`id="assurance"`,
`value="selfDeclared"`,
`value="technicallyRecorded"`,
`value="signed"`,
`value="verified"`,
`Nachweisgrundlage`,
} {
if !strings.Contains(body, expected) {
t.Fatalf("generator missing %q", expected)
}
}
}
func TestDeclarationUsesSelectedAssurance(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/declaration?preset=research&lang=de&assurance=technicallyRecorded", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
body := w.Body.String()
for _, expected := range []string{
`"assurance":"technicallyRecorded"`,
`Als Nachweisgrundlage ist eine technische Protokollierung im Erstellungs- oder Veröffentlichungsprozess angegeben.`,
`<colgroup>`,
`scope="col"`,
} {
if !strings.Contains(body, expected) {
t.Fatalf("declaration missing %q", expected)
}
}
}
func TestSignedAssuranceExplainsScope(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/declaration?preset=summary&lang=de&assurance=signed", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "Die Signatur bestätigt nicht automatisch die inhaltliche Richtigkeit der Angaben.") {
t.Fatal("signed assurance scope is not explained")
}
}
func TestBackgroundPageGerman(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/background?lang=de", nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
body := w.Body.String()
for _, expected := range []string{
"Warum KI-Nutzung gekennzeichnet wird",
"Artikel 50 gilt ab 2. August 2026",
"keine allgemeine Kennzeichnungspflicht",
"KI-generierter Text zu Angelegenheiten von öffentlichem Interesse",
"Leitlinien zu Transparenzpflichten",
`hreflang="en"`,
`id="background-language"`,
} {
if !strings.Contains(body, expected) {
t.Fatalf("background page missing %q", expected)
}
}
}
func TestBackgroundPageFrenchNegotiation(t *testing.T) {
r := httptest.NewRequest(http.MethodGet, "/background", nil)
r.Header.Set("Accept-Language", "fr-FR,fr;q=0.9,en;q=0.8")
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("status %d: %s", w.Code, w.Body.String())
}
if !strings.Contains(w.Body.String(), "Pourquoi signaler l’utilisation de l’IA") {
t.Fatal("missing French background content")
}
}
func TestNavigationUsesInternalBackgroundPage(t *testing.T) {
for _, path := range []string{"/?lang=de", "/product?lang=de"} {
r := httptest.NewRequest(http.MethodGet, path, nil)
w := httptest.NewRecorder()
testHandler(t).ServeHTTP(w, r)
if w.Code != http.StatusOK {
t.Fatalf("%s returned %d", path, w.Code)
}
if !strings.Contains(w.Body.String(), `href="/background?lang=de"`) {
t.Fatalf("%s does not link to the internal background page", path)
}
}
}