Harden configuration validation
This commit is contained in:
114
internal/config/validation_duration_test.go
Normal file
114
internal/config/validation_duration_test.go
Normal file
@@ -0,0 +1,114 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestValidateDurationsRequirePositiveValues(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
set func(*PipelineConfig, string)
|
||||
want string
|
||||
}{
|
||||
{name: "whisperx timeout", set: func(p *PipelineConfig, value string) { p.WhisperX.Timeout = value }, want: "pipeline.whisperx.timeout"},
|
||||
{name: "whisperx retry delay", set: func(p *PipelineConfig, value string) { p.WhisperX.RetryDelay = value }, want: "pipeline.whisperx.retry_delay"},
|
||||
{name: "seriatim timeout", set: func(p *PipelineConfig, value string) { p.Seriatim.Timeout = value }, want: "pipeline.seriatim.timeout"},
|
||||
{name: "audita timeout", set: func(p *PipelineConfig, value string) { p.Audita.Timeout = value }, want: "pipeline.audita.timeout"},
|
||||
{name: "scriptorium timeout", set: func(p *PipelineConfig, value string) { p.Scriptorium.Timeout = value }, want: "pipeline.scriptorium.timeout"},
|
||||
{name: "scriptorium artifact timeout", set: func(p *PipelineConfig, value string) {
|
||||
if p.Scriptorium.Artifacts == nil {
|
||||
p.Scriptorium.Artifacts = map[string]ScriptoriumArtifactConfig{}
|
||||
}
|
||||
p.Scriptorium.Artifacts["session_recap"] = ScriptoriumArtifactConfig{Timeout: value}
|
||||
}, want: "pipeline.scriptorium.artifacts.session_recap.timeout"},
|
||||
{name: "trim bounds timeout", set: func(p *PipelineConfig, value string) { p.Trim.Bounds.Timeout = value }, want: "pipeline.trim.bounds.timeout"},
|
||||
{name: "notification timeout", set: func(p *PipelineConfig, value string) { p.Notification.Timeout = value }, want: "pipeline.notification.timeout"},
|
||||
}
|
||||
|
||||
for _, value := range []string{"0s", "-1ms"} {
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name+"/"+value, func(t *testing.T) {
|
||||
cfg := loadedValidConfig(t)
|
||||
tt.set(cfg.Pipeline, value)
|
||||
err := Validate(cfg)
|
||||
if err == nil || !strings.Contains(err.Error(), tt.want+" must be positive") {
|
||||
t.Fatalf("Validate() error = %v, want positive-value error for %s", err, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateNotariusTimeoutRequiresPositiveValue(t *testing.T) {
|
||||
for _, value := range []string{"0s", "-1ms"} {
|
||||
t.Run(value, func(t *testing.T) {
|
||||
cfg := loadedValidConfig(t)
|
||||
cfg.Pipeline.Notarius = &NotariusConfig{
|
||||
Enabled: true,
|
||||
Binary: "notarius",
|
||||
ConfigPath: "/tmp/notarius.yml",
|
||||
PipelineID: "session",
|
||||
Timeout: value,
|
||||
WorkingDirectory: "/tmp",
|
||||
Outputs: map[string]NotariusOutputConfig{
|
||||
"npc_registry": {
|
||||
LaneID: "npc-registry",
|
||||
MediaType: "application/json",
|
||||
SchemaID: "notarius.dnd.npc_registry",
|
||||
SchemaVersion: "v1",
|
||||
},
|
||||
},
|
||||
}
|
||||
err := Validate(cfg)
|
||||
if err == nil || !strings.Contains(err.Error(), "pipeline.notarius.timeout must be positive") {
|
||||
t.Fatalf("Validate() error = %v, want positive Notarius timeout error", err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDurationRejectsOverflowAndAcceptsPositiveSubsecondValues(t *testing.T) {
|
||||
cfg := loadedValidConfig(t)
|
||||
cfg.Pipeline.WhisperX.Timeout = "999999999999999999999h"
|
||||
if err := Validate(cfg); err == nil || !strings.Contains(err.Error(), "pipeline.whisperx.timeout must be a valid duration") {
|
||||
t.Fatalf("Validate() overflow error = %v, want duration parse error", err)
|
||||
}
|
||||
|
||||
cfg = loadedValidConfig(t)
|
||||
cfg.Pipeline.WhisperX.Timeout = "1ms"
|
||||
cfg.Pipeline.WhisperX.RetryDelay = "1ms"
|
||||
cfg.Pipeline.Seriatim.Timeout = "1ms"
|
||||
cfg.Pipeline.Audita.Timeout = "1ms"
|
||||
cfg.Pipeline.Scriptorium.Timeout = "1ms"
|
||||
cfg.Pipeline.Trim.Bounds.Timeout = "1ms"
|
||||
cfg.Pipeline.Notification.Timeout = "1ms"
|
||||
if cfg.Pipeline.Scriptorium.Artifacts == nil {
|
||||
cfg.Pipeline.Scriptorium.Artifacts = map[string]ScriptoriumArtifactConfig{}
|
||||
}
|
||||
cfg.Pipeline.Scriptorium.Artifacts["session_recap"] = ScriptoriumArtifactConfig{Timeout: "1ms"}
|
||||
if err := Validate(cfg); err != nil {
|
||||
t.Fatalf("Validate() positive subsecond durations error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateAllowsEmptyArtifactTimeoutFallback(t *testing.T) {
|
||||
cfg := loadedValidConfig(t)
|
||||
if cfg.Pipeline.Scriptorium.Artifacts == nil {
|
||||
cfg.Pipeline.Scriptorium.Artifacts = map[string]ScriptoriumArtifactConfig{}
|
||||
}
|
||||
cfg.Pipeline.Scriptorium.Artifacts["session_recap"] = ScriptoriumArtifactConfig{Timeout: ""}
|
||||
if err := Validate(cfg); err != nil {
|
||||
t.Fatalf("Validate() empty artifact timeout error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func loadedValidConfig(t *testing.T) *Config {
|
||||
t.Helper()
|
||||
pipelinePath, sessionPath := writeConfigFiles(t, testPipelineBaseYAML, testSessionBaseYAML)
|
||||
cfg, err := Load(pipelinePath, sessionPath)
|
||||
if err != nil {
|
||||
t.Fatalf("Load() error = %v", err)
|
||||
}
|
||||
return cfg
|
||||
}
|
||||
Reference in New Issue
Block a user