package usecase import ( "context" "path/filepath" "testing" "gitea.maximumdirect.net/eric/scriptorium/internal/artifact" "gitea.maximumdirect.net/eric/scriptorium/internal/domain" "gitea.maximumdirect.net/eric/scriptorium/internal/profile" "gitea.maximumdirect.net/eric/scriptorium/internal/prompt" "gitea.maximumdirect.net/eric/scriptorium/internal/validate" ) type integrationLLM struct{} func (f *integrationLLM) Generate(ctx context.Context, req domain.GenerateRequest) (*domain.GenerateResponse, error) { return &domain.GenerateResponse{ Content: `{"summary":"Party discovered a captive scout beneath the tower.","events":[{"title":"Scout found in cellar","type":"discovery","notes":"Scout requested rescue from goblin raiders."}]}`, Usage: domain.TokenUsage{ PromptTokens: 42, CompletionTokens: 36, TotalTokens: 78, }, }, nil } func TestRunnerIntegrationWithProfilesFixturesAndValidation(t *testing.T) { root, err := filepath.Abs(filepath.Join("..", "..")) if err != nil { t.Fatalf("failed to resolve repo root: %v", err) } profilesDir := filepath.Join(root, "profiles") schemasDir := filepath.Join(root, "schemas") fixturesDir := filepath.Join(root, "examples", "fixtures") runner := NewRunner( profile.NewFilesystemRepository(profilesDir), artifact.NewCompositeReader(), prompt.NewGoRenderer(), &integrationLLM{}, validate.NewStandardValidator(schemasDir), ) res, err := runner.Run(context.Background(), domain.RunRequest{ ProfileID: "generic.structured_events", Inputs: map[string]domain.ArtifactRef{ "transcript": { Type: domain.ArtifactRefFile, URI: filepath.Join(fixturesDir, "transcript.md"), }, "glossary": { Type: domain.ArtifactRefFile, URI: filepath.Join(fixturesDir, "glossary.yml"), }, }, }) if err != nil { t.Fatalf("expected no error, got %v", err) } if res.ProfileID != "generic.structured_events" { t.Fatalf("unexpected profile id: %q", res.ProfileID) } if res.ProfileVersion != "1.0.0" { t.Fatalf("unexpected profile version: %q", res.ProfileVersion) } if res.Validation.Status != domain.ValidationPassed { t.Fatalf("expected passed validation, got %q", res.Validation.Status) } if res.Validation.Mode != domain.ValidationJSONSchema { t.Fatalf("expected json_schema mode, got %q", res.Validation.Mode) } if res.Artifact.ContentType != "application/json" { t.Fatalf("expected application/json output, got %q", res.Artifact.ContentType) } if len(res.RawOutput) == 0 { t.Fatal("expected raw output to be preserved") } if res.PromptHash == "" { t.Fatal("expected non-empty prompt hash") } if len(res.InputHashes) != 2 { t.Fatalf("expected two input hashes, got %d", len(res.InputHashes)) } if res.InputHashes["transcript"] == "" || res.InputHashes["glossary"] == "" { t.Fatalf("expected both input hashes to be set, got %#v", res.InputHashes) } if res.Usage.TotalTokens != 78 { t.Fatalf("expected usage from fake llm, got %+v", res.Usage) } if res.StartTime.IsZero() || res.EndTime.IsZero() { t.Fatal("expected start/end timestamps") } if res.EndTime.Before(res.StartTime) { t.Fatalf("expected end >= start, got start=%v end=%v", res.StartTime, res.EndTime) } }