281 lines
9.9 KiB
Go
281 lines
9.9 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/narratio/internal/artifacts"
|
|
"gitea.maximumdirect.net/eric/narratio/internal/manifest"
|
|
)
|
|
|
|
const (
|
|
sharedProviderName = "NARRATIO_SHARED_PROVIDER_KEY"
|
|
auditaOnlyName = "NARRATIO_AUDITA_ONLY_KEY"
|
|
sharedProviderKey = "synthetic-shared-provider-value"
|
|
auditaOnlyKey = "synthetic-audita-only-value"
|
|
)
|
|
|
|
func TestCommandsShareProviderCredentialAcrossAdapters(t *testing.T) {
|
|
configDir := t.TempDir()
|
|
workspace := t.TempDir()
|
|
secretsDir := filepath.Join(configDir, "secrets")
|
|
for name, value := range map[string]string{
|
|
sharedProviderName: sharedProviderKey,
|
|
auditaOnlyName: auditaOnlyKey,
|
|
} {
|
|
restoreEnvAfterTest(t, name)
|
|
if err := os.Unsetenv(name); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
mustWriteSecretFile(t, filepath.Join(secretsDir, name), value+"\n")
|
|
}
|
|
fixture := writeProviderProcessWrapper(t)
|
|
seriatim := writeSeriatimAppTestWrapper(t)
|
|
configPath := filepath.Join(configDir, "pipeline.yml")
|
|
campaignPath := writeAppTestCampaignConfig(t, configDir)
|
|
sessionPath := filepath.Join(configDir, "session.yml")
|
|
notariusConfig := filepath.Join(configDir, "notarius.yml")
|
|
mustWriteTestFile(t, notariusConfig, "pipelines: {}\n")
|
|
mustWriteTestFile(t, configPath, `workspace:
|
|
root: `+workspace+`
|
|
storage:
|
|
backend: local
|
|
whisperx:
|
|
transcribe_url: https://example.com/transcribe
|
|
seriatim:
|
|
binary: `+seriatim+`
|
|
audita:
|
|
binary: `+fixture+`
|
|
credentials: [`+sharedProviderName+`, `+auditaOnlyName+`]
|
|
scriptorium:
|
|
binary: `+fixture+`
|
|
credentials: [`+sharedProviderName+`]
|
|
render_debug: true
|
|
artifacts:
|
|
recap:
|
|
enabled: true
|
|
prompt_id: recap
|
|
output_path: artifacts/recap.md
|
|
inputs:
|
|
transcript:
|
|
source: narratio.transcript.final_trimmed
|
|
notarius:
|
|
enabled: true
|
|
binary: `+fixture+`
|
|
config_path: `+notariusConfig+`
|
|
pipeline_id: dnd-session
|
|
credentials: [`+sharedProviderName+`]
|
|
outputs:
|
|
npc_registry:
|
|
lane_id: npc-registry
|
|
media_type: application/json
|
|
schema_id: notarius.dnd.npc_registry
|
|
schema_version: v1
|
|
module_key: dnd/npc-registry
|
|
notification:
|
|
mode: noop
|
|
`)
|
|
mustWriteTestFile(t, sessionPath, `session_id: 2026-05-03
|
|
campaign: sample-campaign
|
|
inputs:
|
|
audio_dir: ./audio
|
|
speakers_file: ./speakers.yml
|
|
autocorrect_file: ./autocorrect.yml
|
|
glossary_file: ./glossary.yml
|
|
`)
|
|
|
|
var stdout, stderr bytes.Buffer
|
|
if code := Execute([]string{"config", "show", "--config", configPath}, &stdout, &stderr); code != 0 {
|
|
t.Fatalf("config show exit = %d; stderr=%q", code, stderr.String())
|
|
}
|
|
if !strings.Contains(stdout.String(), sharedProviderName) || !strings.Contains(stdout.String(), auditaOnlyName) {
|
|
t.Fatalf("config show omitted reference names: %q", stdout.String())
|
|
}
|
|
assertNoProviderValues(t, stdout.Bytes(), stderr.Bytes())
|
|
|
|
workRoot := filepath.Join(workspace, "work", "sample-campaign", "2026-05-03")
|
|
manifestPath := filepath.Join(workRoot, "manifest.json")
|
|
basePath := filepath.Join(workRoot, "transcripts", "base.json")
|
|
mustWriteTestFile(t, basePath, `{"schema":"seriatim-intermediate","segments":[]}`)
|
|
seedCurrentBuiltInEvidence(t, manifestPath, "2026-05-03", "sample-campaign", "merge", artifacts.TranscriptOutputKindBase, artifacts.ArtifactTranscriptBase, basePath)
|
|
mustWriteTestFile(t, filepath.Join(workRoot, "inputs", "glossary.yml"), "[]\n")
|
|
args := []string{"--config", configPath, "--campaign-file", campaignPath, "--session", sessionPath}
|
|
stdout.Reset()
|
|
stderr.Reset()
|
|
if code := Execute(append([]string{"run-stage", "polish", "2026-05-03", "--force"}, args...), &stdout, &stderr); code != 0 {
|
|
t.Fatalf("polish exit = %d; stderr=%q", code, stderr.String())
|
|
}
|
|
assertNoProviderValues(t, stdout.Bytes(), stderr.Bytes())
|
|
m, err := (&manifest.LocalStore{}).Load(context.Background(), manifestPath)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if polish := m.Stages["polish"]; polish == nil || len(polish.Outputs) != 1 || len(polish.Logs) != 5 {
|
|
t.Fatalf("polish did not retain canonical transcript and ordinary reports: %#v", polish)
|
|
}
|
|
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
|
|
found := false
|
|
for _, path := range m.Stages["polish"].Logs {
|
|
found = found || filepath.Base(path) == name
|
|
}
|
|
if !found {
|
|
t.Fatalf("missing polish report %q", name)
|
|
}
|
|
}
|
|
|
|
trimmedPath := filepath.Join(workRoot, "transcripts", "final.trimmed.json")
|
|
mustWriteTestFile(t, trimmedPath, `{"segments":[]}`)
|
|
setCurrentBuiltInEvidence(t, m, "trim", artifacts.TranscriptOutputKindFinalTrimmed, artifacts.ArtifactTranscriptFinalTrimmed, trimmedPath)
|
|
if err := (&manifest.LocalStore{}).Save(context.Background(), manifestPath, m); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
stdout.Reset()
|
|
stderr.Reset()
|
|
if code := Execute(append([]string{"run-stage", "extract", "2026-05-03", "--force"}, args...), &stdout, &stderr); code != 0 {
|
|
t.Fatalf("extract exit = %d; stderr=%q", code, stderr.String())
|
|
}
|
|
assertNoProviderValues(t, stdout.Bytes(), stderr.Bytes())
|
|
if !strings.Contains(stdout.String(), "stage=extract executed=1") {
|
|
t.Fatalf("extract did not execute: %q", stdout.String())
|
|
}
|
|
redactedLogs := 0
|
|
stdout.Reset()
|
|
stderr.Reset()
|
|
if code := Execute(append([]string{"analyze", "2026-05-03"}, args...), &stdout, &stderr); code != 0 {
|
|
t.Fatalf("analyze exit = %d; stderr=%q", code, stderr.String())
|
|
}
|
|
assertNoProviderValues(t, stdout.Bytes(), stderr.Bytes())
|
|
if err := filepath.WalkDir(workRoot, func(path string, entry os.DirEntry, walkErr error) error {
|
|
if walkErr != nil || entry.IsDir() {
|
|
return walkErr
|
|
}
|
|
data, err := os.ReadFile(path)
|
|
if err == nil {
|
|
assertNoProviderValues(t, data)
|
|
if strings.HasSuffix(path, ".stderr.log") && bytes.Contains(data, []byte("<redacted>")) {
|
|
redactedLogs++
|
|
}
|
|
}
|
|
return err
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if redactedLogs < 4 {
|
|
t.Fatalf("redacted subprocess logs = %d, want Audita, Notarius and Scriptorium run/render", redactedLogs)
|
|
}
|
|
}
|
|
|
|
func assertNoProviderValues(t *testing.T, content ...[]byte) {
|
|
t.Helper()
|
|
for _, data := range content {
|
|
if bytes.Contains(data, []byte(sharedProviderKey)) || bytes.Contains(data, []byte(auditaOnlyKey)) {
|
|
t.Fatal("credential value appeared in inspection or workspace output")
|
|
}
|
|
}
|
|
}
|
|
|
|
func writeProviderProcessWrapper(t *testing.T) string {
|
|
t.Helper()
|
|
exe, err := os.Executable()
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
path := filepath.Join(t.TempDir(), "provider-process.sh")
|
|
if err := os.WriteFile(path, []byte("#!/bin/sh\nexec \""+exe+"\" -test.run=TestProviderCredentialProcessFixture -- \"$@\"\n"), 0o755); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return path
|
|
}
|
|
|
|
func TestProviderCredentialProcessFixture(t *testing.T) {
|
|
if !appHelperInvocation() {
|
|
return
|
|
}
|
|
start := -1
|
|
for i, arg := range os.Args {
|
|
if arg == "--" {
|
|
start = i + 1
|
|
break
|
|
}
|
|
}
|
|
if start < 0 || start >= len(os.Args) {
|
|
os.Exit(2)
|
|
}
|
|
args := os.Args[start:]
|
|
if os.Getenv(sharedProviderName) != sharedProviderKey {
|
|
_, _ = os.Stderr.WriteString("shared credential not forwarded\n")
|
|
os.Exit(2)
|
|
}
|
|
if appSeriatimFlagValue(args, "--prompt") != "" {
|
|
if _, present := os.LookupEnv(auditaOnlyName); present {
|
|
os.Exit(2)
|
|
}
|
|
if appSeriatimFlagValue(args, "--api-key-env") != "" {
|
|
os.Exit(2)
|
|
}
|
|
output := appSeriatimFlagValue(args, "--out")
|
|
if err := os.MkdirAll(filepath.Dir(output), 0o755); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
if err := os.WriteFile(output, []byte("{}\n"), 0o644); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
_, _ = os.Stdout.WriteString(sharedProviderKey + "\n")
|
|
_, _ = os.Stderr.WriteString(sharedProviderKey + "\n")
|
|
os.Exit(0)
|
|
}
|
|
switch args[0] {
|
|
case "process":
|
|
if os.Getenv(auditaOnlyName) != auditaOnlyKey {
|
|
_, _ = os.Stderr.WriteString("Audita-only credential not forwarded\n")
|
|
os.Exit(2)
|
|
}
|
|
output := appSeriatimFlagValue(args, "--output")
|
|
if output == "" {
|
|
os.Exit(2)
|
|
}
|
|
if err := os.MkdirAll(filepath.Dir(output), 0o755); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
if err := os.WriteFile(output, []byte(`{"schema":"audita.processed.v1","segments":[]}`), 0o644); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
for _, name := range []string{"errors.yml", "recoveries.yml", "report.md"} {
|
|
if err := os.WriteFile(filepath.Join(filepath.Dir(output), name), []byte("status: completed\n"), 0o644); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
case "run":
|
|
if _, present := os.LookupEnv(auditaOnlyName); present {
|
|
_, _ = os.Stderr.WriteString("Audita-only credential reached Notarius\n")
|
|
os.Exit(2)
|
|
}
|
|
root := appSeriatimFlagValue(args, "--output-dir")
|
|
bundle := filepath.Join(root, "fixture-run")
|
|
if err := os.MkdirAll(filepath.Join(bundle, "lanes"), 0o755); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
for name, value := range map[string]string{
|
|
"manifest.json": "{}", "rejected.json": `{"rejected":[]}`,
|
|
"warnings.json": `{"schema_version":"notarius.warnings.v2","group_count":0,"occurrence_count":0,"groups":[]}`,
|
|
"diagnostics.json": `{"schema_version":"notarius.diagnostics.v1","group_count":0,"occurrence_count":0,"truncated":false,"unrepresented_occurrence_count":0,"groups":[]}`,
|
|
"lanes/npc.json": `{"records":[]}`,
|
|
"index.json": `{"manifest_file":"manifest.json","output_files":[{"lane_id":"npc-registry","file":"lanes/npc.json","media_type":"application/json","module_key":"dnd/npc-registry","schema_id":"notarius.dnd.npc_registry","schema_version":"v1"}],"rejected_file":"rejected.json","warnings_file":"warnings.json","diagnostics_file":"diagnostics.json"}`,
|
|
} {
|
|
if err := os.WriteFile(filepath.Join(bundle, filepath.FromSlash(name)), []byte(value), 0o644); err != nil {
|
|
os.Exit(2)
|
|
}
|
|
}
|
|
_, _ = os.Stdout.WriteString(fmt.Sprintf(`{"schema_version":"notarius.run-result.v2","run_id":"fixture-run","pipeline_id":"dnd-session","output_directory":%q,"index_file":"index.json","normalized_output_count":1,"rejected_output_count":0,"warning_group_count":0,"warning_occurrence_count":0,"diagnostic_group_count":0,"diagnostic_occurrence_count":0,"diagnostics_truncated":false,"validation_status":"approved"}`, bundle))
|
|
default:
|
|
os.Exit(2)
|
|
}
|
|
_, _ = os.Stderr.WriteString(sharedProviderKey + "\n")
|
|
os.Exit(0)
|
|
}
|