package config //nolint:testpackage // checks the unexported errUnknownKey import ( "errors" "os" "path/filepath" "strings" "testing" ) // keysTestConfig loads; each case below adds one line to it. const keysTestConfig = "storage_url: file:///tmp/vaultik-test-storage\n" + "s3:\n" + " region: us-east-1\n" + "snapshots:\n" + " test:\n" + " paths: [/tmp/vaultik-test-source]\n" // TestParseRefusesUnknownKey checks that a key no setting has, at any // depth, fails the parse with an error naming the key and its line. A key // that a ${...} reference brings in is not in the file, so its error gives // no line. func TestParseRefusesUnknownKey(t *testing.T) { t.Parallel() snapshotPath := filepath.Join(t.TempDir(), "snapshot.yml") err := os.WriteFile(snapshotPath, []byte("other:\n"+ " paths: [/tmp/vaultik-test-source]\n"+ " exlude: [/tmp/cache]\n"), 0o600) if err != nil { t.Fatalf("write snapshot: %v", err) } tests := []struct { name string yaml string want string }{ {"top level", keysTestConfig + "compression_lvl: 9\n", `unknown config key "compression_lvl" on line 7`}, {"in a snapshot", keysTestConfig + " exlude: [/tmp/cache]\n", `unknown config key "snapshots.test.exlude" on line 7`}, {"in s3", strings.Replace(keysTestConfig, " region:", " bukcet: b\n region:", 1), `unknown config key "s3.bukcet" on line 3`}, {"from a reference", keysTestConfig + " other: ${YAML:" + snapshotPath + ":other}\n", `unknown config key "snapshots.other.exlude"`}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() _, err := Parse([]byte(tt.yaml)) if !errors.Is(err, errUnknownKey) { t.Fatalf("Parse() error = %v, want errUnknownKey", err) } if err.Error() != tt.want { t.Errorf("Parse() error = %q, want %q", err.Error(), tt.want) } }) } } // TestParseAcceptsKnownKeys checks that a config with a ${...} reference // and a YAML merge key (<<) still parses. func TestParseAcceptsKnownKeys(t *testing.T) { t.Parallel() levelPath := filepath.Join(t.TempDir(), "level") err := os.WriteFile(levelPath, []byte("9"), 0o600) if err != nil { t.Fatalf("write level: %v", err) } data := strings.Replace(keysTestConfig, " test:", " test: &test", 1) + " other:\n" + " <<: *test\n" + " exclude: [/tmp/vaultik-test-source/cache]\n" + "compression_level: ${FILE:" + levelPath + "}\n" cfg, err := Parse([]byte(data)) if err != nil { t.Fatalf("Parse() unexpected error: %v", err) } if cfg.CompressionLevel != 9 { t.Errorf("compression_level = %d, want 9", cfg.CompressionLevel) } other := cfg.Snapshots["other"] if len(other.Paths) != 1 || len(other.Exclude) != 1 { t.Errorf("snapshot other = %+v, want the paths of test and one exclude", other) } } // TestParseAcceptsEnvSection checks that a config with a top-level env // section still parses. smartconfig sets the environment variables it // lists, so its keys are not settings. func TestParseAcceptsEnvSection(t *testing.T) { const name = "VAULTIK_TEST_ENV_SECTION" // Parse sets the variable; t.Setenv restores it after the test. t.Setenv(name, "") _, err := Parse([]byte(keysTestConfig + "env:\n " + name + ": set\n")) if err != nil { t.Fatalf("Parse() unexpected error: %v", err) } }