2026-05-04 21:23:48 -05:00
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"
2026-05-05 10:18:28 -05:00
"gitea.maximumdirect.net/eric/scriptorium/internal/promptdef"
2026-05-04 21:23:48 -05:00
"gitea.maximumdirect.net/eric/scriptorium/internal/validate"
)
type integrationLLM struct { }
func ( f * integrationLLM ) Generate ( ctx context . Context , req domain . GenerateRequest ) ( * domain . GenerateResponse , error ) {
2026-05-08 07:32:54 -05:00
lastIntegrationRequest = req
2026-05-04 21:23:48 -05:00
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
}
2026-05-08 07:32:54 -05:00
var lastIntegrationRequest domain . GenerateRequest
2026-05-05 10:58:22 -05:00
func TestRunnerIntegrationWithPromptAndProfileFixturesAndValidation ( t * testing . T ) {
2026-05-04 21:23:48 -05:00
root , err := filepath . Abs ( filepath . Join ( ".." , ".." ) )
if err != nil {
t . Fatalf ( "failed to resolve repo root: %v" , err )
}
2026-05-26 07:15:36 -05:00
promptsDir := filepath . Join ( root , "examples" , "prompts" )
profilesDir := filepath . Join ( root , "examples" , "profiles" )
schemasDir := filepath . Join ( root , "examples" , "schemas" )
2026-05-04 21:23:48 -05:00
fixturesDir := filepath . Join ( root , "examples" , "fixtures" )
2026-05-05 10:58:22 -05:00
t . Setenv ( "SCRIPTORIUM_API_KEY" , "test-key" )
2026-05-04 21:23:48 -05:00
runner := NewRunner (
2026-05-05 10:58:22 -05:00
promptdef . NewFilesystemRepository ( promptsDir ) ,
profile . NewFilesystemRepository ( profilesDir ) ,
2026-05-04 21:23:48 -05:00
artifact . NewCompositeReader ( ) ,
prompt . NewGoRenderer ( ) ,
& integrationLLM { } ,
validate . NewStandardValidator ( schemasDir ) ,
)
res , err := runner . Run ( context . Background ( ) , domain . RunRequest {
2026-05-05 10:58:22 -05:00
PromptID : "generic.structured_events" ,
2026-05-04 21:23:48 -05:00
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 )
}
2026-05-05 10:09:31 -05:00
if res . PromptID != "generic.structured_events" {
t . Fatalf ( "unexpected prompt id: %q" , res . PromptID )
2026-05-04 21:23:48 -05:00
}
2026-05-05 10:58:22 -05:00
if res . SelectedProfileID != "local-quality" {
t . Fatalf ( "expected selected profile local-quality from prompt default, got %q" , res . SelectedProfileID )
}
2026-05-05 08:41:07 -05:00
if res . RunID == "" {
t . Fatal ( "expected run id" )
}
2026-05-05 10:09:31 -05:00
if res . PromptHash == "" {
t . Fatal ( "expected prompt hash" )
2026-05-05 08:41:07 -05:00
}
2026-05-05 10:09:31 -05:00
if res . PromptVersion != "1.0.0" {
t . Fatalf ( "unexpected prompt version: %q" , res . PromptVersion )
2026-05-04 21:23:48 -05:00
}
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 )
}
2026-05-08 07:32:54 -05:00
if lastIntegrationRequest . StructuredOutput == nil {
t . Fatal ( "expected provider-level structured output request for json_schema prompt" )
}
if lastIntegrationRequest . StructuredOutput . Type != domain . StructuredOutputJSONSchema {
t . Fatalf ( "expected structured output type json_schema, got %q" , lastIntegrationRequest . StructuredOutput . Type )
}
if lastIntegrationRequest . StructuredOutput . JSONSchema == nil || lastIntegrationRequest . StructuredOutput . JSONSchema . Schema == nil {
t . Fatalf ( "expected structured output json_schema payload, got %+v" , lastIntegrationRequest . StructuredOutput . JSONSchema )
}
2026-05-04 21:23:48 -05:00
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 )
}
2026-05-05 08:41:07 -05:00
if res . Duration < 0 {
t . Fatalf ( "expected non-negative duration, got %s" , res . Duration )
}
2026-05-04 21:23:48 -05:00
}