package config import ( "strings" "testing" ) func TestValidatePublishTransformPolicy(t *testing.T) { tests := publishTransformPolicyCases() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { err := ValidatePublishTransformPolicy(tt.publish, tt.transform) if tt.wantErr && err == nil { t.Fatal("ValidatePublishTransformPolicy() error = nil, want error") } if !tt.wantErr && err != nil { t.Fatalf("ValidatePublishTransformPolicy() error = %v", err) } }) } } func TestValidateChecksPublishTransformPolicy(t *testing.T) { tests := publishTransformPolicyCases() for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := Config{Pipelines: []Pipeline{{ ID: "reports", Source: Backend{ Backend: BackendLocal, Path: "/source", }, Destinations: []Destination{{ ID: "archive", Backend: BackendLocal, Path: "/destination", Publish: &tt.publish, Transform: tt.transform, }}, }}} ApplyDefaults(&cfg) err := Validate(cfg) if tt.wantErr && err == nil { t.Fatal("Validate() error = nil, want error") } if !tt.wantErr && err != nil { t.Fatalf("Validate() error = %v", err) } }) } } func TestValidateAcceptsForceReplacementTransferActions(t *testing.T) { cfg := Config{Pipelines: []Pipeline{{ ID: "reports", Source: Backend{ Backend: BackendLocal, Path: "/source", }, Destinations: []Destination{{ ID: "archive", Backend: BackendLocal, Path: "/destination", Transfer: TransferPolicy{ OnDestinationNewer: TransferActionReplace, OnConflict: TransferActionReplace, }, }}, }}} ApplyDefaults(&cfg) if err := Validate(cfg); err != nil { t.Fatalf("Validate() error = %v", err) } } func TestValidatePathMapping(t *testing.T) { tests := []struct { name string mode string wantErr bool }{ {name: "preserve relative", mode: PathMappingPreserveRelative}, {name: "fixed", mode: PathMappingFixed}, {name: "invalid", mode: "archive", wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := Config{Pipelines: []Pipeline{{ ID: "reports", Source: Backend{Backend: BackendLocal, Path: "/source"}, Destinations: []Destination{{ ID: "archive", Backend: BackendLocal, Path: "/destination", PathMap: PathMapping{Mode: tt.mode}, }}, }}} ApplyDefaults(&cfg) err := Validate(cfg) if tt.wantErr && err == nil { t.Fatal("Validate() error = nil, want error") } if !tt.wantErr && err != nil { t.Fatalf("Validate() error = %v", err) } }) } } func TestValidateLinks(t *testing.T) { tests := []struct { name string links *Links wantErr bool }{ {name: "absent links"}, {name: "http", links: &Links{BaseURL: "http://reports.example.com/archive", Primary: LinkPrimaryAuto}}, {name: "https", links: &Links{BaseURL: "https://reports.example.com/archive/", Primary: LinkPrimaryHTML}}, {name: "source primary", links: &Links{BaseURL: "https://reports.example.com", Primary: LinkPrimarySource}}, {name: "missing base", links: &Links{Primary: LinkPrimaryAuto}, wantErr: true}, {name: "ftp scheme", links: &Links{BaseURL: "ftp://reports.example.com", Primary: LinkPrimaryAuto}, wantErr: true}, {name: "missing host", links: &Links{BaseURL: "https:///archive", Primary: LinkPrimaryAuto}, wantErr: true}, {name: "query", links: &Links{BaseURL: "https://reports.example.com/archive?preview=1", Primary: LinkPrimaryAuto}, wantErr: true}, {name: "fragment", links: &Links{BaseURL: "https://reports.example.com/archive#top", Primary: LinkPrimaryAuto}, wantErr: true}, {name: "invalid primary", links: &Links{BaseURL: "https://reports.example.com", Primary: "document"}, wantErr: true}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { cfg := Config{Pipelines: []Pipeline{{ ID: "reports", Source: Backend{Backend: BackendLocal, Path: "/source"}, Destinations: []Destination{{ ID: "web", Backend: BackendLocal, Path: "/destination", Links: tt.links, }}, }}} ApplyDefaults(&cfg) err := Validate(cfg) if tt.wantErr && err == nil { t.Fatal("Validate() error = nil, want error") } if !tt.wantErr && err != nil { t.Fatalf("Validate() error = %v", err) } }) } } func TestValidateLinksReportsFieldContext(t *testing.T) { cfg := Config{Pipelines: []Pipeline{{ ID: "reports", Source: Backend{Backend: BackendLocal, Path: "/source"}, Destinations: []Destination{{ ID: "web", Backend: BackendLocal, Path: "/destination", Links: &Links{BaseURL: "https://reports.example.com/archive?preview=1", Primary: LinkPrimaryAuto}, }}, }}} ApplyDefaults(&cfg) err := Validate(cfg) if err == nil { t.Fatal("Validate() error = nil, want error") } want := "pipelines[0].destinations[0].links.base_url must not include a query string" if !strings.Contains(err.Error(), want) { t.Fatalf("Validate() error = %q, want %q", err, want) } } type publishTransformPolicyCase struct { name string publish PublishPolicy transform Transform wantErr bool } func publishTransformPolicyCases() []publishTransformPolicyCase { return []publishTransformPolicyCase{ { name: "source only allowed", publish: PublishPolicy{Source: true}, }, { name: "html only sidecar allowed", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeSidecar, }}, }, { name: "html only default mode allowed", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, }}, }, { name: "html only index allowed", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeIndex, }}, }, { name: "html only index input allowed", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeIndex, Input: "report.md", }}, }, { name: "source and html sidecar allowed", publish: PublishPolicy{Source: true, HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeSidecar, }}, }, { name: "no outputs rejected", publish: PublishPolicy{}, wantErr: true, }, { name: "html without transform rejected", publish: PublishPolicy{HTML: true}, wantErr: true, }, { name: "html with disabled transform rejected", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, Mode: TransformModeSidecar, }}, wantErr: true, }, { name: "html with wrong mode rejected", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: "inline", }}, wantErr: true, }, { name: "source only enabled transform rejected", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeSidecar, }}, wantErr: true, }, { name: "enabled markdown wrong mode rejected", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: "inline", }}, wantErr: true, }, { name: "sidecar input rejected", publish: PublishPolicy{HTML: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: true, Mode: TransformModeSidecar, Input: "report.md", }}, wantErr: true, }, { name: "disabled markdown empty mode allowed", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, }}, }, { name: "disabled markdown sidecar mode allowed", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, Mode: TransformModeSidecar, }}, }, { name: "disabled markdown index mode allowed", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, Mode: TransformModeIndex, }}, }, { name: "disabled markdown input rejected", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, Mode: TransformModeIndex, Input: "report.md", }}, wantErr: true, }, { name: "disabled markdown wrong mode rejected", publish: PublishPolicy{Source: true}, transform: Transform{MarkdownToHTML: &MarkdownToHTML{ Enabled: false, Mode: "inline", }}, wantErr: true, }, } }