package config import ( "errors" "strings" "testing" configkit "gitea.maximumdirect.net/eric/ConfigKit" ) func TestDocumentLoadAcceptsVersion5AndCanonicalShapes(t *testing.T) { t.Parallel() for _, source := range []string{ `version: 5 pipelines: session: input: module: seriatim references: party: path: ./party.txt steps: - id: describe artifacts: spells: extract: module: dnd/spells validators: - module: generic/valid_json options: strict: true - id: use-artifact references: spells: artifact: step: describe lane: spells artifacts: locations: extract: module: dnd/locations `, `version: "5" pipelines: session: input: module: seriatim `, } { loaded, err := loadDocumentSource(configkit.Bytes("document.yml", []byte(source)), LoadOptions{}) if err != nil { t.Fatalf("loadDocumentSource() error = %v", err) } profile, ok := loaded.Config.Pipelines["session"] if !ok || profile.Input.Module != "seriatim" { t.Fatalf("pipeline = %#v", profile) } } } func TestDocumentLoadRejectsUnsupportedVersionsBeforeDocumentDecoding(t *testing.T) { t.Parallel() tests := []struct { name string source string code configkit.Code }{ {name: "missing", source: "unknown: value\n", code: configkit.CodeVersionError}, {name: "null", source: "version: null\nunknown: value\n", code: configkit.CodeVersionError}, {name: "version four", source: "version: 4\nunknown: value\n", code: configkit.CodeVersionError}, {name: "unsupported", source: "version: 6\nunknown: value\n", code: configkit.CodeVersionError}, {name: "noninteger", source: "version: 5.0\n", code: configkit.CodeVersionType}, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := loadDocumentSource(configkit.Bytes("document.yml", []byte(test.source)), LoadOptions{}) var loadError *configkit.LoadError if !errors.As(err, &loadError) { t.Fatalf("error = %v, want ConfigKit load error", err) } diagnostics := loadError.Diagnostics() if len(diagnostics) != 1 || diagnostics[0].Code != test.code || diagnostics[0].Path.String() != "/version" { t.Fatalf("diagnostics = %#v", diagnostics) } if strings.Contains(diagnostics[0].Message, "unknown") { t.Fatalf("document decoding ran before version validation: %#v", diagnostics) } }) } } func TestDocumentLoadRejectsShorthandAndUnknownFields(t *testing.T) { t.Parallel() tests := []struct { name string source string code configkit.Code path string }{ { name: "binding shorthand", source: "version: 5\npipelines:\n session:\n input: seriatim\n", code: configkit.CodeDecodeType, path: "/pipelines/session/input", }, { name: "validator nested producer field", source: "version: 5\npipelines:\n session:\n input:\n module: seriatim\n validators:\n - module: generic/valid_json\n validators: []\n", code: configkit.CodeDecodeUnknownField, path: "/pipelines/session/input/validators/0/validators", }, { name: "unknown field", source: "version: 5\npipelines:\n session:\n input:\n module: seriatim\n unknown: true\n", code: configkit.CodeDecodeUnknownField, path: "/pipelines/session/input/unknown", }, } for _, test := range tests { t.Run(test.name, func(t *testing.T) { _, err := loadDocumentSource(configkit.Bytes("document.yml", []byte(test.source)), LoadOptions{}) var loadError *configkit.LoadError if !errors.As(err, &loadError) { t.Fatalf("error = %v, want ConfigKit load error", err) } diagnostics := loadError.Diagnostics() if len(diagnostics) != 1 || diagnostics[0].Code != test.code || diagnostics[0].Path.String() != test.path { t.Fatalf("diagnostics = %#v", diagnostics) } }) } } func TestDocumentDefinitionReturnsIndependentDefaults(t *testing.T) { t.Parallel() first, err := loadDocumentSource(configkit.Bytes("first.yml", []byte("version: 5\n")), LoadOptions{}) if err != nil { t.Fatal(err) } first.Config.Pipelines["changed"] = PipelineDocument{} first.Config.Concurrency.StageWorkers["extract"] = 3 second, err := loadDocumentSource(configkit.Bytes("second.yml", []byte("version: 5\n")), LoadOptions{}) if err != nil { t.Fatal(err) } if len(second.Config.Pipelines) != 0 || second.Config.Concurrency.StageWorkers["extract"] != defaultLLMConcurrency || len(second.Config.Concurrency.StageWorkers) != 1 { t.Fatalf("defaults aliased across loads: %#v", second.Config) } }