From 1071058f4e3bf76d336ca1e6ff2056bdf2475693 Mon Sep 17 00:00:00 2001 From: sneak Date: Mon, 21 Sep 2026 13:05:22 +0000 Subject: [PATCH] Delete dead code and stale fixtures, fix config set reindent (closes #70) Remove the unused internal/models package (and its coverage-only test), internal/cli/vaultik_snapshot_types.go (a second, dead SnapshotInfo that shadowed the live type in internal/vaultik), and two fixtures (test-config.yml, test/integration-config.yml) that use source_dirs and other keys config.Config no longer has. All confirmed by grep against origin/next; none has a production consumer. config set now writes via yaml.NewEncoder with 2-space indent, matching defaultConfigTemplate, so the first set no longer reindents the file it promises to preserve. A test covers the round-trip. Single commit rather than one commit per deletion, per task instruction. test/integration-config.yml removed per the manager note on the issue. Model: opus-4-8 --- internal/cli/config.go | 31 +++++++++++- internal/cli/config_test.go | 41 ++++++++++++++++ internal/cli/vaultik_snapshot_types.go | 12 ----- internal/models/models.go | 67 -------------------------- internal/models/models_test.go | 58 ---------------------- test-config.yml | 27 ----------- test/integration-config.yml | 24 --------- 7 files changed, 71 insertions(+), 189 deletions(-) delete mode 100644 internal/cli/vaultik_snapshot_types.go delete mode 100644 internal/models/models.go delete mode 100644 internal/models/models_test.go delete mode 100644 test-config.yml delete mode 100644 test/integration-config.yml diff --git a/internal/cli/config.go b/internal/cli/config.go index 11c05b1..98fc46a 100644 --- a/internal/cli/config.go +++ b/internal/cli/config.go @@ -1,6 +1,7 @@ package cli import ( + "bytes" "errors" "fmt" "os" @@ -24,6 +25,11 @@ const configSetArgs = 2 // parent config dirs (e.g. ~/.config) are conventionally traversable. const configDirMode = 0o755 +// configYAMLIndent matches the 2-space indentation of defaultConfigTemplate, +// so `config set` writes the file back with the same indentation rather than +// yaml.Marshal's 4-space default. +const configYAMLIndent = 2 + var ( errConfigExists = errors.New("config file already exists") errEmptyConfig = errors.New("empty config file") @@ -381,7 +387,7 @@ Examples: return err } - out, err := yaml.Marshal(root) + out, err := marshalConfigYAML(root) if err != nil { return fmt.Errorf("marshaling config: %w", err) } @@ -405,6 +411,29 @@ Examples: } } +// marshalConfigYAML renders a config document tree with 2-space indentation, +// matching defaultConfigTemplate. yaml.Marshal defaults to 4 spaces, which +// would reindent the whole file on the first `config set` despite the promise +// to preserve formatting. +func marshalConfigYAML(root *yaml.Node) ([]byte, error) { + var buf bytes.Buffer + + enc := yaml.NewEncoder(&buf) + enc.SetIndent(configYAMLIndent) + + err := enc.Encode(root) + if err != nil { + return nil, err + } + + err = enc.Close() + if err != nil { + return nil, err + } + + return buf.Bytes(), nil +} + // loadYAMLFile parses a YAML file into a yaml.Node document tree, // which preserves comments and ordering for round-tripping. func loadYAMLFile(path string) (*yaml.Node, error) { diff --git a/internal/cli/config_test.go b/internal/cli/config_test.go index f717841..2bffceb 100644 --- a/internal/cli/config_test.go +++ b/internal/cli/config_test.go @@ -188,6 +188,47 @@ func TestYAMLPathSet(t *testing.T) { } } +// TestConfigSetPreservesFormatting asserts the `config set` write path +// (marshalConfigYAML) round-trips a 2-space-indented file without reindenting +// it to yaml.Marshal's 4-space default, and keeps comments. +func TestConfigSetPreservesFormatting(t *testing.T) { + t.Parallel() + + root := parseTestYAML(t) + + err := yamlPathSet(root, splitPath("s3.bucket"), "newbucket") + if err != nil { + t.Fatalf("set s3.bucket: %v", err) + } + + out, err := marshalConfigYAML(root) + if err != nil { + t.Fatalf("marshal: %v", err) + } + + text := string(out) + + for _, want := range []string{"# top comment", "# inline comment"} { + if !contains(text, want) { + t.Errorf("round-tripped YAML dropped comment %q:\n%s", want, text) + } + } + + // Nested map keys stay at 2-space indent; the bug reindented them to 4. + if !contains(text, "\n bucket: newbucket") { + t.Errorf("expected 2-space indent for s3.bucket, got:\n%s", text) + } + + if contains(text, "\n bucket:") { + t.Errorf("s3.bucket reindented to 4 spaces:\n%s", text) + } + + // Sequence items under a key also stay at 2 spaces. + if !contains(text, "\n - age1aaa") { + t.Errorf("expected 2-space indent for sequence item, got:\n%s", text) + } +} + func splitPath(s string) []string { return strings.Split(s, ".") } diff --git a/internal/cli/vaultik_snapshot_types.go b/internal/cli/vaultik_snapshot_types.go deleted file mode 100644 index 1f9d4dd..0000000 --- a/internal/cli/vaultik_snapshot_types.go +++ /dev/null @@ -1,12 +0,0 @@ -package cli - -import "time" - -// SnapshotInfo represents snapshot information for listing -// -//nolint:tagliatelle // snake_case is the established output format -type SnapshotInfo struct { - ID string `json:"id"` - Timestamp time.Time `json:"timestamp"` - CompressedSize int64 `json:"compressed_size"` -} diff --git a/internal/models/models.go b/internal/models/models.go deleted file mode 100644 index 819402a..0000000 --- a/internal/models/models.go +++ /dev/null @@ -1,67 +0,0 @@ -// Package models defines shared value types describing files, chunks, -// blobs, and snapshots as they move through the backup pipeline. -package models - -import ( - "time" -) - -// FileInfo represents a file in the backup system -type FileInfo struct { - Path string - MTime time.Time - Size int64 -} - -// ChunkInfo represents a content-addressed chunk -type ChunkInfo struct { - Hash string // SHA256 hash - Size int64 - Offset int64 // Offset within source file -} - -// ChunkRef represents a reference to a chunk in a blob or file -type ChunkRef struct { - ChunkHash string - Offset int64 - Length int64 -} - -// BlobInfo represents an encrypted blob containing multiple chunks -type BlobInfo struct { - Hash string // SHA256 hash of the blob content (content-addressable) - CreatedAt time.Time - Size int64 - ChunkCount int -} - -// Snapshot represents a backup snapshot -type Snapshot struct { - ID string // ISO8601 timestamp - Hostname string - Version string - CreatedAt time.Time - FileCount int64 - ChunkCount int64 - BlobCount int64 - TotalSize int64 - MetadataSize int64 -} - -// SnapshotMetadata contains the full metadata for a snapshot -type SnapshotMetadata struct { - Snapshot *Snapshot - Files map[string]*FileInfo - Chunks map[string]*ChunkInfo - Blobs map[string]*BlobInfo - FileChunks map[string][]*ChunkRef // path -> chunks - BlobChunks map[string][]*ChunkRef // blob hash -> chunks -} - -// Chunk represents a data chunk for processing -type Chunk struct { - Data []byte - Hash string - Offset int64 - Length int64 -} diff --git a/internal/models/models_test.go b/internal/models/models_test.go deleted file mode 100644 index b5c4cab..0000000 --- a/internal/models/models_test.go +++ /dev/null @@ -1,58 +0,0 @@ -package models_test - -import ( - "testing" - "time" - - "sneak.berlin/go/vaultik/internal/models" -) - -// TestModelsCompilation ensures all model types can be instantiated -func TestModelsCompilation(t *testing.T) { - t.Parallel() - - // This test primarily serves as a compilation test - // to ensure all types are properly defined - - // Test FileInfo - fi := &models.FileInfo{ - Path: "/test/file.txt", - MTime: time.Now(), - Size: 1024, - } - if fi.Path != "/test/file.txt" { - t.Errorf("FileInfo.Path not set correctly") - } - - // Test ChunkInfo - ci := &models.ChunkInfo{ - Hash: "abc123", - Size: 512, - Offset: 0, - } - if ci.Hash != "abc123" { - t.Errorf("ChunkInfo.Hash not set correctly") - } - - // Test BlobInfo - bi := &models.BlobInfo{ - Hash: "blob123", - CreatedAt: time.Now(), - Size: 1024, - ChunkCount: 2, - } - if bi.Hash != "blob123" { - t.Errorf("BlobInfo.Hash not set correctly") - } - - // Test Snapshot - s := &models.Snapshot{ - ID: "2024-01-01T00:00:00Z", - Hostname: "test-host", - Version: "1.0.0", - CreatedAt: time.Now(), - } - if s.ID != "2024-01-01T00:00:00Z" { - t.Errorf("Snapshot.ID not set correctly") - } -} diff --git a/test-config.yml b/test-config.yml deleted file mode 100644 index ab7ce46..0000000 --- a/test-config.yml +++ /dev/null @@ -1,27 +0,0 @@ -# Vaultik test configuration -hostname: test-host -index_path: /tmp/vaultik-test/index.db -source_dirs: - - /tmp/vaultik-test/source - -# S3 configuration -s3: - endpoint: http://localhost:19000 # gofakes3 test endpoint - bucket: test-bucket - prefix: test- - access_key_id: test-key - secret_access_key: test-secret - region: us-east-1 - -# Chunking configuration -chunk_size: 65536 # 64KB average chunk size -min_chunk_size: 32768 # 32KB minimum -max_chunk_size: 131072 # 128KB maximum -blob_size: 1048576 # 1MB blobs for testing - -# Compression -compression_level: 3 - -# Encryption -# age_recipients: -# - age1qyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqszqgpqyqs3mw88h \ No newline at end of file diff --git a/test/integration-config.yml b/test/integration-config.yml deleted file mode 100644 index a716482..0000000 --- a/test/integration-config.yml +++ /dev/null @@ -1,24 +0,0 @@ -age_recipients: - - age1278m9q7dp3chsh2dcy82qk27v047zywyvtxwnj4cvt0z65jw6a7q5dqhfj # sneak's long term age key - - age1ezrjmfpwsc95svdg0y54mums3zevgzu0x0ecq2f7tp8a05gl0sjq9q9wjg # insecure integration test key -source_dirs: - - /tmp/vaultik-test-source -exclude: - - '*.log' - - '*.tmp' - - '.git' - - 'node_modules' -s3: - endpoint: http://ber1app1.local:3900/ - bucket: vaultik-integration-test - prefix: test-host/ - access_key_id: GKbc8e6d35fdf50847f155aca5 - secret_access_key: 217046bee47c050301e3cc13e3cba1a8a943cf5f37f8c7979c349c5254441d18 - region: us-east-1 - use_ssl: false - part_size: 5242880 # 5MB -index_path: /tmp/vaultik-integration-test.sqlite -chunk_size: 10MB -blob_size_limit: 10GB -compression_level: 3 -hostname: test-host \ No newline at end of file -- 2.54.0