Fix every finding surfaced by the canonical .golangci.yml with golangci-lint v2.12.2 (refs #61), behavior-preserving throughout: - err113: dynamic errors replaced with package-level sentinels and %w wrapping; direct comparisons converted to errors.Is - goprintffuncname: printf-style helpers renamed with an f suffix (ui.Writer message methods, cli.ReportErrorf, database.Fatalf, vaultik stdoutf) and all call sites updated - revive: stuttering type names renamed (blob.Handler, blob.WithReader, blob.ChunkPosition, storage.URL, storage.Info), doc comments added, unused parameters blanked, package comments added - contextcheck/noctx: ctx threaded through blob.Packer (AddChunk/Flush/FinalizeBlob/PackChunks) and scanner call sites; context-aware exec and sql variants used - funlen/cyclop/gocognit/nestif/dupl: oversized or duplicated functions split into focused helpers across production and test code - paralleltest/tparallel/thelper/usetesting/testpackage: tests parallelized where safe (global log.Initialize kept in the serial phase), helpers marked, t.TempDir adopted, external test packages where only exported API is used - gosec: integer conversions clamped or justified, header timeouts added, remaining findings suppressed with per-site justifications - mnd/goconst/lll/wsl_v5/nlreturn/noinlineerr/errcheck and other mechanical findings fixed directly Remove the deprecated log.LogOptions alias (callers migrated to log.Options). make check is green.
198 lines
4.3 KiB
Go
198 lines
4.3 KiB
Go
package cli //nolint:testpackage // exercises unexported yamlPathGet/yamlPathSet
|
|
|
|
import (
|
|
"strings"
|
|
"testing"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
"sneak.berlin/go/vaultik/internal/config"
|
|
)
|
|
|
|
// TestDefaultConfigTemplateParses ensures the init template is valid YAML
|
|
// that unmarshals into the Config struct with the expected snapshots.
|
|
func TestDefaultConfigTemplateParses(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
var cfg config.Config
|
|
|
|
err := yaml.Unmarshal([]byte(defaultConfigTemplate), &cfg)
|
|
if err != nil {
|
|
t.Fatalf("default config template is not valid YAML: %v", err)
|
|
}
|
|
|
|
if len(cfg.AgeRecipients) != 1 {
|
|
t.Errorf("expected 1 placeholder age recipient, got %d", len(cfg.AgeRecipients))
|
|
}
|
|
|
|
home, ok := cfg.Snapshots["home"]
|
|
if !ok {
|
|
t.Fatal("expected 'home' snapshot in default config")
|
|
}
|
|
|
|
if len(home.Paths) == 0 {
|
|
t.Error("home snapshot should have at least one path")
|
|
}
|
|
|
|
if len(home.Exclude) == 0 {
|
|
t.Error("home snapshot should have exclude patterns")
|
|
}
|
|
|
|
apps, ok := cfg.Snapshots["apps"]
|
|
if !ok {
|
|
t.Fatal("expected 'apps' snapshot in default config")
|
|
}
|
|
|
|
if len(apps.Paths) != 1 || apps.Paths[0] != "/Applications" {
|
|
t.Errorf("apps snapshot should back up /Applications, got %v", apps.Paths)
|
|
}
|
|
|
|
if len(apps.Exclude) == 0 {
|
|
t.Error("apps snapshot should have exclude patterns")
|
|
}
|
|
}
|
|
|
|
const testYAML = `# top comment
|
|
compression_level: 3
|
|
age_recipients:
|
|
- age1aaa
|
|
s3:
|
|
bucket: oldbucket # inline comment
|
|
region: us-east-1
|
|
snapshots:
|
|
home:
|
|
paths:
|
|
- "~"
|
|
`
|
|
|
|
func parseTestYAML(t *testing.T) *yaml.Node {
|
|
t.Helper()
|
|
|
|
var root yaml.Node
|
|
|
|
err := yaml.Unmarshal([]byte(testYAML), &root)
|
|
if err != nil {
|
|
t.Fatalf("parsing test yaml: %v", err)
|
|
}
|
|
|
|
return &root
|
|
}
|
|
|
|
func TestYAMLPathGet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := parseTestYAML(t)
|
|
|
|
tests := []struct {
|
|
path string
|
|
want string
|
|
err bool
|
|
}{
|
|
{"compression_level", "3", false},
|
|
{"s3.bucket", "oldbucket", false},
|
|
{"s3.region", "us-east-1", false},
|
|
{"age_recipients.0", "age1aaa", false},
|
|
{"age_recipients.5", "", true},
|
|
{"age_recipients.notanumber", "", true},
|
|
{"s3.nonexistent", "", true},
|
|
{"nonexistent", "", true},
|
|
{"compression_level.sub", "", true},
|
|
}
|
|
|
|
for _, tt := range tests {
|
|
t.Run(tt.path, func(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
node, err := yamlPathGet(root, splitPath(tt.path))
|
|
if tt.err {
|
|
if err == nil {
|
|
t.Fatalf("expected error for %q", tt.path)
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if node.Value != tt.want {
|
|
t.Errorf("get %q = %q, want %q", tt.path, node.Value, tt.want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestYAMLPathSet(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
root := parseTestYAML(t)
|
|
|
|
// Overwrite existing nested value
|
|
err := yamlPathSet(root, splitPath("s3.bucket"), "newbucket")
|
|
if err != nil {
|
|
t.Fatalf("set s3.bucket: %v", err)
|
|
}
|
|
|
|
// Create new nested key with intermediate map
|
|
err = yamlPathSet(root, splitPath("s3.endpoint"), "s3.example.com")
|
|
if err != nil {
|
|
t.Fatalf("set s3.endpoint: %v", err)
|
|
}
|
|
|
|
err = yamlPathSet(root, splitPath("newmap.newkey"), "val")
|
|
if err != nil {
|
|
t.Fatalf("set newmap.newkey: %v", err)
|
|
}
|
|
|
|
// Overwrite a sequence element and append a new one
|
|
err = yamlPathSet(root, splitPath("age_recipients.0"), "age1bbb")
|
|
if err != nil {
|
|
t.Fatalf("set age_recipients.0: %v", err)
|
|
}
|
|
|
|
err = yamlPathSet(root, splitPath("age_recipients.1"), "age1ccc")
|
|
if err != nil {
|
|
t.Fatalf("append age_recipients.1: %v", err)
|
|
}
|
|
|
|
err = yamlPathSet(root, splitPath("age_recipients.5"), "age1ddd")
|
|
if err == nil {
|
|
t.Error("expected out-of-range append to fail")
|
|
}
|
|
|
|
// Round-trip and verify values + comment preservation
|
|
out, err := yaml.Marshal(root)
|
|
if err != nil {
|
|
t.Fatalf("marshal: %v", err)
|
|
}
|
|
|
|
text := string(out)
|
|
|
|
wants := []string{
|
|
"newbucket", "s3.example.com", "newkey: val",
|
|
"# top comment", "# inline comment", "age1bbb", "age1ccc",
|
|
}
|
|
for _, want := range wants {
|
|
if !contains(text, want) {
|
|
t.Errorf("round-tripped YAML missing %q:\n%s", want, text)
|
|
}
|
|
}
|
|
|
|
got, err := yamlPathGet(root, splitPath("s3.bucket"))
|
|
if err != nil {
|
|
t.Fatalf("get after set: %v", err)
|
|
}
|
|
|
|
if got.Value != "newbucket" {
|
|
t.Errorf("s3.bucket = %q after set, want newbucket", got.Value)
|
|
}
|
|
}
|
|
|
|
func splitPath(s string) []string {
|
|
return strings.Split(s, ".")
|
|
}
|
|
|
|
func contains(haystack, needle string) bool {
|
|
return strings.Contains(haystack, needle)
|
|
}
|