212 lines
7.8 KiB
Go
212 lines
7.8 KiB
Go
package app
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"errors"
|
|
"path/filepath"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
const artifactCatalogHeader = "key\tsource\tdefinitions\tenabled\toutput\tdependencies\tfamily\tcharacter\tfamily_source\tparty_source\n"
|
|
|
|
func TestConfigArtifactsReportsOrdinaryCatalogWithoutCampaign(t *testing.T) {
|
|
dir := t.TempDir()
|
|
pipelinePath := filepath.Join(dir, "pipeline.yml")
|
|
mustWriteTestFile(t, pipelinePath, `workspace:
|
|
root: `+filepath.Join(dir, "workspace")+`
|
|
whisperx:
|
|
transcribe_url: https://transcription.example.com/transcribe
|
|
scriptorium:
|
|
binary: scriptorium
|
|
artifacts:
|
|
zeta:
|
|
enabled: false
|
|
output_path: artifacts/zeta.md
|
|
alpha:
|
|
enabled: false
|
|
output_path: artifacts/alpha.md
|
|
inputs:
|
|
upstream: {source: narratio.artifact.zeta, required: true}
|
|
`)
|
|
|
|
var out bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), []string{"--config", pipelinePath}, &out); err != nil {
|
|
t.Fatalf("ConfigArtifacts() error = %v", err)
|
|
}
|
|
lines := strings.Split(strings.TrimSuffix(out.String(), "\n"), "\n")
|
|
if len(lines) != 3 || !strings.HasPrefix(lines[1], "alpha\t") || !strings.HasPrefix(lines[2], "zeta\t") {
|
|
t.Fatalf("catalog order/output = %q", out.String())
|
|
}
|
|
alpha := strings.Split(lines[1], "\t")
|
|
if len(alpha) != 10 {
|
|
t.Fatalf("alpha fields = %#v", alpha)
|
|
}
|
|
if alpha[1] != "narratio.artifact.alpha" || alpha[2] != pipelinePath || alpha[3] != "false" || alpha[4] != "artifacts/alpha.md" || alpha[5] != "zeta" {
|
|
t.Fatalf("alpha record = %#v", alpha)
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsEmptyCatalogWritesOnlyHeader(t *testing.T) {
|
|
dir := t.TempDir()
|
|
pipelinePath := filepath.Join(dir, "pipeline.yml")
|
|
mustWriteTestFile(t, pipelinePath, "workspace:\n root: "+filepath.Join(dir, "workspace")+"\nwhisperx:\n transcribe_url: https://transcription.example.com/transcribe\n")
|
|
var out bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), []string{"--config", pipelinePath}, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if out.String() != artifactCatalogHeader {
|
|
t.Fatalf("empty catalog = %q, want header only", out.String())
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsReportsGeneratedFamilyProvenance(t *testing.T) {
|
|
pipelinePath, campaignPath := writeInspectionFamilyConfig(t)
|
|
var out bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), []string{"--config", pipelinePath, "--campaign-file", campaignPath}, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
records := catalogRecordsByKey(t, out.String())
|
|
meta := records["character_meta_arannis"]
|
|
note := records["character_note_arannis"]
|
|
partyPath := filepath.Join(filepath.Dir(campaignPath), "party.yml")
|
|
if len(meta) != 10 || meta[6] != "character_meta" || meta[7] != "arannis" || meta[8] != pipelinePath || meta[9] != partyPath {
|
|
t.Fatalf("generated meta record = %#v", meta)
|
|
}
|
|
if len(note) != 10 || note[5] != "character_meta_arannis" || note[6] != "character_note" {
|
|
t.Fatalf("generated note record = %#v", note)
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsReportsOverlayFamilyContributors(t *testing.T) {
|
|
pipelinePath, campaignPath := writeInspectionFamilyConfig(t)
|
|
overlayPath := filepath.Join(filepath.Dir(pipelinePath), "overlays", "testing.yml")
|
|
mustWriteTestFile(t, overlayPath, `scriptorium:
|
|
artifact_families:
|
|
character_note:
|
|
prompt_id: dnd.character_note.testing
|
|
`)
|
|
|
|
args := []string{"--config", pipelinePath, "--campaign-file", campaignPath, "--overlay", "testing"}
|
|
var catalog bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), args, &catalog); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
records := catalogRecordsByKey(t, catalog.String())
|
|
if got := strings.Split(records["character_note_arannis"][2], ","); !reflect.DeepEqual(got, []string{overlayPath, pipelinePath}) {
|
|
t.Fatalf("generated family definitions = %#v", got)
|
|
}
|
|
|
|
var sources bytes.Buffer
|
|
if err := ConfigSources(context.Background(), args, &sources); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := "scriptorium.artifacts.character_note_arannis.prompt_id\tfamily\t" + overlayPath
|
|
if !strings.Contains(sources.String(), want) {
|
|
t.Fatalf("sources output missing overlay family contributor %q:\n%s", want, sources.String())
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsReportsBaseAndOverlayContributors(t *testing.T) {
|
|
dir := t.TempDir()
|
|
pipelinePath := filepath.Join(dir, "pipeline.yml")
|
|
overlayPath := filepath.Join(dir, "overlays", "testing.yml")
|
|
mustWriteTestFile(t, pipelinePath, `workspace:
|
|
root: /srv/narratio
|
|
whisperx:
|
|
transcribe_url: https://transcription.example.com/transcribe
|
|
scriptorium:
|
|
binary: scriptorium
|
|
artifacts:
|
|
notes:
|
|
enabled: false
|
|
output_path: artifacts/notes.md
|
|
`)
|
|
mustWriteTestFile(t, overlayPath, `scriptorium:
|
|
artifacts:
|
|
notes:
|
|
enabled: true
|
|
prompt_id: notes
|
|
overlay_only:
|
|
enabled: false
|
|
output_path: artifacts/overlay-only.md
|
|
`)
|
|
|
|
var out bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), []string{"--config", pipelinePath, "--overlay", "testing"}, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
records := catalogRecordsByKey(t, out.String())
|
|
if got := strings.Split(records["notes"][2], ","); !reflect.DeepEqual(got, []string{overlayPath, pipelinePath}) {
|
|
t.Fatalf("notes definitions = %#v", got)
|
|
}
|
|
if got := records["overlay_only"][2]; got != overlayPath {
|
|
t.Fatalf("overlay-only definition = %q, want %q", got, overlayPath)
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsIdentityDoesNotDependOnDefinitionFilename(t *testing.T) {
|
|
inlineDir := t.TempDir()
|
|
inlinePath := filepath.Join(inlineDir, "pipeline.yml")
|
|
mustWriteTestFile(t, inlinePath, artifactIdentityPipeline("", "recap"))
|
|
importDir := t.TempDir()
|
|
importPath := filepath.Join(importDir, "pipeline.yml")
|
|
mustWriteTestFile(t, importPath, artifactIdentityPipeline("artifacts.yml", ""))
|
|
mustWriteTestFile(t, filepath.Join(importDir, "artifacts.yml"), "scriptorium:\n artifacts:\n recap:\n enabled: false\n output_path: artifacts/recap.md\n")
|
|
|
|
for _, path := range []string{inlinePath, importPath} {
|
|
var out bytes.Buffer
|
|
if err := ConfigArtifacts(context.Background(), []string{"--config", path}, &out); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if got := catalogRecordsByKey(t, out.String())["recap"][1]; got != "narratio.artifact.recap" {
|
|
t.Fatalf("source identity for %s = %q", path, got)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestConfigArtifactsPropagatesWriterErrorsAndEscapesFields(t *testing.T) {
|
|
dir := t.TempDir()
|
|
pipelinePath := filepath.Join(dir, "pipeline.yml")
|
|
mustWriteTestFile(t, pipelinePath, "workspace:\n root: /srv/narratio\nwhisperx:\n transcribe_url: https://transcription.example.com/transcribe\n")
|
|
want := errors.New("write rejected")
|
|
err := ConfigArtifacts(context.Background(), []string{"--config", pipelinePath}, rejectingCatalogWriter{err: want})
|
|
if !errors.Is(err, want) {
|
|
t.Fatalf("ConfigArtifacts() error = %v, want writer error", err)
|
|
}
|
|
if got := escapeCatalogField("a\\b\tc\nd\r"); got != `a\\b\tc\nd\r` {
|
|
t.Fatalf("escaped field = %q", got)
|
|
}
|
|
}
|
|
|
|
func catalogRecordsByKey(t *testing.T, output string) map[string][]string {
|
|
t.Helper()
|
|
lines := strings.Split(strings.TrimSuffix(output, "\n"), "\n")
|
|
if len(lines) == 0 || lines[0]+"\n" != artifactCatalogHeader {
|
|
t.Fatalf("catalog header = %q", output)
|
|
}
|
|
records := make(map[string][]string, len(lines)-1)
|
|
for _, line := range lines[1:] {
|
|
fields := strings.Split(line, "\t")
|
|
records[fields[0]] = fields
|
|
}
|
|
return records
|
|
}
|
|
|
|
func artifactIdentityPipeline(importPath, inlineKey string) string {
|
|
document := "workspace:\n root: /srv/narratio\nwhisperx:\n transcribe_url: https://transcription.example.com/transcribe\nscriptorium:\n binary: scriptorium\n"
|
|
if importPath != "" {
|
|
document = "configkit:\n imports:\n - path: " + importPath + "\n" + document
|
|
}
|
|
if inlineKey != "" {
|
|
document += " artifacts:\n " + inlineKey + ":\n enabled: false\n output_path: artifacts/recap.md\n"
|
|
}
|
|
return document
|
|
}
|
|
|
|
type rejectingCatalogWriter struct{ err error }
|
|
|
|
func (w rejectingCatalogWriter) Write([]byte) (int, error) { return 0, w.err }
|