360 lines
12 KiB
Go
360 lines
12 KiB
Go
package publish
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
"gitea.maximumdirect.net/eric/distributor/internal/bundle"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/config"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/state"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/storage/fake"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/testutil"
|
|
"gitea.maximumdirect.net/eric/distributor/internal/transform"
|
|
)
|
|
|
|
func TestOutputStateProjection(t *testing.T) {
|
|
sourceOutput := Output{
|
|
SourcePath: "report.md",
|
|
DestinationPath: "report.md",
|
|
Kind: state.OutputKindSource,
|
|
URL: "https://reports.example.com/report.md",
|
|
SHA256: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
Size: 123,
|
|
}
|
|
sourceState := sourceOutput.StateOutputFile()
|
|
if sourceState.Path != "report.md" || sourceState.Kind != state.OutputKindSource || sourceState.SourcePath != "report.md" || sourceState.URL != sourceOutput.URL || sourceState.SHA256 != sourceOutput.SHA256 || sourceState.Size != sourceOutput.Size {
|
|
t.Fatalf("source state output = %#v", sourceState)
|
|
}
|
|
|
|
generatedOutput := Output{
|
|
SourcePath: "report.md",
|
|
DestinationPath: "report.html",
|
|
Kind: state.OutputKindGenerated,
|
|
Transform: transform.MarkdownToHTML,
|
|
URL: "https://reports.example.com/report.html",
|
|
SHA256: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
Size: 456,
|
|
}
|
|
generatedState := generatedOutput.StateOutputFile()
|
|
if generatedState.Path != "report.html" || generatedState.Kind != state.OutputKindGenerated || generatedState.SourcePath != "report.md" || generatedState.Transform != transform.MarkdownToHTML || generatedState.URL != generatedOutput.URL || generatedState.SHA256 != generatedOutput.SHA256 || generatedState.Size != generatedOutput.Size {
|
|
t.Fatalf("generated state output = %#v", generatedState)
|
|
}
|
|
}
|
|
|
|
func TestOutputSliceProjections(t *testing.T) {
|
|
outputs := []Output{
|
|
{SourcePath: "report.md", DestinationPath: "report.md", Kind: state.OutputKindSource, SHA256: "sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", Size: 1},
|
|
{SourcePath: "report.md", DestinationPath: "report.html", Kind: state.OutputKindGenerated, Transform: transform.MarkdownToHTML, URL: "https://reports.example.com/report.html", SHA256: "sha256:bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb", Size: 2},
|
|
}
|
|
|
|
stateOutputs := StateOutputFiles(outputs)
|
|
if len(stateOutputs) != 2 || stateOutputs[1].Path != "report.html" || stateOutputs[1].Transform != transform.MarkdownToHTML || stateOutputs[1].URL != outputs[1].URL {
|
|
t.Fatalf("state outputs = %#v", stateOutputs)
|
|
}
|
|
|
|
paths := ManagedOutputPaths(outputs)
|
|
if len(paths) != 2 || paths[0] != "report.md" || paths[1] != "report.html" {
|
|
t.Fatalf("managed paths = %#v", paths)
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsRejectsCollision(t *testing.T) {
|
|
sourceBackend := fake.New()
|
|
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "", testutil.BundleOptions{
|
|
Files: []testutil.SourceFile{
|
|
{Path: "report.md", Data: "# Report\n"},
|
|
{Path: "report.html", Data: "<p>source html</p>\n"},
|
|
},
|
|
})
|
|
_, err := PlanOutputs(context.Background(), Request{
|
|
SourceBackend: sourceBackend,
|
|
SourceBundle: sourceBundle,
|
|
Publish: config.PublishPolicy{Source: true, HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{Enabled: true, Mode: config.TransformModeSidecar}},
|
|
Transformers: testResolver{transform.MarkdownToHTML: testTransformer{outputs: []transform.Output{{
|
|
Path: "report.html",
|
|
SourcePath: "report.md",
|
|
Transform: transform.MarkdownToHTML,
|
|
Data: []byte("<p>Report</p>\n"),
|
|
SHA256: bundle.FileDigest([]byte("<p>Report</p>\n")),
|
|
Size: int64(len("<p>Report</p>\n")),
|
|
}}}},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("PlanSourceOutputs() error = nil, want collision")
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsRejectsHTMLWithoutMarkdown(t *testing.T) {
|
|
sourceBackend := fake.New()
|
|
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "", testutil.BundleOptions{
|
|
Files: []testutil.SourceFile{{Path: "summary.txt", Data: "Summary\n"}},
|
|
})
|
|
_, err := PlanOutputs(context.Background(), Request{
|
|
SourceBackend: sourceBackend,
|
|
SourceBundle: sourceBundle,
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{Enabled: true, Mode: config.TransformModeSidecar}},
|
|
Transformers: testResolver{
|
|
transform.MarkdownToHTML: testTransformer{},
|
|
},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("PlanOutputs() error = nil, want no markdown failure")
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsRejectsHTMLWithoutTransformerResolver(t *testing.T) {
|
|
_, err := PlanOutputs(context.Background(), Request{
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{Enabled: true, Mode: config.TransformModeSidecar}},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("PlanOutputs() error = nil, want resolver failure")
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsRejectsMissingMarkdownTransformer(t *testing.T) {
|
|
_, err := PlanOutputs(context.Background(), Request{
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{Enabled: true, Mode: config.TransformModeSidecar}},
|
|
Transformers: testResolver{},
|
|
})
|
|
if err == nil {
|
|
t.Fatal("PlanOutputs() error = nil, want missing transformer failure")
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsUsesRegisteredTransformer(t *testing.T) {
|
|
data := []byte("<p>Generated</p>\n")
|
|
outputs, err := PlanOutputs(context.Background(), Request{
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{Enabled: true, Mode: config.TransformModeSidecar}},
|
|
Transformers: testResolver{transform.MarkdownToHTML: testTransformer{outputs: []transform.Output{{
|
|
Path: "report.html",
|
|
SourcePath: "report.md",
|
|
Transform: transform.MarkdownToHTML,
|
|
Data: data,
|
|
SHA256: bundle.FileDigest(data),
|
|
Size: int64(len(data)),
|
|
}}}},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("PlanOutputs() error = %v", err)
|
|
}
|
|
if got, want := len(outputs), 1; got != want {
|
|
t.Fatalf("output count = %d, want %d", got, want)
|
|
}
|
|
if outputs[0].DestinationPath != "report.html" || string(outputs[0].Data) != string(data) {
|
|
t.Fatalf("output = %#v", outputs[0])
|
|
}
|
|
}
|
|
|
|
func TestPlanOutputsPassesMarkdownOptions(t *testing.T) {
|
|
data := []byte("<p>Generated</p>\n")
|
|
transformer := &recordingTransformer{outputs: []transform.Output{{
|
|
Path: "index.html",
|
|
SourcePath: "report.md",
|
|
Transform: transform.MarkdownToHTML,
|
|
Data: data,
|
|
SHA256: bundle.FileDigest(data),
|
|
Size: int64(len(data)),
|
|
}}}
|
|
|
|
_, err := PlanOutputs(context.Background(), Request{
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
|
Enabled: true,
|
|
Mode: config.TransformModeIndex,
|
|
Input: "report.md",
|
|
}},
|
|
Transformers: testResolver{transform.MarkdownToHTML: transformer},
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("PlanOutputs() error = %v", err)
|
|
}
|
|
if transformer.request.Markdown.Mode != config.TransformModeIndex || transformer.request.Markdown.Input != "report.md" {
|
|
t.Fatalf("markdown options = %#v, want index/report.md", transformer.request.Markdown)
|
|
}
|
|
}
|
|
|
|
func TestBuildRejectsHTMLWithoutTransform(t *testing.T) {
|
|
sourceBackend := fake.New()
|
|
destinationBackend := fake.New()
|
|
sourceBundle := testutil.WriteFakeSourceBundle(t, sourceBackend, "", testutil.BundleOptions{
|
|
Files: []testutil.SourceFile{{Path: "report.md", Data: "# Report\n"}},
|
|
})
|
|
_, err := Build(context.Background(), Request{
|
|
PipelineID: "reports",
|
|
DestinationID: "archive",
|
|
SourceBackend: sourceBackend,
|
|
DestinationBackend: destinationBackend,
|
|
DestinationBundlePath: "",
|
|
SourceBundle: sourceBundle,
|
|
Publish: config.PublishPolicy{HTML: true},
|
|
Transfer: config.TransferPolicy{
|
|
OnDestinationSame: config.TransferActionSkip,
|
|
OnDestinationOlder: config.TransferActionReplace,
|
|
OnDestinationNewer: config.TransferActionSkip,
|
|
OnConflict: config.TransferActionFail,
|
|
},
|
|
})
|
|
if err == nil {
|
|
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: "html only index allowed",
|
|
publish: config.PublishPolicy{HTML: true},
|
|
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
|
Enabled: true,
|
|
Mode: config.TransformModeIndex,
|
|
}},
|
|
},
|
|
{
|
|
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: "source only enabled transform rejected",
|
|
publish: config.PublishPolicy{Source: true},
|
|
transform: config.Transform{MarkdownToHTML: &config.MarkdownToHTML{
|
|
Enabled: true,
|
|
Mode: config.TransformModeSidecar,
|
|
}},
|
|
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)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
type testResolver map[string]transform.Transformer
|
|
|
|
func (r testResolver) Get(name string) (transform.Transformer, bool) {
|
|
transformer, ok := r[name]
|
|
return transformer, ok
|
|
}
|
|
|
|
type testTransformer struct {
|
|
outputs []transform.Output
|
|
err error
|
|
}
|
|
|
|
func (t testTransformer) Generate(context.Context, transform.Request) ([]transform.Output, error) {
|
|
return t.outputs, t.err
|
|
}
|
|
|
|
type recordingTransformer struct {
|
|
outputs []transform.Output
|
|
request transform.Request
|
|
err error
|
|
}
|
|
|
|
func (t *recordingTransformer) Generate(_ context.Context, req transform.Request) ([]transform.Output, error) {
|
|
t.request = req
|
|
return t.outputs, t.err
|
|
}
|