Verify shared provider credentials across both adapters

This commit is contained in:
2026-09-25 16:06:02 +00:00
parent e5c1029165
commit fae18bf4a7
4 changed files with 260 additions and 5 deletions

View File

@@ -0,0 +1,244 @@
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+`]
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
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 < 2 {
t.Fatalf("redacted subprocess logs = %d, want Audita and Notarius", 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)
}
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)
}