274 lines
8.7 KiB
Go
274 lines
8.7 KiB
Go
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)
|
||
}
|
||
}
|
||
}
|