Remediate all lint findings under the canonical golangci-lint config

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.
This commit is contained in:
2026-08-07 18:51:21 +00:00
parent 6cf9211407
commit 7ae470e530
121 changed files with 8344 additions and 5406 deletions

View File

@@ -3,6 +3,7 @@ package snapshot_test
import (
"context"
"database/sql"
"os"
"path/filepath"
"testing"
"time"
@@ -14,9 +15,114 @@ import (
"sneak.berlin/go/vaultik/internal/types"
)
// Shared test fixture values for the snapshot_test package.
const (
// testHost is the hostname recorded on test snapshot rows.
testHost = "test-host"
// testVersion is the vaultik version recorded on test snapshot rows.
testVersion = "test"
// testAgePublicKey is the fixed age public key used for test encryption.
testAgePublicKey = "age1ezrjmfpwsc95svdg0y54mums3zevgzu0x0ecq2f7tp8a05gl0sjq9q9wjg"
)
// TestMain initializes the shared logger once, before any tests run, so
// parallel tests never race on the logger's global state.
func TestMain(m *testing.M) {
log.Initialize(log.Config{})
os.Exit(m.Run())
}
// createTestSnapshotRecord inserts an empty snapshot row used as the
// association target for scan tests.
func createTestSnapshotRecord(
ctx context.Context, t *testing.T, repos *database.Repositories, snapshotID string,
) {
t.Helper()
err := repos.WithTx(ctx, func(ctx context.Context, tx *sql.Tx) error {
snapshot := &database.Snapshot{
ID: types.SnapshotID(snapshotID),
Hostname: testHost,
VaultikVersion: testVersion,
StartedAt: time.Now(),
CompletedAt: nil,
FileCount: 0,
ChunkCount: 0,
BlobCount: 0,
TotalSize: 0,
BlobSize: 0,
CompressionRatio: 1.0,
}
return repos.Snapshots.Create(ctx, tx, snapshot)
})
if err != nil {
t.Fatalf("failed to create snapshot: %v", err)
}
}
// verifySimpleScanDatabase checks the database contents produced by
// TestScannerSimpleDirectory's scan.
func verifySimpleScanDatabase(
ctx context.Context, t *testing.T, repos *database.Repositories,
) {
t.Helper()
// Verify files in database - includes regular files and directories
files, err := repos.Files.ListByPrefix(ctx, "/source")
if err != nil {
t.Fatalf("failed to list files: %v", err)
}
// 6 regular files + 3 directories (/source, /source/subdir, /source/subdir2)
if len(files) != 9 {
t.Errorf("expected 9 entries in database (6 files + 3 dirs), got %d", len(files))
}
// Verify specific file
file1, err := repos.Files.GetByPath(ctx, "/source/file1.txt")
if err != nil {
t.Fatalf("failed to get file1.txt: %v", err)
}
if file1.Size != 13 {
t.Errorf("expected file1.txt size 13, got %d", file1.Size)
}
if file1.Mode != 0644 {
t.Errorf("expected file1.txt mode 0644, got %o", file1.Mode)
}
// Verify chunks were created
chunks, err := repos.FileChunks.GetByFile(ctx, "/source/file1.txt")
if err != nil {
t.Fatalf("failed to get chunks for file1.txt: %v", err)
}
if len(chunks) != 1 { // Small file should be one chunk
t.Errorf("expected 1 chunk for file1.txt, got %d", len(chunks))
}
// Verify deduplication - file3.txt and file4.txt have different content
// but we should still have the correct number of unique chunks
allChunks, err := repos.Chunks.List(ctx)
if err != nil {
t.Fatalf("failed to list all chunks: %v", err)
}
// We should have at most 6 chunks (one per unique file content)
// Empty file might not create a chunk
if len(allChunks) > 6 {
t.Errorf("expected at most 6 chunks, got %d", len(allChunks))
}
}
func TestScannerSimpleDirectory(t *testing.T) {
// Initialize logger for tests
log.Initialize(log.Config{})
t.Parallel()
// Create in-memory filesystem
fs := afero.NewMemMapFs()
@@ -74,38 +180,17 @@ func TestScannerSimpleDirectory(t *testing.T) {
Repositories: repos,
MaxBlobSize: int64(1024 * 1024), // 1MB blobs
CompressionLevel: 3,
AgeRecipients: []string{"age1ezrjmfpwsc95svdg0y54mums3zevgzu0x0ecq2f7tp8a05gl0sjq9q9wjg"}, // Test public key
AgeRecipients: []string{testAgePublicKey},
})
// Create a snapshot record for testing
ctx := context.Background()
snapshotID := "test-snapshot-001"
err = repos.WithTx(ctx, func(ctx context.Context, tx *sql.Tx) error {
snapshot := &database.Snapshot{
ID: types.SnapshotID(snapshotID),
Hostname: "test-host",
VaultikVersion: "test",
StartedAt: time.Now(),
CompletedAt: nil,
FileCount: 0,
ChunkCount: 0,
BlobCount: 0,
TotalSize: 0,
BlobSize: 0,
CompressionRatio: 1.0,
}
return repos.Snapshots.Create(ctx, tx, snapshot)
})
if err != nil {
t.Fatalf("failed to create snapshot: %v", err)
}
createTestSnapshotRecord(ctx, t, repos, snapshotID)
// Scan the directory
var result *snapshot.ScanResult
result, err = scanner.Scan(ctx, "/source", snapshotID)
result, err := scanner.Scan(ctx, "/source", snapshotID)
if err != nil {
t.Fatalf("scan failed: %v", err)
}
@@ -120,58 +205,13 @@ func TestScannerSimpleDirectory(t *testing.T) {
t.Errorf("expected at least 97 bytes scanned, got %d", result.BytesScanned)
}
// Verify files in database - includes regular files and directories
files, err := repos.Files.ListByPrefix(ctx, "/source")
if err != nil {
t.Fatalf("failed to list files: %v", err)
}
// 6 regular files + 3 directories (/source, /source/subdir, /source/subdir2)
if len(files) != 9 {
t.Errorf("expected 9 entries in database (6 files + 3 dirs), got %d", len(files))
}
// Verify specific file
file1, err := repos.Files.GetByPath(ctx, "/source/file1.txt")
if err != nil {
t.Fatalf("failed to get file1.txt: %v", err)
}
if file1.Size != 13 {
t.Errorf("expected file1.txt size 13, got %d", file1.Size)
}
if file1.Mode != 0644 {
t.Errorf("expected file1.txt mode 0644, got %o", file1.Mode)
}
// Verify chunks were created
chunks, err := repos.FileChunks.GetByFile(ctx, "/source/file1.txt")
if err != nil {
t.Fatalf("failed to get chunks for file1.txt: %v", err)
}
if len(chunks) != 1 { // Small file should be one chunk
t.Errorf("expected 1 chunk for file1.txt, got %d", len(chunks))
}
// Verify deduplication - file3.txt and file4.txt have different content
// but we should still have the correct number of unique chunks
allChunks, err := repos.Chunks.List(ctx)
if err != nil {
t.Fatalf("failed to list all chunks: %v", err)
}
// We should have at most 6 chunks (one per unique file content)
// Empty file might not create a chunk
if len(allChunks) > 6 {
t.Errorf("expected at most 6 chunks, got %d", len(allChunks))
}
verifySimpleScanDatabase(ctx, t, repos)
}
func TestScannerLargeFile(t *testing.T) {
// Initialize logger for tests
log.Initialize(log.Config{})
t.Parallel()
// Create in-memory filesystem
fs := afero.NewMemMapFs()
@@ -182,14 +222,17 @@ func TestScannerLargeFile(t *testing.T) {
// Fill with pseudo-random data to ensure chunk boundaries
for i := range largeContent {
// Simple pseudo-random generator for deterministic tests
//nolint:gosec // G115: intentional byte truncation of test data
largeContent[i] = byte((i * 7919) ^ (i >> 3))
}
if err := fs.MkdirAll("/source", 0755); err != nil {
err := fs.MkdirAll("/source", 0755)
if err != nil {
t.Fatal(err)
}
if err := afero.WriteFile(fs, "/source/large.bin", largeContent, 0644); err != nil {
err = afero.WriteFile(fs, "/source/large.bin", largeContent, 0644)
if err != nil {
t.Fatal(err)
}
@@ -214,38 +257,17 @@ func TestScannerLargeFile(t *testing.T) {
Repositories: repos,
MaxBlobSize: int64(1024 * 1024),
CompressionLevel: 3,
AgeRecipients: []string{"age1ezrjmfpwsc95svdg0y54mums3zevgzu0x0ecq2f7tp8a05gl0sjq9q9wjg"}, // Test public key
AgeRecipients: []string{testAgePublicKey},
})
// Create a snapshot record for testing
ctx := context.Background()
snapshotID := "test-snapshot-001"
err = repos.WithTx(ctx, func(ctx context.Context, tx *sql.Tx) error {
snapshot := &database.Snapshot{
ID: types.SnapshotID(snapshotID),
Hostname: "test-host",
VaultikVersion: "test",
StartedAt: time.Now(),
CompletedAt: nil,
FileCount: 0,
ChunkCount: 0,
BlobCount: 0,
TotalSize: 0,
BlobSize: 0,
CompressionRatio: 1.0,
}
return repos.Snapshots.Create(ctx, tx, snapshot)
})
if err != nil {
t.Fatalf("failed to create snapshot: %v", err)
}
createTestSnapshotRecord(ctx, t, repos, snapshotID)
// Scan the directory
var result *snapshot.ScanResult
result, err = scanner.Scan(ctx, "/source", snapshotID)
result, err := scanner.Scan(ctx, "/source", snapshotID)
if err != nil {
t.Fatalf("scan failed: %v", err)
}
@@ -257,7 +279,8 @@ func TestScannerLargeFile(t *testing.T) {
// The file size should be at least 1MB
if result.BytesScanned < 1024*1024 {
t.Errorf("expected at least %d bytes scanned, got %d", 1024*1024, result.BytesScanned)
t.Errorf("expected at least %d bytes scanned, got %d",
1024*1024, result.BytesScanned)
}
// Verify chunks