package artifacts import ( "errors" "os" "path/filepath" "strings" "testing" "time" "gitea.maximumdirect.net/eric/narratio/internal/manifest" ) func TestNormalizeSessionArtifactSource(t *testing.T) { tests := []struct { name string source string wantID string wantErr string }{ {name: "legacy alias processed unsupported", source: "processed_transcript", wantErr: "unsupported artifact source"}, {name: "legacy alias normalized unsupported", source: "normalized_transcript", wantErr: "unsupported artifact source"}, {name: "legacy alias trimmed unsupported", source: "trimmed_transcript", wantErr: "unsupported artifact source"}, {name: "canonical", source: ArtifactTranscriptTrimmed, wantID: ArtifactTranscriptTrimmed}, {name: "unsupported", source: "narratio.unknown", wantErr: "unsupported artifact source"}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { got, err := NormalizeSessionArtifactSource(tt.source) if tt.wantErr != "" { if err == nil || !strings.Contains(err.Error(), tt.wantErr) { t.Fatalf("NormalizeSessionArtifactSource() error = %v, want contains %q", err, tt.wantErr) } return } if err != nil { t.Fatalf("NormalizeSessionArtifactSource() error = %v", err) } if got != tt.wantID { t.Fatalf("NormalizeSessionArtifactSource() = %q, want %q", got, tt.wantID) } }) } } func TestResolveSessionArtifactPrefersManifestOutput(t *testing.T) { workspace := t.TempDir() paths := buildSessionPaths(workspace, "campaign", "session") if err := os.MkdirAll(paths.ArtifactsDir, 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } manifestPath := filepath.Join(paths.ArtifactsDir, "normalized.from-manifest.json") if err := os.WriteFile(manifestPath, []byte(`{"segments":[]}`), 0o644); err != nil { t.Fatalf("WriteFile() error = %v", err) } canonicalPath := filepath.Join(paths.TranscriptsDir, "normalized.json") if err := os.MkdirAll(filepath.Dir(canonicalPath), 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } if err := os.WriteFile(canonicalPath, []byte(`{"segments":[{"id":123}]}`), 0o644); err != nil { t.Fatalf("WriteFile() error = %v", err) } m := manifest.New("session", time.Now().UTC()) m.MarkStageSucceeded("normalize", time.Now().UTC(), []manifest.ArtifactRecord{ {Kind: "transcript_normalized", LocalPath: manifestPath, ProducerRunID: "run-123"}, }) resolved, err := ResolveSessionArtifact(paths, m, ArtifactTranscriptFull) if err != nil { t.Fatalf("ResolveSessionArtifact() error = %v", err) } if resolved.Path != manifestPath { t.Fatalf("resolved path = %q, want %q", resolved.Path, manifestPath) } if resolved.Provenance != "manifest.normalize.outputs" { t.Fatalf("provenance = %q, want manifest.normalize.outputs", resolved.Provenance) } if resolved.ProducerRunID != "run-123" { t.Fatalf("producer run id = %q, want run-123", resolved.ProducerRunID) } } func TestResolveSessionArtifactFallsBackToCanonicalPath(t *testing.T) { workspace := t.TempDir() paths := buildSessionPaths(workspace, "campaign", "session") canonicalPath := filepath.Join(paths.TranscriptsDir, "trimmed.json") if err := os.MkdirAll(filepath.Dir(canonicalPath), 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } if err := os.WriteFile(canonicalPath, []byte(`{"segments":[]}`), 0o644); err != nil { t.Fatalf("WriteFile() error = %v", err) } resolved, err := ResolveSessionArtifact(paths, nil, ArtifactTranscriptTrimmed) if err != nil { t.Fatalf("ResolveSessionArtifact() error = %v", err) } if resolved.Path != canonicalPath { t.Fatalf("resolved path = %q, want %q", resolved.Path, canonicalPath) } if resolved.Provenance != "fallback.canonical_path" { t.Fatalf("provenance = %q, want fallback.canonical_path", resolved.Provenance) } } func TestResolveSessionArtifactMissingReturnsTypedError(t *testing.T) { workspace := t.TempDir() paths := buildSessionPaths(workspace, "campaign", "session") _, err := ResolveSessionArtifact(paths, nil, ArtifactTranscriptTrimmed) if err == nil { t.Fatal("expected error, got nil") } if !errors.Is(err, ErrSessionArtifactNotFound) { t.Fatalf("errors.Is(err, ErrSessionArtifactNotFound) = false; err=%v", err) } } func TestResolveSessionArtifactValidatesTranscriptShape(t *testing.T) { workspace := t.TempDir() paths := buildSessionPaths(workspace, "campaign", "session") canonicalPath := filepath.Join(paths.TranscriptsDir, "processed.json") if err := os.MkdirAll(filepath.Dir(canonicalPath), 0o755); err != nil { t.Fatalf("MkdirAll() error = %v", err) } if err := os.WriteFile(canonicalPath, []byte(`{"not_segments":[]}`), 0o644); err != nil { t.Fatalf("WriteFile() error = %v", err) } _, err := ResolveSessionArtifact(paths, nil, ArtifactTranscriptPolished) if err == nil { t.Fatal("expected error, got nil") } if !strings.Contains(err.Error(), "top-level segments is required") { t.Fatalf("error = %q, want segments validation error", err.Error()) } }