package engine import ( "errors" "io" "strings" "testing" ) func TestParseImportDeclarationSpecNormalizesPresenceAndPlacement(t *testing.T) { t.Parallel() specs, diagnostics := parseImportSpecsYAML(t, ` - path: [one.yaml, one.yaml, two.yaml] required: false destination: [services] placement: sequence-item - dir: [] recursive: true pattern: [] exclude: [] - glob: config/*.yaml `) if len(diagnostics) != 0 { t.Fatalf("diagnostics = %#v", diagnostics) } if len(specs) != 3 { t.Fatalf("spec count = %d, want 3", len(specs)) } first := specs[0] if first.selector != importSelectorPath || len(first.paths) != 2 || first.paths[0] != "one.yaml" || first.paths[1] != "two.yaml" { t.Fatalf("path selector = %#v", first) } if first.required == nil || *first.required || first.destination.String() != "/services" || first.placement != placementModeSequenceItem { t.Fatalf("explicit declaration values = %#v", first) } if first.path.String() != "/configkit/imports/0" || first.fieldSites["required"].path.String() != "/configkit/imports/0/required" { t.Fatalf("stored paths = declaration %q, required %q", first.path.String(), first.fieldSites["required"].path.String()) } second := specs[1] if second.selector != importSelectorDirectory || second.paths == nil || len(second.paths) != 0 || !second.patternSet || second.pattern == nil || len(second.pattern) != 0 || !second.excludeSet || second.exclude == nil { t.Fatalf("empty list presence was lost: %#v", second) } third := specs[2] if third.selector != importSelectorGlob || third.glob != "config/*.yaml" || len(third.globSegments) != 2 { t.Fatalf("glob selector = %#v", third) } } func TestParseImportDeclarationSpecValidatesSelectorMatrixAndTypes(t *testing.T) { t.Parallel() tests := []struct { name, yaml, path string }{ {name: "missing selector", yaml: " - required: true\n", path: "/configkit/imports/0"}, {name: "multiple selectors", yaml: " - path: one.yaml\n dir: fragments\n", path: "/configkit/imports/0"}, {name: "removed optional", yaml: " - path: one.yaml\n optional: true\n", path: "/configkit/imports/0/optional"}, {name: "inapplicable field", yaml: " - path: one.yaml\n pattern: '*.yaml'\n", path: "/configkit/imports/0/pattern"}, {name: "selector null", yaml: " - path: null\n", path: "/configkit/imports/0/path"}, {name: "empty path", yaml: " - path: ''\n", path: "/configkit/imports/0/path"}, {name: "nested selector list", yaml: " - path: [[one.yaml]]\n", path: "/configkit/imports/0/path/0"}, {name: "null selector member", yaml: " - dir: [fragments, null]\n", path: "/configkit/imports/0/dir/1"}, {name: "empty glob", yaml: " - glob: ''\n", path: "/configkit/imports/0/glob"}, {name: "recursive glob wildcard", yaml: " - glob: '**/*.yaml'\n", path: "/configkit/imports/0/glob"}, {name: "null boolean", yaml: " - path: one.yaml\n required: null\n", path: "/configkit/imports/0/required"}, {name: "bad pattern member with empty roots", yaml: " - dir: []\n pattern: ['*', '[']\n", path: "/configkit/imports/0/pattern/1"}, {name: "bad exclusion member", yaml: " - glob: '*.yaml'\n exclude: ['[']\n", path: "/configkit/imports/0/exclude/0"}, {name: "invalid destination", yaml: " - path: one.yaml\n destination: [configkit]\n", path: "/configkit/imports/0/destination/0"}, {name: "invalid placement", yaml: " - path: one.yaml\n placement: sequence\n", path: "/configkit/imports/0/placement"}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { t.Parallel() _, diagnostics := parseImportSpecsYAML(t, test.yaml) if len(diagnostics) == 0 { t.Fatal("expected a declaration diagnostic") } if diagnostics[0].Code != CodeImportInvalid || diagnostics[0].Path.String() != test.path { t.Fatalf("diagnostic = %#v, want %s at %s", diagnostics[0], CodeImportInvalid, test.path) } if diagnostics[0].Primary == nil || diagnostics[0].Primary.Line == 0 { t.Fatalf("diagnostic has no source location: %#v", diagnostics[0]) } }) } } func TestParseImportDeclarationSpecValidatesDepthSyntaxAndPolicy(t *testing.T) { t.Parallel() for _, test := range []struct { value string valid bool want int }{ {value: "0", valid: true, want: 0}, {value: "-0", valid: true, want: 0}, {value: "999999999999999999999999999999999999", valid: true, want: int(^uint(0) >> 1)}, {value: "-1"}, {value: "1.5"}, {value: "1e3"}, {value: "'2'"}, {value: "true"}, {value: "null"}, } { t.Run(test.value, func(t *testing.T) { t.Parallel() specs, diagnostics := parseImportSpecsYAML(t, " - dir: []\n recursive: true\n depth: "+test.value+"\n") if !test.valid { if len(diagnostics) == 0 || diagnostics[0].Path.String() != "/configkit/imports/0/depth" { t.Fatalf("diagnostics = %#v, want invalid depth", diagnostics) } return } if len(diagnostics) != 0 || len(specs) != 1 || specs[0].depth != test.want { t.Fatalf("specs, diagnostics = %#v, %#v; want depth %d", specs, diagnostics, test.want) } }) } specs, diagnostics := parseImportSpecsYAML(t, " - dir: []\n depth: 1\n") if len(diagnostics) != 0 { t.Fatalf("syntax-only parse diagnostics = %#v", diagnostics) } _, diagnostics = resolveImportPolicy(specs[0], importDefaults{}) if len(diagnostics) != 1 || diagnostics[0].Path.String() != "/configkit/imports/0/depth" { t.Fatalf("policy diagnostics = %#v, want depth applicability error", diagnostics) } recursive := true policy, diagnostics := resolveImportPolicy(specs[0], importDefaults{recursive: &recursive}) if len(diagnostics) != 0 || !policy.recursive || policy.depth != 1 || !policy.depthSet { t.Fatalf("application-default policy, diagnostics = %#v, %#v", policy, diagnostics) } } func TestResolveImportPolicyAppliesDefaultsIndependently(t *testing.T) { t.Parallel() omittedSpecs, diagnostics := parseImportSpecsYAML(t, " - dir: fragments\n") if len(diagnostics) != 0 { t.Fatal(diagnostics) } omitted, diagnostics := resolveImportPolicy(omittedSpecs[0], importDefaults{}) if len(diagnostics) != 0 || omitted.required || omitted.recursive || !omitted.follow || !omitted.hidden || omitted.pattern != nil { t.Fatalf("library defaults = %#v, diagnostics = %#v", omitted, diagnostics) } specs, diagnostics := parseImportSpecsYAML(t, ` - dir: fragments required: false recursive: false follow: false hidden: false pattern: [] `) if len(diagnostics) != 0 { t.Fatal(diagnostics) } appRequired, appRecursive, appFollow, appHidden := true, true, true, true defaults := importDefaults{ required: &appRequired, recursive: &appRecursive, follow: &appFollow, hidden: &appHidden, pattern: []string{"*.yaml"}, } policy, diagnostics := resolveImportPolicy(specs[0], defaults) if len(diagnostics) != 0 { t.Fatalf("policy diagnostics = %#v", diagnostics) } if policy.required || policy.recursive || policy.follow || policy.hidden || policy.pattern == nil || len(policy.pattern) != 0 { t.Fatalf("explicit values did not override application defaults: %#v", policy) } pathSpecs, diagnostics := parseImportSpecsYAML(t, " - path: file.yaml\n") if len(diagnostics) != 0 { t.Fatal(diagnostics) } pathPolicy, diagnostics := resolveImportPolicy(pathSpecs[0], defaults) if len(diagnostics) != 0 || pathPolicy.required != true || pathPolicy.recursive || pathPolicy.pattern != nil { t.Fatalf("path policy applied unsupported defaults: %#v, %#v", pathPolicy, diagnostics) } defaulted, diagnostics := resolveImportPolicy(omittedSpecs[0], defaults) if len(diagnostics) != 0 || len(defaulted.pattern) != 1 || defaulted.pattern[0] != "*.yaml" { t.Fatalf("directory pattern default = %#v, diagnostics = %#v", defaulted.pattern, diagnostics) } snapshot := snapshotImportDefaults(defaults) *defaults.required = false defaults.pattern[0] = "*.toml" if snapshot.required == nil || !*snapshot.required || snapshot.pattern[0] != "*.yaml" { t.Fatalf("snapshot shares mutable inputs: %#v", snapshot) } empty := snapshotImportDefaults(importDefaults{pattern: []string{}}) if empty.pattern == nil || len(empty.pattern) != 0 { t.Fatalf("snapshot lost explicit empty pattern: %#v", empty) } if snapshotImportDefaults(importDefaults{}).pattern != nil { t.Fatal("snapshot changed omitted pattern into explicit empty") } } func TestInvalidImportDefaultPatternsFailBeforeReadingSource(t *testing.T) { t.Parallel() reader := &countingImportSpecReader{Reader: strings.NewReader("not: read\n")} _, err := Load(Reader("invalid-default-pattern.yml", reader), testSecretBinding(), Options[struct{}]{ importDefaults: importDefaults{pattern: []string{"["}}, }) var loadErr *LoadError if !errors.As(err, &loadErr) || len(loadErr.Diagnostics()) == 0 || loadErr.Diagnostics()[0].Code != CodeOptionsInvalid { t.Fatalf("load error = %T %v, want invalid-options LoadError", err, err) } if reader.reads != 0 { t.Fatalf("reader consumed %d bytes before invalid defaults failed", reader.reads) } } func parseImportSpecsYAML(t *testing.T, declarations string) ([]importDeclarationSpec, []Diagnostic) { t.Helper() parsed, diagnostics := parseSource(Bytes("import specification fixture", []byte("configkit:\n imports:\n"+declarations)), "source-1", defaultParseLimits()) if len(diagnostics) != 0 { return nil, diagnostics } index := documentEntryIndex(parsed.document.Mapping, directiveKey) if index < 0 { t.Fatal("missing root directive in test fixture") } return parseImportSpecificationDirective(parsed.document.Mapping[index].Value, Keys(directiveKey)) } type countingImportSpecReader struct { io.Reader reads int } func (reader *countingImportSpecReader) Read(buffer []byte) (int, error) { read, err := reader.Reader.Read(buffer) reader.reads += read return read, err }