Files
jbergner 133fb10a93
All checks were successful
release-tag / release-image (push) Successful in 1m47s
RC-5 Versiopn: 1.6.3
2026-07-22 05:58:38 +02:00

165 lines
6.1 KiB
Go

package declaration
import (
"errors"
"net/url"
"strings"
"testing"
)
func TestPresetQuery(t *testing.T) {
v := url.Values{"preset": {"research"}, "subject": {"https://example.org/post"}, "lang": {"fr"}}
d, err := NewFromQuery(v, "https://example.org/context/v1")
if err != nil {
t.Fatal(err)
}
c := d.Components["text"]
if c.AIExtent != "assisted" || len(c.Activities) != 1 || c.Activities[0] != "research" {
t.Fatalf("unexpected component: %#v", c)
}
if d.Language != "fr" {
t.Fatalf("unexpected language: %s", d.Language)
}
if c.HumanReview != "none" {
t.Fatalf("preset inferred human review=%q", c.HumanReview)
}
}
func TestNoAIRejectsActivities(t *testing.T) {
d := Declaration{Context: "https://example.org/context/v1", Type: "AIUsageDeclaration", SchemaVersion: SchemaVersion, Language: "de", Assurance: "selfDeclared", Components: map[string]Component{"text": {AIExtent: "none", Activities: []string{"research"}, HumanReview: "editorial"}}}
if Validate(d) == nil {
t.Fatal("expected validation error")
}
}
func TestCustomRequiresPro(t *testing.T) {
v := url.Values{"preset": {"research"}, "lang": {"en"}, "customTitle": {"Custom"}}
_, err := NewFromQuery(v, "https://example.org/context/v1")
if !errors.Is(err, ErrCustomTextRequiresPro) {
t.Fatalf("unexpected error: %v", err)
}
d, err := NewFromQueryWithOptions(v, "https://example.org/context/v1", ParseOptions{AllowCustomText: true, DefaultLanguage: "en"})
if err != nil {
t.Fatal(err)
}
if d.Presentation == nil || d.Presentation.Title != "Custom" {
t.Fatalf("missing presentation: %#v", d.Presentation)
}
}
func TestLegacySchemaValidation(t *testing.T) {
d := Declaration{Context: "https://example.org/context/v1", Type: "AIUsageDeclaration", SchemaVersion: LegacySchemaVersion, Language: "en", Assurance: "selfDeclared", Components: map[string]Component{"text": {AIExtent: "assisted", HumanReview: "editorial"}}}
if err := Validate(d); err != nil {
t.Fatalf("legacy declaration should remain valid: %v", err)
}
d.Presentation = &Presentation{Title: "Not allowed in 1.0"}
if Validate(d) == nil {
t.Fatal("expected presentation to require schema 1.1")
}
}
func TestArticleDeclarationFromQuery(t *testing.T) {
q := url.Values{
"mode": {"article"}, "lang": {"de"},
"textExtent": {"none"}, "imageExtent": {"full"}, "researchExtent": {"assisted"},
}
d, err := NewFromQuery(q, "https://example.org/context/v1")
if err != nil {
t.Fatal(err)
}
if len(d.Components) != 3 {
t.Fatalf("components=%d", len(d.Components))
}
if d.Components["text"].AIExtent != "none" {
t.Fatalf("unexpected text extent")
}
if d.Components["text"].HumanReview != "none" {
t.Fatalf("no-AI component review=%q, want none", d.Components["text"].HumanReview)
}
if d.Components["research"].HumanReview != "none" {
t.Fatalf("AI-assisted component review=%q, want no inferred review", d.Components["research"].HumanReview)
}
if got := d.Components["research"].Activities; len(got) != 1 || got[0] != "research" {
t.Fatalf("research activities=%v", got)
}
if got := d.Components["image"].Activities; len(got) != 1 || got[0] != "imageGeneration" {
t.Fatalf("image activities=%v", got)
}
}
func TestArticleDeclarationKeepsPerComponentReviews(t *testing.T) {
q := url.Values{
"mode": {"article"}, "lang": {"de"},
"textExtent": {"assisted"}, "textReview": {"basic"},
"imageExtent": {"full"}, "imageReview": {"expert"},
}
d, err := NewFromQuery(q, "https://example.org/context/v1")
if err != nil {
t.Fatal(err)
}
if got := d.Components["text"].HumanReview; got != "basic" {
t.Fatalf("text review=%q", got)
}
if got := d.Components["image"].HumanReview; got != "expert" {
t.Fatalf("image review=%q", got)
}
}
func TestAssuranceFromQuery(t *testing.T) {
for _, assurance := range []string{"selfDeclared", "technicallyRecorded", "signed", "verified"} {
d, err := NewFromQuery(url.Values{"preset": {"research"}, "lang": {"de"}, "assurance": {assurance}}, "https://example.org/context/v1")
if err != nil {
t.Fatalf("assurance %s: %v", assurance, err)
}
if d.Assurance != assurance {
t.Fatalf("assurance=%q, want %q", d.Assurance, assurance)
}
}
}
func TestEditorialResponsibilityRequiresName(t *testing.T) {
_, err := NewFromQuery(url.Values{
"preset": {"research"},
"responsibleUrl": {"https://example.org/impressum"},
}, "https://example.org/context")
if err == nil || !strings.Contains(err.Error(), "editorialResponsibility.name is required") {
t.Fatalf("expected editorial responsibility name validation error, got %v", err)
}
}
func TestLegalContextPublicInterestRequiresAIText(t *testing.T) {
_, err := NewFromQuery(url.Values{
"mode": {"article"}, "lang": {"de"}, "textExtent": {"none"},
"legalContext": {"publicInterestText"},
}, "https://example.org/context/v1")
if err == nil || !strings.Contains(err.Error(), "publicInterestText requires a text component with AI involvement") {
t.Fatalf("expected public-interest consistency error, got %v", err)
}
}
func TestLegalContextDeepfakeRequiresAIMedia(t *testing.T) {
_, err := NewFromQuery(url.Values{
"mode": {"article"}, "lang": {"de"}, "textExtent": {"full"},
"legalContext": {"deepfake"},
}, "https://example.org/context/v1")
if err == nil || !strings.Contains(err.Error(), "deepfake requires an image, audio or video component with AI involvement") {
t.Fatalf("expected deepfake consistency error, got %v", err)
}
}
func TestLegalContextAndResponsibilityAreSerialized(t *testing.T) {
d, err := NewFromQuery(url.Values{
"mode": {"article"}, "lang": {"de"}, "textExtent": {"partial"}, "textReview": {"expert"},
"legalContext": {"publicInterestText"}, "responsibleRole": {"publisher"}, "responsible": {"Beispielredaktion"},
}, "https://example.org/context/v1")
if err != nil {
t.Fatal(err)
}
if d.SchemaVersion != "1.2" || d.LegalContext == nil || len(d.LegalContext.Categories) != 1 {
t.Fatalf("unexpected legal context: %#v", d)
}
if d.EditorialResponsibility == nil || !d.EditorialResponsibility.Assumed || d.EditorialResponsibility.Role != "publisher" {
t.Fatalf("responsibility not explicitly assumed: %#v", d.EditorialResponsibility)
}
}