Centralize publish transform policy validation

This commit is contained in:
2026-05-31 03:25:27 +00:00
parent bda5f6ac6a
commit 93805bcc8c
5 changed files with 275 additions and 27 deletions

View File

@@ -57,8 +57,7 @@ func Validate(cfg Config) error {
}
errs = validateBackend(errs, destinationContext, destination.Backend, destination.Path, destination.URI, destination.Endpoint, destination.Bucket)
errs = validatePublishPolicy(errs, destinationContext+".publish", destination.Publish)
errs = validateTransform(errs, destinationContext+".transform", destination.Publish, destination.Transform)
errs = validatePublishTransformPolicy(errs, destinationContext, destination.Publish, destination.Transform)
errs = validateTransferPolicy(errs, destinationContext+".transfer", destination.Transfer)
}
}
@@ -104,36 +103,40 @@ func validateValidationPolicy(errs ValidationErrors, context string, policy Vali
return errs
}
func validatePublishPolicy(errs ValidationErrors, context string, policy *PublishPolicy) ValidationErrors {
func validatePublishTransformPolicy(errs ValidationErrors, context string, policy *PublishPolicy, transform Transform) ValidationErrors {
if policy == nil {
errs = append(errs, context+" is required")
errs = append(errs, context+".publish is required")
return errs
}
if !policy.Source && !policy.HTML {
errs = append(errs, context+" must enable source or html")
if err := ValidatePublishTransformPolicy(*policy, transform); err != nil {
errs = append(errs, context+"."+err.Error())
}
return errs
}
func validateTransform(errs ValidationErrors, context string, publish *PublishPolicy, transform Transform) ValidationErrors {
publishesHTML := publish != nil && publish.HTML
if transform.MarkdownToHTML == nil {
if publishesHTML {
errs = append(errs, context+".markdown_to_html is required when publish.html is true")
}
return errs
func ValidatePublishTransformPolicy(publish PublishPolicy, transform Transform) error {
if !publish.Source && !publish.HTML {
return fmt.Errorf("publish must enable source or html")
}
if publishesHTML && !transform.MarkdownToHTML.Enabled {
errs = append(errs, context+".markdown_to_html.enabled must be true when publish.html is true")
if publish.HTML && transform.MarkdownToHTML == nil {
return fmt.Errorf("transform.markdown_to_html is required when publish.html is true")
}
if transform.MarkdownToHTML == nil {
return nil
}
if publish.HTML && !transform.MarkdownToHTML.Enabled {
return fmt.Errorf("transform.markdown_to_html.enabled must be true when publish.html is true")
}
if publish.HTML && transform.MarkdownToHTML.Mode != TransformModeSidecar {
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
}
if transform.MarkdownToHTML.Enabled && transform.MarkdownToHTML.Mode != TransformModeSidecar {
errs = append(errs, context+".markdown_to_html.mode must be "+TransformModeSidecar)
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
}
if !transform.MarkdownToHTML.Enabled && transform.MarkdownToHTML.Mode != "" && transform.MarkdownToHTML.Mode != TransformModeSidecar {
errs = append(errs, context+".markdown_to_html.mode must be "+TransformModeSidecar)
return fmt.Errorf("transform.markdown_to_html.mode must be %s", TransformModeSidecar)
}
return errs
return nil
}
func validateTransferPolicy(errs ValidationErrors, context string, policy TransferPolicy) ValidationErrors {

View File

@@ -0,0 +1,141 @@
package config
import "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)
}
})
}
}
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: "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: "enabled markdown wrong mode rejected",
publish: PublishPolicy{Source: true},
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
Enabled: true,
Mode: "inline",
}},
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 wrong mode rejected",
publish: PublishPolicy{Source: true},
transform: Transform{MarkdownToHTML: &MarkdownToHTML{
Enabled: false,
Mode: "inline",
}},
wantErr: true,
},
}
}

View File

@@ -99,3 +99,112 @@ func TestBuildRejectsHTMLWithoutTransform(t *testing.T) {
t.Fatal("Build() error = nil, want missing transform error")
}
}
func TestValidateRequestChecksPublishTransformPolicy(t *testing.T) {
tests := []struct {
name string
publish config.PublishPolicy
transform config.Transform
wantErr bool
}{
{
name: "source only allowed",
publish: config.PublishPolicy{Source: true},
},
{
name: "html only sidecar allowed",
publish: config.PublishPolicy{HTML: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: true,
Mode: config.TransformModeSidecar,
}},
},
{
name: "source and html sidecar allowed",
publish: config.PublishPolicy{Source: true, HTML: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: true,
Mode: config.TransformModeSidecar,
}},
},
{
name: "no outputs rejected",
publish: config.PublishPolicy{},
wantErr: true,
},
{
name: "html without transform rejected",
publish: config.PublishPolicy{HTML: true},
wantErr: true,
},
{
name: "html with disabled transform rejected",
publish: config.PublishPolicy{HTML: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: false,
Mode: config.TransformModeSidecar,
}},
wantErr: true,
},
{
name: "html with wrong mode rejected",
publish: config.PublishPolicy{HTML: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: true,
Mode: "inline",
}},
wantErr: true,
},
{
name: "enabled markdown wrong mode rejected",
publish: config.PublishPolicy{Source: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: true,
Mode: "inline",
}},
wantErr: true,
},
{
name: "disabled markdown empty mode allowed",
publish: config.PublishPolicy{Source: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: false,
}},
},
{
name: "disabled markdown sidecar mode allowed",
publish: config.PublishPolicy{Source: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: false,
Mode: config.TransformModeSidecar,
}},
},
{
name: "disabled markdown wrong mode rejected",
publish: config.PublishPolicy{Source: true},
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
Enabled: false,
Mode: "inline",
}},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := validateRequest(Request{
PipelineID: "reports",
DestinationID: "archive",
SourceBackend: fake.New(),
DestinationBackend: fake.New(),
Publish: tt.publish,
Transform: tt.transform,
})
if tt.wantErr && err == nil {
t.Fatal("validateRequest() error = nil, want error")
}
if !tt.wantErr && err != nil {
t.Fatalf("validateRequest() error = %v", err)
}
})
}
}

View File

@@ -102,13 +102,8 @@ func validateRequest(req Request) error {
if req.DestinationBackend == nil {
return fmt.Errorf("destination backend is required")
}
if !req.Publish.Source && !req.Publish.HTML {
return fmt.Errorf("publish source or html must be enabled")
}
if req.Publish.HTML {
if req.Transform.MarkdownToHTML == nil || !req.Transform.MarkdownToHTML.Enabled || req.Transform.MarkdownToHTML.Mode != config.TransformModeSidecar {
return fmt.Errorf("publish html requires markdown_to_html transform enabled with sidecar mode")
}
if err := config.ValidatePublishTransformPolicy(req.Publish, req.Transform); err != nil {
return fmt.Errorf("publish/transform policy: %w", err)
}
return nil
}