Files
distributor/internal/config/load_test.go

309 lines
7.8 KiB
Go

package config
import (
"os"
"path/filepath"
"strings"
"testing"
)
func TestLoadFileValidMinimalLocalToLocalConfig(t *testing.T) {
cfg := loadConfig(t, `
pipelines:
- id: local-copy
source:
backend: local
path: /var/spool/reports
destinations:
- id: archive
backend: local
path: /srv/archive
`)
if got, want := len(cfg.Pipelines), 1; got != want {
t.Fatalf("pipeline count = %d, want %d", got, want)
}
destination := cfg.Pipelines[0].Destinations[0]
if !destination.Publish.Source || destination.Publish.HTML {
t.Fatalf("publish defaults = source:%t html:%t, want source:true html:false", destination.Publish.Source, destination.Publish.HTML)
}
if got, want := cfg.Pipelines[0].Validation.OnDigestMismatch, ValidationActionFail; got != want {
t.Fatalf("validation default = %q, want %q", got, want)
}
if got, want := destination.Transfer.OnDestinationOlder, TransferActionReplace; got != want {
t.Fatalf("transfer default = %q, want %q", got, want)
}
}
func TestLoadFileValidFanOutConfig(t *testing.T) {
cfg := loadConfig(t, `
pipelines:
- id: fan-out
source:
backend: local
path: /var/spool/reports
destinations:
- id: markdown-archive
backend: s3
endpoint: https://s3.example.com
bucket: reports
prefix: archive
publish:
source: true
html: false
- id: static-site
backend: ssh
uri: ssh://deploy@example.com:22
path: /srv/www/reports
publish:
source: false
html: true
transform:
markdown_to_html:
enabled: true
mode: sidecar
`)
if got, want := len(cfg.Pipelines[0].Destinations), 2; got != want {
t.Fatalf("destination count = %d, want %d", got, want)
}
}
func TestLoadFileValidBackendConfigs(t *testing.T) {
tests := map[string]string{
"local": `
pipelines:
- id: local-backend
source:
backend: local
path: /source
destinations:
- id: local-destination
backend: local
path: /destination
`,
"ssh": `
pipelines:
- id: ssh-backend
source:
backend: ssh
uri: ssh://reports@example.com:22
path: /source
destinations:
- id: ssh-destination
backend: ssh
uri: ssh://deploy@example.com:22
path: /destination
`,
"s3": `
pipelines:
- id: s3-backend
source:
backend: s3
endpoint: https://s3.example.com
bucket: source
prefix: incoming
region: us-east-1
force_path_style: true
credentials:
access_key_id_env: DISTRIBUTOR_S3_ACCESS_KEY_ID
secret_access_key_env: DISTRIBUTOR_S3_SECRET_ACCESS_KEY
destinations:
- id: s3-destination
backend: s3
endpoint: https://s3.example.com
bucket: destination
prefix: archive
`,
}
for name, body := range tests {
t.Run(name, func(t *testing.T) {
loadConfig(t, body)
})
}
}
func TestLoadFileRejectsDuplicatePipelineIDs(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: duplicate
source:
backend: local
path: /one
destinations:
- id: archive
backend: local
path: /archive
- id: duplicate
source:
backend: local
path: /two
destinations:
- id: archive
backend: local
path: /archive
`, "pipeline id duplicate is duplicated")
}
func TestLoadFileRejectsDuplicateDestinationIDs(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
source:
backend: local
path: /source
destinations:
- id: archive
backend: local
path: /archive-one
- id: archive
backend: local
path: /archive-two
`, "destination id archive is duplicated")
}
func TestLoadFileRejectsMissingRequiredFields(t *testing.T) {
tests := map[string]string{
"pipelines": ``,
"pipeline id": `pipelines: [{source: {backend: local, path: /source}, destinations: [{id: archive, backend: local, path: /archive}]}]`,
"source backend": `pipelines: [{id: reports, source: {path: /source}, destinations: [{id: archive, backend: local, path: /archive}]}]`,
"destinations": `pipelines: [{id: reports, source: {backend: local, path: /source}}]`,
"destination id": `pipelines: [{id: reports, source: {backend: local, path: /source}, destinations: [{backend: local, path: /archive}]}]`,
"local path": `pipelines: [{id: reports, source: {backend: local}, destinations: [{id: archive, backend: local, path: /archive}]}]`,
"ssh uri": `pipelines: [{id: reports, source: {backend: ssh, path: /source}, destinations: [{id: archive, backend: local, path: /archive}]}]`,
"s3 bucket": `pipelines: [{id: reports, source: {backend: s3, endpoint: "https://s3.example.com"}, destinations: [{id: archive, backend: local, path: /archive}]}]`,
"publish outputs": `pipelines: [{id: reports, source: {backend: local, path: /source}, destinations: [{id: archive, backend: local, path: /archive, publish: {source: false, html: false}}]}]`,
}
for name, body := range tests {
t.Run(name, func(t *testing.T) {
assertLoadError(t, body, "")
})
}
}
func TestLoadFileRejectsUnsupportedBackend(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
source:
backend: ftp
path: /source
destinations:
- id: archive
backend: local
path: /archive
`, "backend ftp is unsupported")
}
func TestLoadFileRejectsInvalidTransferAction(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
source:
backend: local
path: /source
destinations:
- id: archive
backend: local
path: /archive
transfer:
on_destination_older: overwrite
`, "on_destination_older must be replace or fail")
}
func TestLoadFileRejectsInvalidValidationAction(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
source:
backend: local
path: /source
validation:
on_digest_mismatch: warn
destinations:
- id: archive
backend: local
path: /archive
`, "on_digest_mismatch must be fail")
}
func TestLoadFileRejectsHTMLPublishWithoutTransform(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
source:
backend: local
path: /source
destinations:
- id: static
backend: local
path: /srv/www
publish:
source: false
html: true
`, "markdown_to_html is required")
}
func TestLoadFileRejectsUnknownFields(t *testing.T) {
assertLoadError(t, `
pipelines:
- id: reports
surprise: true
source:
backend: local
path: /source
destinations:
- id: archive
backend: local
path: /archive
`, "field surprise not found")
}
func TestExampleConfigsLoad(t *testing.T) {
for _, path := range []string{
"../../examples/local-to-local.yml",
"../../examples/local-publish.yml",
"../../examples/local-html.yml",
"../../examples/fan-out.yml",
} {
t.Run(path, func(t *testing.T) {
if _, err := LoadFile(path); err != nil {
t.Fatalf("LoadFile(%q) error = %v", path, err)
}
})
}
}
func loadConfig(t *testing.T, body string) Config {
t.Helper()
path := writeConfig(t, body)
cfg, err := LoadFile(path)
if err != nil {
t.Fatalf("LoadFile() error = %v", err)
}
return cfg
}
func assertLoadError(t *testing.T, body, want string) {
t.Helper()
path := writeConfig(t, body)
_, err := LoadFile(path)
if err == nil {
t.Fatal("LoadFile() error = nil, want error")
}
if want != "" && !strings.Contains(err.Error(), want) {
t.Fatalf("LoadFile() error = %q, want substring %q", err.Error(), want)
}
}
func writeConfig(t *testing.T, body string) string {
t.Helper()
path := filepath.Join(t.TempDir(), "config.yml")
if err := os.WriteFile(path, []byte(strings.TrimSpace(body)+"\n"), 0o600); err != nil {
t.Fatalf("write config: %v", err)
}
return path
}