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.
253 lines
6.2 KiB
Go
253 lines
6.2 KiB
Go
package database_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"math"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/vaultik/internal/database"
|
|
"sneak.berlin/go/vaultik/internal/types"
|
|
)
|
|
|
|
const (
|
|
Mebibyte = 1024 * 1024
|
|
oneHundredMebibytes = 100 * Mebibyte
|
|
fortyMebibytes = 40 * Mebibyte
|
|
sixtyMebibytes = 60 * Mebibyte
|
|
twoHundredMebibytes = 200 * Mebibyte
|
|
compressionRatioPoint4 = 0.4
|
|
compressionRatioPoint3 = 0.3
|
|
)
|
|
|
|
func TestSnapshotRepository(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewSnapshotRepository(db)
|
|
|
|
// Test Create
|
|
snapshot := &database.Snapshot{
|
|
ID: "2024-01-01T12:00:00Z",
|
|
Hostname: testHostname,
|
|
VaultikVersion: testVersion,
|
|
StartedAt: time.Now().Truncate(time.Second),
|
|
CompletedAt: nil,
|
|
FileCount: 100,
|
|
ChunkCount: 500,
|
|
BlobCount: 10,
|
|
TotalSize: oneHundredMebibytes,
|
|
BlobSize: fortyMebibytes,
|
|
CompressionRatio: compressionRatioPoint4, // 40MB / 100MB
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, snapshot)
|
|
if err != nil {
|
|
t.Fatalf("failed to create snapshot: %v", err)
|
|
}
|
|
|
|
// Test GetByID
|
|
retrieved, err := repo.GetByID(ctx, snapshot.ID.String())
|
|
if err != nil {
|
|
t.Fatalf("failed to get snapshot: %v", err)
|
|
}
|
|
|
|
if retrieved == nil {
|
|
t.Fatal("expected snapshot, got nil")
|
|
}
|
|
|
|
if retrieved.ID != snapshot.ID {
|
|
t.Errorf("ID mismatch: got %s, want %s", retrieved.ID, snapshot.ID)
|
|
}
|
|
|
|
if retrieved.Hostname != snapshot.Hostname {
|
|
t.Errorf("hostname mismatch: got %s, want %s",
|
|
retrieved.Hostname, snapshot.Hostname)
|
|
}
|
|
|
|
if retrieved.FileCount != snapshot.FileCount {
|
|
t.Errorf("file count mismatch: got %d, want %d",
|
|
retrieved.FileCount, snapshot.FileCount)
|
|
}
|
|
}
|
|
|
|
func TestSnapshotRepositoryUpdateCounts(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewSnapshotRepository(db)
|
|
|
|
snapshot := &database.Snapshot{
|
|
ID: "2024-01-02T12:00:00Z",
|
|
Hostname: testHostname,
|
|
VaultikVersion: testVersion,
|
|
StartedAt: time.Now().Truncate(time.Second),
|
|
CompletedAt: nil,
|
|
FileCount: 100,
|
|
ChunkCount: 500,
|
|
BlobCount: 10,
|
|
TotalSize: oneHundredMebibytes,
|
|
BlobSize: fortyMebibytes,
|
|
CompressionRatio: compressionRatioPoint4,
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, snapshot)
|
|
if err != nil {
|
|
t.Fatalf("failed to create snapshot: %v", err)
|
|
}
|
|
|
|
// Test UpdateCounts
|
|
err = repo.UpdateCounts(ctx, nil, snapshot.ID.String(),
|
|
200, 1000, 20, twoHundredMebibytes, sixtyMebibytes)
|
|
if err != nil {
|
|
t.Fatalf("failed to update counts: %v", err)
|
|
}
|
|
|
|
retrieved, err := repo.GetByID(ctx, snapshot.ID.String())
|
|
if err != nil {
|
|
t.Fatalf("failed to get updated snapshot: %v", err)
|
|
}
|
|
|
|
if retrieved.FileCount != 200 {
|
|
t.Errorf("file count not updated: got %d, want %d", retrieved.FileCount, 200)
|
|
}
|
|
|
|
if retrieved.ChunkCount != 1000 {
|
|
t.Errorf("chunk count not updated: got %d, want %d",
|
|
retrieved.ChunkCount, 1000)
|
|
}
|
|
|
|
if retrieved.BlobCount != 20 {
|
|
t.Errorf("blob count not updated: got %d, want %d", retrieved.BlobCount, 20)
|
|
}
|
|
|
|
if retrieved.TotalSize != twoHundredMebibytes {
|
|
t.Errorf("total size not updated: got %d, want %d",
|
|
retrieved.TotalSize, twoHundredMebibytes)
|
|
}
|
|
|
|
if retrieved.BlobSize != sixtyMebibytes {
|
|
t.Errorf("blob size not updated: got %d, want %d",
|
|
retrieved.BlobSize, sixtyMebibytes)
|
|
}
|
|
|
|
expectedRatio := compressionRatioPoint3 // 0.3
|
|
if math.Abs(retrieved.CompressionRatio-expectedRatio) > 0.001 {
|
|
t.Errorf("compression ratio not updated: got %f, want %f",
|
|
retrieved.CompressionRatio, expectedRatio)
|
|
}
|
|
}
|
|
|
|
func TestSnapshotRepositoryListRecent(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewSnapshotRepository(db)
|
|
|
|
// Add snapshots
|
|
for i := 1; i <= 5; i++ {
|
|
s := &database.Snapshot{
|
|
ID: types.SnapshotID(fmt.Sprintf("2024-01-0%dT12:00:00Z", i)),
|
|
Hostname: testHostname,
|
|
VaultikVersion: testVersion,
|
|
StartedAt: time.Now().Add(time.Duration(i) * time.Hour).Truncate(time.Second),
|
|
CompletedAt: nil,
|
|
FileCount: int64(100 * i),
|
|
ChunkCount: int64(500 * i),
|
|
BlobCount: int64(10 * i),
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, s)
|
|
if err != nil {
|
|
t.Fatalf("failed to create snapshot %d: %v", i, err)
|
|
}
|
|
}
|
|
|
|
// Test listing with limit
|
|
recent, err := repo.ListRecent(ctx, 3)
|
|
if err != nil {
|
|
t.Fatalf("failed to list recent snapshots: %v", err)
|
|
}
|
|
|
|
if len(recent) != 3 {
|
|
t.Errorf("expected 3 recent snapshots, got %d", len(recent))
|
|
}
|
|
|
|
// Verify order (most recent first)
|
|
for i := range len(recent) - 1 {
|
|
if recent[i].StartedAt.Before(recent[i+1].StartedAt) {
|
|
t.Error("snapshots not in descending order")
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestSnapshotRepositoryNotFound(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewSnapshotRepository(db)
|
|
|
|
// Test GetByID with non-existent ID
|
|
snapshot, err := repo.GetByID(ctx, "nonexistent")
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
|
|
if snapshot != nil {
|
|
t.Error("expected nil for non-existent snapshot")
|
|
}
|
|
|
|
// Test UpdateCounts on non-existent snapshot
|
|
err = repo.UpdateCounts(ctx, nil, "nonexistent",
|
|
100, 200, 10, oneHundredMebibytes, fortyMebibytes)
|
|
if err != nil {
|
|
t.Fatalf("unexpected error: %v", err)
|
|
}
|
|
// No error expected, but no rows should be affected
|
|
}
|
|
|
|
func TestSnapshotRepositoryDuplicate(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewSnapshotRepository(db)
|
|
|
|
snapshot := &database.Snapshot{
|
|
ID: "2024-01-01T12:00:00Z",
|
|
Hostname: testHostname,
|
|
VaultikVersion: testVersion,
|
|
StartedAt: time.Now().Truncate(time.Second),
|
|
CompletedAt: nil,
|
|
FileCount: 100,
|
|
ChunkCount: 500,
|
|
BlobCount: 10,
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, snapshot)
|
|
if err != nil {
|
|
t.Fatalf("failed to create snapshot: %v", err)
|
|
}
|
|
|
|
// Try to create duplicate - should fail due to primary key constraint
|
|
err = repo.Create(ctx, nil, snapshot)
|
|
if err == nil {
|
|
t.Error("expected error for duplicate snapshot")
|
|
}
|
|
}
|