Complete configuration diagnostics workstream
This commit is contained in:
@@ -11,7 +11,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
name string
|
||||
scriptoriumYAML string
|
||||
wantLoadErr string
|
||||
wantValidateErr string
|
||||
wantValidateErr []string
|
||||
assert func(t *testing.T, cfg *Config)
|
||||
}{
|
||||
{
|
||||
@@ -73,7 +73,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
enabled: true
|
||||
output_path: artifacts/session_recap.md
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts.session_recap.prompt_id is required when enabled",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.prompt_id", "required"},
|
||||
},
|
||||
{
|
||||
name: "enabled artifact missing output path fails",
|
||||
@@ -84,7 +84,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
enabled: true
|
||||
prompt_id: dnd.session_recap
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts.session_recap.output_path is required when enabled",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.output_path", "required"},
|
||||
},
|
||||
{
|
||||
name: "invalid timeout fails",
|
||||
@@ -92,7 +92,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
binary: scriptorium
|
||||
timeout: definitely-not-a-duration
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.timeout must be a valid duration",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.timeout", "valid duration"},
|
||||
},
|
||||
{
|
||||
name: "legacy previous session source fails validation",
|
||||
@@ -114,7 +114,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
session_id: true
|
||||
output_kind: session_recap
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source "` + legacyPreviousSource + `" is unsupported`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source", "unsupported"},
|
||||
},
|
||||
{
|
||||
name: "obsolete input passthrough fields fail strict decoding",
|
||||
@@ -166,7 +166,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.previous_session.artifact.
|
||||
required: false
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source "narratio.previous_session.artifact." must reference configured artifact key matching ^[a-z][a-z0-9_]*$`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source", "configured artifact key"},
|
||||
},
|
||||
{
|
||||
name: "canonical previous-session source invalid artifact key fails validation",
|
||||
@@ -182,7 +182,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.previous_session.artifact.session-recap
|
||||
required: false
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source "narratio.previous_session.artifact.session-recap" must reference configured artifact key matching ^[a-z][a-z0-9_]*$`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source", "configured artifact key"},
|
||||
},
|
||||
{
|
||||
name: "canonical previous-session source unknown artifact fails validation",
|
||||
@@ -198,7 +198,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.previous_session.artifact.quest_log
|
||||
required: false
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source "narratio.previous_session.artifact.quest_log" references unknown artifact "quest_log"`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.inputs.previous_recap.source", "unknown artifact"},
|
||||
},
|
||||
{
|
||||
name: "canonical artifact source is accepted",
|
||||
@@ -265,7 +265,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.unknown
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.session_recap.inputs.transcript.source "narratio.unknown" is unsupported`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.inputs.transcript.source", "unsupported"},
|
||||
},
|
||||
{
|
||||
name: "artifact render_debug override is accepted",
|
||||
@@ -380,7 +380,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
prompt_id: dnd.session_recap
|
||||
output_path: artifacts/session_recap.md
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts keys must match ^[a-z][a-z0-9_]*$",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts keys", "must match"},
|
||||
},
|
||||
{
|
||||
name: "artifact output path outside artifacts root fails validation",
|
||||
@@ -392,7 +392,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
prompt_id: dnd.session_recap
|
||||
output_path: transcripts/session_recap.md
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts.session_recap.output_path must be under artifacts/",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.output_path", "under artifacts/"},
|
||||
},
|
||||
{
|
||||
name: "missing depends_on for artifact source fails validation",
|
||||
@@ -416,7 +416,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.artifact.session_recap
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.player_handout.inputs.recap.source "narratio.artifact.session_recap" requires depends_on entry "session_recap"`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.player_handout.inputs.recap.source", "requires depends_on"},
|
||||
},
|
||||
{
|
||||
name: "dependency on unknown artifact fails validation",
|
||||
@@ -434,7 +434,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.artifact.session_recap
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.player_handout.depends_on[0] "session_recap" is not a configured artifact key`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.player_handout.depends_on[0]", "not a configured artifact"},
|
||||
},
|
||||
{
|
||||
name: "self dependency fails validation",
|
||||
@@ -448,7 +448,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
prompt_id: dnd.session_recap
|
||||
output_path: artifacts/session_recap.md
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts.session_recap.depends_on must not include itself",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.depends_on", "itself"},
|
||||
},
|
||||
{
|
||||
name: "configured dependency cycle fails validation",
|
||||
@@ -476,7 +476,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.artifact.artifact_a
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts dependencies must not contain cycles",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts dependencies", "cycles"},
|
||||
},
|
||||
{
|
||||
name: "disabled dependency cycle fails validation",
|
||||
@@ -494,7 +494,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
- artifact_a
|
||||
output_path: artifacts/b.md
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts dependencies must not contain cycles",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts dependencies", "cycles"},
|
||||
},
|
||||
{
|
||||
name: "artifact source typo fails validation",
|
||||
@@ -510,7 +510,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.artifact.session-recap
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: `pipeline.scriptorium.artifacts.player_handout.inputs.recap.source "narratio.artifact.session-recap" is unsupported`,
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.player_handout.inputs.recap.source", "unsupported"},
|
||||
},
|
||||
{
|
||||
name: "referenced disabled artifact missing output path fails validation",
|
||||
@@ -530,7 +530,7 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
source: narratio.artifact.session_recap
|
||||
required: true
|
||||
`,
|
||||
wantValidateErr: "pipeline.scriptorium.artifacts.session_recap.output_path is required when artifact is referenced",
|
||||
wantValidateErr: []string{"pipeline.scriptorium.artifacts.session_recap.output_path", "referenced"},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -558,12 +558,14 @@ func TestScriptoriumLoadAndValidate(t *testing.T) {
|
||||
}
|
||||
|
||||
err = Validate(cfg)
|
||||
if tt.wantValidateErr != "" {
|
||||
if len(tt.wantValidateErr) > 0 {
|
||||
if err == nil {
|
||||
t.Fatalf("expected validation error containing %q, got nil", tt.wantValidateErr)
|
||||
}
|
||||
if !strings.Contains(err.Error(), tt.wantValidateErr) {
|
||||
t.Fatalf("validation error = %q, want to contain %q", err.Error(), tt.wantValidateErr)
|
||||
for _, fragment := range tt.wantValidateErr {
|
||||
if !strings.Contains(err.Error(), fragment) {
|
||||
t.Fatalf("validation error = %q, want to contain %q", err.Error(), fragment)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@@ -596,16 +598,9 @@ func TestScriptoriumDiagnosticsAreIndependentOfMapInsertionOrder(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestScriptoriumLegacyTranscriptSourcesRejected(t *testing.T) {
|
||||
legacyTranscriptSources := []string{
|
||||
"narratio.transcript." + "merged",
|
||||
"narratio.transcript." + "full",
|
||||
"narratio.transcript." + "trimmed",
|
||||
}
|
||||
|
||||
for _, source := range legacyTranscriptSources {
|
||||
t.Run(source, func(t *testing.T) {
|
||||
pipelineYAML := testPipelineBaseYAML + `
|
||||
func TestScriptoriumUnsupportedTranscriptSourceRejected(t *testing.T) {
|
||||
source := "narratio.transcript." + "merged"
|
||||
pipelineYAML := testPipelineBaseYAML + `
|
||||
scriptorium:
|
||||
binary: scriptorium
|
||||
artifacts:
|
||||
@@ -618,17 +613,14 @@ scriptorium:
|
||||
source: ` + source + `
|
||||
required: true
|
||||
`
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, pipelineYAML, testSessionBaseYAML)
|
||||
cfg, err := Load(pipelinePath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
err = Validate(cfg)
|
||||
wantErr := `pipeline.scriptorium.artifacts.session_recap.inputs.transcript.source "` + source + `" is unsupported`
|
||||
if err == nil || !strings.Contains(err.Error(), wantErr) {
|
||||
t.Fatalf("Validate() error = %v, want to contain %q", err, wantErr)
|
||||
}
|
||||
})
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, pipelineYAML, testSessionBaseYAML)
|
||||
cfg, err := Load(pipelinePath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
err = Validate(cfg)
|
||||
if err == nil || !strings.Contains(err.Error(), "pipeline.scriptorium.artifacts.session_recap.inputs.transcript.source") || !strings.Contains(err.Error(), "unsupported") {
|
||||
t.Fatalf("Validate() error = %v, want source field and unsupported category", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user