Implemented shared S3 audio caching for prepare and restore --include-audio
This commit is contained in:
67
internal/config/cache_config_test.go
Normal file
67
internal/config/cache_config_test.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCacheDefaults(t *testing.T) {
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, `workspace:
|
||||
root: /tmp/narratio
|
||||
whisperx:
|
||||
transcribe_url: https://example.com/transcribe
|
||||
analyzer:
|
||||
timeout: 20m
|
||||
notification:
|
||||
timeout: 10s
|
||||
`, `session_id: 2026-05-03
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
`)
|
||||
cfg, err := Load(pipelinePath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
if cfg.Pipeline.Cache.Root != DefaultCacheRoot {
|
||||
t.Fatalf("cache.root = %q, want %q", cfg.Pipeline.Cache.Root, DefaultCacheRoot)
|
||||
}
|
||||
if cfg.Pipeline.Cache.S3Audio == nil || *cfg.Pipeline.Cache.S3Audio != DefaultCacheS3Audio {
|
||||
t.Fatalf("cache.s3_audio = %v, want %v", cfg.Pipeline.Cache.S3Audio, DefaultCacheS3Audio)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheStrictDecodeRejectsUnknownFields(t *testing.T) {
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, `workspace:
|
||||
root: /tmp/narratio
|
||||
cache:
|
||||
root: /var/cache/narratio
|
||||
unknown: true
|
||||
`, `session_id: 2026-05-03
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
`)
|
||||
_, err := Load(pipelinePath, sessionPath)
|
||||
if err == nil || !strings.Contains(err.Error(), "strict decode failed") {
|
||||
t.Fatalf("Load() error = %v, want strict decode failed", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestCacheValidationRequiresRootWhenS3AudioEnabled(t *testing.T) {
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, `workspace:
|
||||
root: /tmp/narratio
|
||||
cache:
|
||||
root: " "
|
||||
s3_audio: true
|
||||
`, `session_id: 2026-05-03
|
||||
inputs:
|
||||
audio_dir: ./audio
|
||||
`)
|
||||
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.cache.root is required") {
|
||||
t.Fatalf("Validate() error = %v, want cache root error", err)
|
||||
}
|
||||
}
|
||||
@@ -19,6 +19,7 @@ type PipelineConfig struct {
|
||||
Workspace WorkspaceConfig `yaml:"workspace"`
|
||||
Storage StorageConfig `yaml:"storage"`
|
||||
Spool SpoolConfig `yaml:"spool"`
|
||||
Cache CacheConfig `yaml:"cache"`
|
||||
Archive *ArchiveConfig `yaml:"archive"`
|
||||
Secrets *SecretsConfig `yaml:"secrets"`
|
||||
WhisperX WhisperXConfig `yaml:"whisperx"`
|
||||
@@ -90,6 +91,12 @@ type SpoolConfig struct {
|
||||
DeleteAudioAfterArchive bool `yaml:"delete_audio_after_archive"`
|
||||
}
|
||||
|
||||
// CacheConfig configures durable local caches for reusable remote inputs.
|
||||
type CacheConfig struct {
|
||||
Root string `yaml:"root"`
|
||||
S3Audio *bool `yaml:"s3_audio"`
|
||||
}
|
||||
|
||||
// ArchiveConfig configures archive behavior and artifact promotions.
|
||||
type ArchiveConfig struct {
|
||||
Enabled *bool `yaml:"enabled"`
|
||||
|
||||
@@ -14,6 +14,8 @@ const (
|
||||
DefaultStorageS3RootPrefix = "dnd"
|
||||
DefaultWorkspaceRoot = "/var/lib/narratio"
|
||||
DefaultSpoolRoot = "/var/spool/narratio"
|
||||
DefaultCacheRoot = "/var/cache/narratio"
|
||||
DefaultCacheS3Audio = true
|
||||
|
||||
DefaultWhisperXLanguage = "en"
|
||||
DefaultWhisperXTimeout = "30m"
|
||||
|
||||
@@ -361,6 +361,7 @@ func applyPipelineDefaults(cfg *PipelineConfig) {
|
||||
applyWorkspaceDefaults(&cfg.Workspace)
|
||||
applyStorageDefaults(&cfg.Storage)
|
||||
applySpoolDefaults(&cfg.Spool)
|
||||
applyCacheDefaults(&cfg.Cache)
|
||||
applyArchiveDefaults(&cfg.Archive)
|
||||
applyWhisperXDefaults(&cfg.WhisperX)
|
||||
applySeriatimDefaults(&cfg.Seriatim)
|
||||
@@ -409,6 +410,18 @@ func applySpoolDefaults(cfg *SpoolConfig) {
|
||||
}
|
||||
}
|
||||
|
||||
func applyCacheDefaults(cfg *CacheConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
}
|
||||
if cfg.Root == "" {
|
||||
cfg.Root = DefaultCacheRoot
|
||||
}
|
||||
if cfg.S3Audio == nil {
|
||||
cfg.S3Audio = boolPtr(DefaultCacheS3Audio)
|
||||
}
|
||||
}
|
||||
|
||||
func applyArchiveDefaults(cfg **ArchiveConfig) {
|
||||
if cfg == nil {
|
||||
return
|
||||
|
||||
@@ -63,6 +63,9 @@ func validatePipeline(cfg *PipelineConfig) error {
|
||||
if err := validateSpool(cfg.Spool); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateCache(cfg.Cache); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := validateArchive(cfg.Archive, cfg.Scriptorium); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -120,6 +123,13 @@ func validateSpool(cfg SpoolConfig) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateCache(cfg CacheConfig) error {
|
||||
if cfg.S3Audio != nil && *cfg.S3Audio && strings.TrimSpace(cfg.Root) == "" {
|
||||
return fmt.Errorf("pipeline.cache.root is required when pipeline.cache.s3_audio is true")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func validateArchive(cfg *ArchiveConfig, scriptorium *ScriptoriumConfig) error {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
|
||||
Reference in New Issue
Block a user