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.
225 lines
4.8 KiB
Go
225 lines
4.8 KiB
Go
package database_test
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"testing"
|
|
"time"
|
|
|
|
"sneak.berlin/go/vaultik/internal/database"
|
|
"sneak.berlin/go/vaultik/internal/types"
|
|
)
|
|
|
|
func TestFileChunkRepository(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewFileChunkRepository(db)
|
|
fileRepo := database.NewFileRepository(db)
|
|
repos := database.NewRepositories(db)
|
|
|
|
// Create test file first
|
|
testTime := time.Now().Truncate(time.Second)
|
|
file := &database.File{
|
|
Path: testFileTxt,
|
|
MTime: testTime,
|
|
Size: 3072,
|
|
Mode: 0644,
|
|
UID: 1000,
|
|
GID: 1000,
|
|
LinkTarget: "",
|
|
}
|
|
|
|
mustCreateFile(t, fileRepo, file)
|
|
mustCreateChunks(t, repos, chunk1Hash, chunk2Hash, chunk3Hash)
|
|
|
|
// Test Create
|
|
fc1 := &database.FileChunk{
|
|
FileID: file.ID,
|
|
Idx: 0,
|
|
ChunkHash: types.ChunkHash(chunk1Hash),
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, fc1)
|
|
if err != nil {
|
|
t.Fatalf("failed to create file chunk: %v", err)
|
|
}
|
|
|
|
// Add more chunks for the same file
|
|
fc2 := &database.FileChunk{
|
|
FileID: file.ID,
|
|
Idx: 1,
|
|
ChunkHash: types.ChunkHash(chunk2Hash),
|
|
}
|
|
|
|
err = repo.Create(ctx, nil, fc2)
|
|
if err != nil {
|
|
t.Fatalf("failed to create second file chunk: %v", err)
|
|
}
|
|
|
|
fc3 := &database.FileChunk{
|
|
FileID: file.ID,
|
|
Idx: 2,
|
|
ChunkHash: types.ChunkHash(chunk3Hash),
|
|
}
|
|
|
|
err = repo.Create(ctx, nil, fc3)
|
|
if err != nil {
|
|
t.Fatalf("failed to create third file chunk: %v", err)
|
|
}
|
|
|
|
// Test GetByFile
|
|
fileChunks, err := repo.GetByFile(ctx, testFileTxt)
|
|
if err != nil {
|
|
t.Fatalf("failed to get file chunks: %v", err)
|
|
}
|
|
|
|
if len(fileChunks) != 3 {
|
|
t.Errorf("expected 3 chunks, got %d", len(fileChunks))
|
|
}
|
|
|
|
// Verify order
|
|
for i, chunk := range fileChunks {
|
|
if chunk.Idx != i {
|
|
t.Errorf("wrong chunk order: expected idx %d, got %d", i, chunk.Idx)
|
|
}
|
|
}
|
|
|
|
// Test duplicate insert (should be idempotent)
|
|
err = repo.Create(ctx, nil, fc1)
|
|
if err != nil {
|
|
t.Fatalf("failed to create duplicate file chunk: %v", err)
|
|
}
|
|
}
|
|
|
|
func TestFileChunkRepositoryDeleteByFileID(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewFileChunkRepository(db)
|
|
fileRepo := database.NewFileRepository(db)
|
|
repos := database.NewRepositories(db)
|
|
|
|
file := &database.File{
|
|
Path: testFileTxt,
|
|
MTime: time.Now().Truncate(time.Second),
|
|
Size: 1024,
|
|
Mode: 0644,
|
|
UID: 1000,
|
|
GID: 1000,
|
|
}
|
|
|
|
mustCreateFile(t, fileRepo, file)
|
|
mustCreateChunks(t, repos, chunk1Hash)
|
|
|
|
fc := &database.FileChunk{
|
|
FileID: file.ID,
|
|
Idx: 0,
|
|
ChunkHash: types.ChunkHash(chunk1Hash),
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, fc)
|
|
if err != nil {
|
|
t.Fatalf("failed to create file chunk: %v", err)
|
|
}
|
|
|
|
// Test DeleteByFileID
|
|
err = repo.DeleteByFileID(ctx, nil, file.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to delete file chunks: %v", err)
|
|
}
|
|
|
|
fileChunks, err := repo.GetByFileID(ctx, file.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get deleted file chunks: %v", err)
|
|
}
|
|
|
|
if len(fileChunks) != 0 {
|
|
t.Errorf("expected 0 chunks after delete, got %d", len(fileChunks))
|
|
}
|
|
}
|
|
|
|
func TestFileChunkRepositoryMultipleFiles(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
db, cleanup := setupTestDB(t)
|
|
defer cleanup()
|
|
|
|
ctx := context.Background()
|
|
repo := database.NewFileChunkRepository(db)
|
|
fileRepo := database.NewFileRepository(db)
|
|
|
|
// Create test files
|
|
testTime := time.Now().Truncate(time.Second)
|
|
filePaths := []string{testFilePath1, testFilePath2, "/file3.txt"}
|
|
files := make([]*database.File, len(filePaths))
|
|
|
|
for i, path := range filePaths {
|
|
file := &database.File{
|
|
Path: types.FilePath(path),
|
|
MTime: testTime,
|
|
Size: 2048,
|
|
Mode: 0644,
|
|
UID: 1000,
|
|
GID: 1000,
|
|
LinkTarget: "",
|
|
}
|
|
|
|
mustCreateFile(t, fileRepo, file)
|
|
|
|
files[i] = file
|
|
}
|
|
|
|
// Create all chunks first
|
|
chunkRepo := database.NewChunkRepository(db)
|
|
|
|
for i := range files {
|
|
for j := range 2 {
|
|
chunkHash := types.ChunkHash(fmt.Sprintf("file%d_chunk%d", i, j))
|
|
chunk := &database.Chunk{
|
|
ChunkHash: chunkHash,
|
|
Size: 1024,
|
|
}
|
|
|
|
err := chunkRepo.Create(ctx, nil, chunk)
|
|
if err != nil {
|
|
t.Fatalf("failed to create chunk %s: %v", chunkHash, err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Create chunks for multiple files
|
|
for i, file := range files {
|
|
for j := range 2 {
|
|
fc := &database.FileChunk{
|
|
FileID: file.ID,
|
|
Idx: j,
|
|
ChunkHash: types.ChunkHash(fmt.Sprintf("file%d_chunk%d", i, j)),
|
|
}
|
|
|
|
err := repo.Create(ctx, nil, fc)
|
|
if err != nil {
|
|
t.Fatalf("failed to create file chunk: %v", err)
|
|
}
|
|
}
|
|
}
|
|
|
|
// Verify each file has correct chunks
|
|
for i, file := range files {
|
|
chunks, err := repo.GetByFileID(ctx, file.ID)
|
|
if err != nil {
|
|
t.Fatalf("failed to get chunks for file %d: %v", i, err)
|
|
}
|
|
|
|
if len(chunks) != 2 {
|
|
t.Errorf("expected 2 chunks for file %d, got %d", i, len(chunks))
|
|
}
|
|
}
|
|
}
|