207 lines
8.4 KiB
Go
207 lines
8.4 KiB
Go
package artifacts
|
|
|
|
import (
|
|
"path/filepath"
|
|
"testing"
|
|
)
|
|
|
|
func TestSessionWorkDirForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
got := SessionWorkDirForCampaign(root, "forsaken", "2026-04-19")
|
|
want := filepath.Join(root, "work", "forsaken", "2026-04-19")
|
|
if got != want {
|
|
t.Fatalf("SessionWorkDirForCampaign() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionManifestPathForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
got := SessionManifestPathForCampaign(root, "forsaken", "2026-04-19")
|
|
want := filepath.Join(root, "work", "forsaken", "2026-04-19", "manifest.json")
|
|
if got != want {
|
|
t.Fatalf("SessionManifestPathForCampaign() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionRunRootAndStageDirForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
runID := "20260515T031522Z-a1b2c3d4"
|
|
runRoot := SessionRunRootForCampaign(root, "forsaken", "2026-04-19", runID)
|
|
wantRoot := filepath.Join(root, "work", "forsaken", "2026-04-19", "runs", runID)
|
|
if runRoot != wantRoot {
|
|
t.Fatalf("SessionRunRootForCampaign() = %q, want %q", runRoot, wantRoot)
|
|
}
|
|
|
|
stageDir := SessionRunStageDirForCampaign(root, "forsaken", "2026-04-19", runID, "transcribe")
|
|
wantStage := filepath.Join(root, "work", "forsaken", "2026-04-19", "runs", runID, "transcribe")
|
|
if stageDir != wantStage {
|
|
t.Fatalf("SessionRunStageDirForCampaign() = %q, want %q", stageDir, wantStage)
|
|
}
|
|
}
|
|
|
|
func TestSessionRunManifestPathForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
runID := "20260515T031522Z-a1b2c3d4"
|
|
got := SessionRunManifestPathForCampaign(root, "forsaken", "2026-04-19", runID)
|
|
want := filepath.Join(root, "work", "forsaken", "2026-04-19", "runs", runID, "manifest.json")
|
|
if got != want {
|
|
t.Fatalf("SessionRunManifestPathForCampaign() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionNotariusPathsForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
campaign := "forsaken"
|
|
sessionID := "2026-04-19"
|
|
runID := "20260515T031522Z-a1b2c3d4"
|
|
extractDir := filepath.Join(root, "work", campaign, sessionID, "runs", runID, "extract")
|
|
|
|
tests := []struct {
|
|
name string
|
|
got string
|
|
want string
|
|
}{
|
|
{name: "extract directory", got: SessionRunExtractDirForCampaign(root, campaign, sessionID, runID), want: extractDir},
|
|
{name: "receipt", got: SessionRunNotariusReceiptPathForCampaign(root, campaign, sessionID, runID), want: filepath.Join(extractDir, "notarius.receipt.json")},
|
|
{name: "stderr", got: SessionRunNotariusLogPathForCampaign(root, campaign, sessionID, runID), want: filepath.Join(extractDir, "notarius.stderr.log")},
|
|
{name: "output root", got: SessionRunNotariusOutputRootForCampaign(root, campaign, sessionID, runID), want: filepath.Join(extractDir, "notarius-output")},
|
|
{name: "durable bundle", got: SessionNotariusBundleDirForCampaign(root, campaign, sessionID, runID), want: filepath.Join(root, "work", campaign, sessionID, "artifacts", "notarius", runID)},
|
|
}
|
|
for _, test := range tests {
|
|
t.Run(test.name, func(t *testing.T) {
|
|
if test.got != test.want {
|
|
t.Fatalf("path = %q, want %q", test.got, test.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestSessionPreviousPathsForCampaign(t *testing.T) {
|
|
root := "/tmp/workspace"
|
|
previousDir := SessionPreviousDirForCampaign(root, "forsaken", "2026-04-19")
|
|
wantPreviousDir := filepath.Join(root, "work", "forsaken", "2026-04-19", "previous")
|
|
if previousDir != wantPreviousDir {
|
|
t.Fatalf("SessionPreviousDirForCampaign() = %q, want %q", previousDir, wantPreviousDir)
|
|
}
|
|
|
|
manifestPath := SessionPreviousManifestPathForCampaign(root, "forsaken", "2026-04-19")
|
|
wantManifestPath := filepath.Join(root, "work", "forsaken", "2026-04-19", "previous", "manifest.json")
|
|
if manifestPath != wantManifestPath {
|
|
t.Fatalf("SessionPreviousManifestPathForCampaign() = %q, want %q", manifestPath, wantManifestPath)
|
|
}
|
|
|
|
artifactsDir := SessionPreviousArtifactsDirForCampaign(root, "forsaken", "2026-04-19")
|
|
wantArtifactsDir := filepath.Join(root, "work", "forsaken", "2026-04-19", "previous", "artifacts")
|
|
if artifactsDir != wantArtifactsDir {
|
|
t.Fatalf("SessionPreviousArtifactsDirForCampaign() = %q, want %q", artifactsDir, wantArtifactsDir)
|
|
}
|
|
|
|
artifactPath := SessionPreviousArtifactPathForCampaign(root, "forsaken", "2026-04-19", "session_recap.md")
|
|
wantArtifactPath := filepath.Join(root, "work", "forsaken", "2026-04-19", "previous", "artifacts", "session_recap.md")
|
|
if artifactPath != wantArtifactPath {
|
|
t.Fatalf("SessionPreviousArtifactPathForCampaign() = %q, want %q", artifactPath, wantArtifactPath)
|
|
}
|
|
|
|
previousRelativeArtifactPath := SessionPreviousArtifactPathForCampaign(root, "forsaken", "2026-04-19", "artifacts/session_recap.md")
|
|
if previousRelativeArtifactPath != wantArtifactPath {
|
|
t.Fatalf("SessionPreviousArtifactPathForCampaign(previous-relative) = %q, want %q", previousRelativeArtifactPath, wantArtifactPath)
|
|
}
|
|
}
|
|
|
|
func TestSessionPreviousPathsFromSessionPaths(t *testing.T) {
|
|
paths := buildSessionPaths("/tmp/workspace", "forsaken", "2026-04-19")
|
|
if got := SessionPreviousDir(paths); got != paths.PreviousDir {
|
|
t.Fatalf("SessionPreviousDir() = %q, want %q", got, paths.PreviousDir)
|
|
}
|
|
if got := SessionPreviousManifestPath(paths); got != paths.PreviousManifestPath {
|
|
t.Fatalf("SessionPreviousManifestPath() = %q, want %q", got, paths.PreviousManifestPath)
|
|
}
|
|
if got := SessionPreviousArtifactsDir(paths); got != paths.PreviousArtifactsDir {
|
|
t.Fatalf("SessionPreviousArtifactsDir() = %q, want %q", got, paths.PreviousArtifactsDir)
|
|
}
|
|
|
|
got := SessionPreviousArtifactPath(paths, "quest_log.json")
|
|
want := filepath.Join(paths.PreviousArtifactsDir, "quest_log.json")
|
|
if got != want {
|
|
t.Fatalf("SessionPreviousArtifactPath() = %q, want %q", got, want)
|
|
}
|
|
|
|
got = SessionPreviousArtifactPath(paths, "artifacts/session_recap.md")
|
|
want = filepath.Join(paths.PreviousArtifactsDir, "session_recap.md")
|
|
if got != want {
|
|
t.Fatalf("SessionPreviousArtifactPath(previous-relative) = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionSpoolAudioDir(t *testing.T) {
|
|
root := "/var/spool/narratio"
|
|
got := SessionSpoolAudioDir(root, "forsaken", "2026-04-19", "20260515T031522Z-a1b2c3d4")
|
|
want := filepath.Join(root, "forsaken", "2026-04-19", "20260515T031522Z-a1b2c3d4", "audio")
|
|
if got != want {
|
|
t.Fatalf("SessionSpoolAudioDir() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionSpoolDir(t *testing.T) {
|
|
root := "/var/spool/narratio"
|
|
got := SessionSpoolDir(root, "forsaken", "2026-04-19")
|
|
want := filepath.Join(root, "forsaken", "2026-04-19")
|
|
if got != want {
|
|
t.Fatalf("SessionSpoolDir() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestSessionSpoolRestoreAudioDir(t *testing.T) {
|
|
root := "/var/spool/narratio"
|
|
got := SessionSpoolRestoreAudioDir(root, "forsaken", "2026-04-19")
|
|
want := filepath.Join(root, "forsaken", "2026-04-19", "restore", "audio")
|
|
if got != want {
|
|
t.Fatalf("SessionSpoolRestoreAudioDir() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestS3AudioCachePath(t *testing.T) {
|
|
got, err := S3AudioCachePath("/var/cache/narratio", "my-dnd-archive", "dnd/campaigns/forsaken/sessions/2026-04-19/audio/alice.flac")
|
|
if err != nil {
|
|
t.Fatalf("S3AudioCachePath() error = %v", err)
|
|
}
|
|
want := filepath.Join("/var/cache/narratio", "s3", "my-dnd-archive", "dnd", "campaigns", "forsaken", "sessions", "2026-04-19", "audio", "alice.flac")
|
|
if got != want {
|
|
t.Fatalf("S3AudioCachePath() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestS3AudioCacheNamespaceDir(t *testing.T) {
|
|
got, err := S3AudioCacheNamespaceDir("/var/cache/narratio", "my-dnd-archive", "dnd")
|
|
if err != nil {
|
|
t.Fatalf("S3AudioCacheNamespaceDir() error = %v", err)
|
|
}
|
|
want := filepath.Join("/var/cache/narratio", "s3", "my-dnd-archive", "dnd", "campaigns")
|
|
if got != want {
|
|
t.Fatalf("S3AudioCacheNamespaceDir() = %q, want %q", got, want)
|
|
}
|
|
}
|
|
|
|
func TestS3AudioCachePathRejectsUnsafeInputs(t *testing.T) {
|
|
tests := []struct {
|
|
name string
|
|
root string
|
|
bucket string
|
|
key string
|
|
}{
|
|
{name: "empty root", root: "", bucket: "bucket", key: "audio/a.flac"},
|
|
{name: "empty bucket", root: "/cache", bucket: "", key: "audio/a.flac"},
|
|
{name: "bucket slash", root: "/cache", bucket: "bad/bucket", key: "audio/a.flac"},
|
|
{name: "empty key", root: "/cache", bucket: "bucket", key: ""},
|
|
{name: "escaping key", root: "/cache", bucket: "bucket", key: "../audio/a.flac"},
|
|
{name: "absolute key", root: "/cache", bucket: "bucket", key: "/audio/a.flac"},
|
|
}
|
|
for _, tt := range tests {
|
|
t.Run(tt.name, func(t *testing.T) {
|
|
if got, err := S3AudioCachePath(tt.root, tt.bucket, tt.key); err == nil {
|
|
t.Fatalf("S3AudioCachePath() = %q, want error", got)
|
|
}
|
|
})
|
|
}
|
|
}
|