diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-14 11:22:02 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-14 11:22:02 +0300 |
| commit | c19666bd44b938ab2627b0c85935d3877c88b373 (patch) | |
| tree | 434e7d2942cd79428106c2f8ede74d8309f1a6d3 /internal | |
| parent | 55593e14ee2a4225d1db1058da9d8d1f663225b6 (diff) | |
refactor: drop goprecords DB pass-through, use storage from CLI (x3)
Remove OpenDB/CreateSchema/ResetRecords/ImportFromDir/ImportFromFS
wrappers and sqlite blank import from goprecords; keep LoadAggregates.
CLI and integration tests call storage.* directly. Storage-focused tests
live under internal/storage.
Made-with: Cursor
Diffstat (limited to 'internal')
| -rw-r--r-- | internal/cli/cli.go | 9 | ||||
| -rw-r--r-- | internal/goprecords/db.go | 29 | ||||
| -rw-r--r-- | internal/goprecords/db_test.go | 201 | ||||
| -rw-r--r-- | internal/goprecords/integration_test_runner.go | 8 | ||||
| -rw-r--r-- | internal/goprecords/integration_test_runner_test.go | 4 | ||||
| -rw-r--r-- | internal/storage/db_test.go | 169 |
6 files changed, 192 insertions, 228 deletions
diff --git a/internal/cli/cli.go b/internal/cli/cli.go index 7dd7cb6..81ebc40 100644 --- a/internal/cli/cli.go +++ b/internal/cli/cli.go @@ -12,6 +12,7 @@ import ( "codeberg.org/snonux/goprecords/internal/authkeys" "codeberg.org/snonux/goprecords/internal/daemon" "codeberg.org/snonux/goprecords/internal/goprecords" + "codeberg.org/snonux/goprecords/internal/storage" "codeberg.org/snonux/goprecords/internal/version" ) @@ -62,15 +63,15 @@ func runImport(args []string) error { return fmt.Errorf("missing -stats-dir") } ctx := context.Background() - db, err := goprecords.OpenDB(ctx, *dbPath) + db, err := storage.Open(ctx, *dbPath) if err != nil { return fmt.Errorf("open db: %w", err) } defer db.Close() - if err := goprecords.CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { return fmt.Errorf("schema: %w", err) } - if err := goprecords.ImportFromDir(ctx, db, *statsDir); err != nil { + if err := storage.ImportFromDir(ctx, db, *statsDir); err != nil { return fmt.Errorf("import: %w", err) } fmt.Fprintf(os.Stderr, "imported %s into %s\n", *statsDir, *dbPath) @@ -85,7 +86,7 @@ func runQuery(args []string) error { return err } ctx := context.Background() - db, err := goprecords.OpenDB(ctx, *dbPath) + db, err := storage.Open(ctx, *dbPath) if err != nil { return fmt.Errorf("open db: %w", err) } diff --git a/internal/goprecords/db.go b/internal/goprecords/db.go index 2bcfb78..b2b095b 100644 --- a/internal/goprecords/db.go +++ b/internal/goprecords/db.go @@ -4,39 +4,10 @@ import ( "context" "database/sql" "fmt" - "io/fs" "codeberg.org/snonux/goprecords/internal/storage" - _ "modernc.org/sqlite" ) -// OpenDB opens the SQLite database at path, creating the file if needed. -func OpenDB(ctx context.Context, path string) (*sql.DB, error) { - return storage.Open(ctx, path) -} - -// CreateSchema creates the record table and indexes (idempotent). -func CreateSchema(ctx context.Context, db *sql.DB) error { - return storage.CreateSchema(ctx, db) -} - -// ResetRecords removes all rows so import is repeatable. -func ResetRecords(ctx context.Context, db *sql.DB) error { - return storage.ResetRecords(ctx, db) -} - -// ImportFromDir reads all .records files from statsDir and inserts into the DB. -// Resets the record table first so the run is repeatable. -func ImportFromDir(ctx context.Context, db *sql.DB, statsDir string) error { - return storage.ImportFromDir(ctx, db, statsDir) -} - -// ImportFromFS reads all non-empty .records files from the root of fsys and inserts into the DB. -// Resets the record table first so the run is repeatable. -func ImportFromFS(ctx context.Context, db *sql.DB, fsys fs.FS) error { - return storage.ImportFromFS(ctx, db, fsys) -} - // LoadAggregates reads all rows from the DB and builds Aggregates (same shape as file-based aggregation). func LoadAggregates(ctx context.Context, db *sql.DB) (*Aggregates, error) { records, err := storage.LoadRecords(ctx, db) diff --git a/internal/goprecords/db_test.go b/internal/goprecords/db_test.go index 0edbae9..3d6b788 100644 --- a/internal/goprecords/db_test.go +++ b/internal/goprecords/db_test.go @@ -3,207 +3,28 @@ package goprecords import ( "context" "math" - "os" "path/filepath" "strings" "testing" - "testing/fstest" -) - -func TestOpenDB(t *testing.T) { - tmpDir := t.TempDir() - dbPath := filepath.Join(tmpDir, "test.db") - - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("failed to open DB: %v", err) - } - defer db.Close() - - if db == nil { - t.Error("expected non-nil database") - } -} - -func TestCreateSchema(t *testing.T) { - tmpDir := t.TempDir() - dbPath := filepath.Join(tmpDir, "test.db") - - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("failed to open DB: %v", err) - } - defer db.Close() - - ctx := context.Background() - err = CreateSchema(ctx, db) - if err != nil { - t.Fatalf("failed to create schema: %v", err) - } - // Verify schema was created by checking if we can query it - _, err = db.ExecContext(ctx, "SELECT 1 FROM record LIMIT 1") - if err != nil { - t.Fatalf("failed to query record table: %v", err) - } -} - -func TestResetRecords(t *testing.T) { - tmpDir := t.TempDir() - dbPath := filepath.Join(tmpDir, "test.db") - - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("failed to open DB: %v", err) - } - defer db.Close() - - ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { - t.Fatalf("failed to create schema: %v", err) - } - - // Insert a record - _, err = db.ExecContext(ctx, - "INSERT INTO record (host, uptime_sec, boot_time, os, os_kernel_name, os_kernel_major) VALUES (?, ?, ?, ?, ?, ?)", - "host1", 1000, 2000, "Linux 5.10", "Linux", "Linux 5...") - if err != nil { - t.Fatalf("failed to insert record: %v", err) - } - - // Reset records - err = ResetRecords(ctx, db) - if err != nil { - t.Fatalf("failed to reset records: %v", err) - } - - // Verify records are empty - var count int - err = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count) - if err != nil { - t.Fatalf("failed to count records: %v", err) - } - - if count != 0 { - t.Errorf("expected 0 records after reset, got %d", count) - } -} - -func TestImportFromDir(t *testing.T) { - // Create temp directory with test records - tmpDir := t.TempDir() - - // Create a test records file - recordsFile := filepath.Join(tmpDir, "testhost.records") - content := []byte("86400:1000000:Linux 5.10.0-test\n" + - "86400:1000001:Linux 5.10.0-test\n" + - "86400:1000002:Linux 5.10.0-test\n") - - if err := os.WriteFile(recordsFile, content, 0644); err != nil { - t.Fatalf("failed to create test file: %v", err) - } - - // Create database - dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("failed to open DB: %v", err) - } - defer db.Close() - - ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { - t.Fatalf("failed to create schema: %v", err) - } - - // Import records - err = ImportFromDir(ctx, db, tmpDir) - if err != nil { - t.Fatalf("failed to import records: %v", err) - } - - // Verify records were imported - var count int - err = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count) - if err != nil { - t.Fatalf("failed to count records: %v", err) - } - - if count != 3 { - t.Errorf("expected 3 records after import, got %d", count) - } -} - -func TestImportFromFS_MapFS(t *testing.T) { - tmpDir := t.TempDir() - dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("open DB: %v", err) - } - defer db.Close() - ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { - t.Fatalf("schema: %v", err) - } - m := fstest.MapFS{ - "testhost.records": &fstest.MapFile{ - Data: []byte("86400:1000000:Linux 5.10.0-test\n" + - "86400:1000001:Linux 5.10.0-test\n" + - "86400:1000002:Linux 5.10.0-test\n"), - Mode: 0o644, - }, - } - if err := ImportFromFS(ctx, db, m); err != nil { - t.Fatalf("ImportFromFS: %v", err) - } - var count int - if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count); err != nil { - t.Fatal(err) - } - if count != 3 { - t.Errorf("count = %d, want 3", count) - } -} - -func TestImportFromDirInvalidPath(t *testing.T) { - tmpDir := t.TempDir() - dbPath := filepath.Join(tmpDir, "test.db") - - db, err := OpenDB(context.Background(), dbPath) - if err != nil { - t.Fatalf("failed to open DB: %v", err) - } - defer db.Close() - - ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { - t.Fatalf("failed to create schema: %v", err) - } - - // Try to import from non-existent directory - err = ImportFromDir(ctx, db, "/nonexistent/path") - if err == nil { - t.Error("expected error for non-existent directory") - } -} + "codeberg.org/snonux/goprecords/internal/storage" +) func TestLoadAggregates(t *testing.T) { tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) + db, err := storage.Open(context.Background(), dbPath) if err != nil { t.Fatalf("failed to open DB: %v", err) } defer db.Close() ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { t.Fatalf("failed to create schema: %v", err) } - // Insert some records _, err = db.ExecContext(ctx, "INSERT INTO record (host, uptime_sec, boot_time, os, os_kernel_name, os_kernel_major) VALUES (?, ?, ?, ?, ?, ?)", "host1", 1000, 2000, "Linux 5.10", "Linux", "Linux 5...") @@ -218,7 +39,6 @@ func TestLoadAggregates(t *testing.T) { t.Fatalf("failed to insert: %v", err) } - // Load aggregates aggs, err := LoadAggregates(ctx, db) if err != nil { t.Fatalf("failed to load aggregates: %v", err) @@ -249,14 +69,14 @@ func TestLoadAggregatesLastKernelMaxBootNearInt64(t *testing.T) { tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) + db, err := storage.Open(context.Background(), dbPath) if err != nil { t.Fatalf("failed to open DB: %v", err) } defer db.Close() ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { t.Fatalf("failed to create schema: %v", err) } @@ -295,18 +115,17 @@ func TestLoadAggregatesEmptyDB(t *testing.T) { tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) + db, err := storage.Open(context.Background(), dbPath) if err != nil { t.Fatalf("failed to open DB: %v", err) } defer db.Close() ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { t.Fatalf("failed to create schema: %v", err) } - // Load from empty database aggs, err := LoadAggregates(ctx, db) if err != nil { t.Fatalf("failed to load aggregates: %v", err) @@ -325,13 +144,13 @@ func TestLoadAggregatesWrapsLoadRecordsError(t *testing.T) { tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "test.db") - db, err := OpenDB(context.Background(), dbPath) + db, err := storage.Open(context.Background(), dbPath) if err != nil { t.Fatalf("failed to open DB: %v", err) } ctx := context.Background() - if err := CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { db.Close() t.Fatalf("failed to create schema: %v", err) } diff --git a/internal/goprecords/integration_test_runner.go b/internal/goprecords/integration_test_runner.go index 0d07276..448dcd7 100644 --- a/internal/goprecords/integration_test_runner.go +++ b/internal/goprecords/integration_test_runner.go @@ -5,6 +5,8 @@ import ( "database/sql" "fmt" "os" + + "codeberg.org/snonux/goprecords/internal/storage" ) // RunIntegrationTests runs integration tests against fixture data. @@ -94,7 +96,7 @@ func testImportExport(ctx context.Context, aggregates *Aggregates, fixturesDir s _ = os.Remove(tmpDB) return 1 } - db, err := OpenDB(ctx, tmpDB) + db, err := storage.Open(ctx, tmpDB) if err != nil { _ = os.Remove(tmpDB) fmt.Printf("FAIL: open tmp db: %v\n", err) @@ -108,12 +110,12 @@ func testImportExportOnDB(ctx context.Context, db *sql.DB, tmpDB string, aggrega db.Close() _ = os.Remove(tmpDB) }() - if err := CreateSchema(ctx, db); err != nil { + if err := storage.CreateSchema(ctx, db); err != nil { fmt.Printf("FAIL: create schema: %v\n", err) return 1 } failed := 0 - if err := ImportFromDir(ctx, db, fixturesDir); err != nil { + if err := storage.ImportFromDir(ctx, db, fixturesDir); err != nil { fmt.Printf("FAIL: import: %v\n", err) return 1 } diff --git a/internal/goprecords/integration_test_runner_test.go b/internal/goprecords/integration_test_runner_test.go index 29de945..20476b7 100644 --- a/internal/goprecords/integration_test_runner_test.go +++ b/internal/goprecords/integration_test_runner_test.go @@ -4,6 +4,8 @@ import ( "context" "path/filepath" "testing" + + "codeberg.org/snonux/goprecords/internal/storage" ) func TestTestImportExportOnDB_createSchemaError(t *testing.T) { @@ -16,7 +18,7 @@ func TestTestImportExportOnDB_createSchemaError(t *testing.T) { } tmpDir := t.TempDir() dbPath := filepath.Join(tmpDir, "import.db") - db, err := OpenDB(ctx, dbPath) + db, err := storage.Open(ctx, dbPath) if err != nil { t.Fatal(err) } diff --git a/internal/storage/db_test.go b/internal/storage/db_test.go index 5415235..b05b4c7 100644 --- a/internal/storage/db_test.go +++ b/internal/storage/db_test.go @@ -6,6 +6,7 @@ import ( "os" "path/filepath" "testing" + "testing/fstest" ) func TestOpen_ContextCanceled(t *testing.T) { @@ -32,3 +33,171 @@ func TestOpen_PingFailsOnDirectoryPath(t *testing.T) { t.Fatal("expected error opening sqlite at directory path") } } + +func TestOpen_createsDatabaseFile(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("failed to open DB: %v", err) + } + defer db.Close() + + if db == nil { + t.Error("expected non-nil database") + } +} + +func TestCreateSchema(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("failed to open DB: %v", err) + } + defer db.Close() + + ctx := context.Background() + err = CreateSchema(ctx, db) + if err != nil { + t.Fatalf("failed to create schema: %v", err) + } + + _, err = db.ExecContext(ctx, "SELECT 1 FROM record LIMIT 1") + if err != nil { + t.Fatalf("failed to query record table: %v", err) + } +} + +func TestResetRecords(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("failed to open DB: %v", err) + } + defer db.Close() + + ctx := context.Background() + if err := CreateSchema(ctx, db); err != nil { + t.Fatalf("failed to create schema: %v", err) + } + + _, err = db.ExecContext(ctx, + "INSERT INTO record (host, uptime_sec, boot_time, os, os_kernel_name, os_kernel_major) VALUES (?, ?, ?, ?, ?, ?)", + "host1", 1000, 2000, "Linux 5.10", "Linux", "Linux 5...") + if err != nil { + t.Fatalf("failed to insert record: %v", err) + } + + err = ResetRecords(ctx, db) + if err != nil { + t.Fatalf("failed to reset records: %v", err) + } + + var count int + err = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count) + if err != nil { + t.Fatalf("failed to count records: %v", err) + } + + if count != 0 { + t.Errorf("expected 0 records after reset, got %d", count) + } +} + +func TestImportFromDir(t *testing.T) { + tmpDir := t.TempDir() + + recordsFile := filepath.Join(tmpDir, "testhost.records") + content := []byte("86400:1000000:Linux 5.10.0-test\n" + + "86400:1000001:Linux 5.10.0-test\n" + + "86400:1000002:Linux 5.10.0-test\n") + + if err := os.WriteFile(recordsFile, content, 0644); err != nil { + t.Fatalf("failed to create test file: %v", err) + } + + dbPath := filepath.Join(tmpDir, "test.db") + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("failed to open DB: %v", err) + } + defer db.Close() + + ctx := context.Background() + if err := CreateSchema(ctx, db); err != nil { + t.Fatalf("failed to create schema: %v", err) + } + + err = ImportFromDir(ctx, db, tmpDir) + if err != nil { + t.Fatalf("failed to import records: %v", err) + } + + var count int + err = db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count) + if err != nil { + t.Fatalf("failed to count records: %v", err) + } + + if count != 3 { + t.Errorf("expected 3 records after import, got %d", count) + } +} + +func TestImportFromFS_MapFS(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("open DB: %v", err) + } + defer db.Close() + ctx := context.Background() + if err := CreateSchema(ctx, db); err != nil { + t.Fatalf("schema: %v", err) + } + m := fstest.MapFS{ + "testhost.records": &fstest.MapFile{ + Data: []byte("86400:1000000:Linux 5.10.0-test\n" + + "86400:1000001:Linux 5.10.0-test\n" + + "86400:1000002:Linux 5.10.0-test\n"), + Mode: 0o644, + }, + } + if err := ImportFromFS(ctx, db, m); err != nil { + t.Fatalf("ImportFromFS: %v", err) + } + var count int + if err := db.QueryRowContext(ctx, "SELECT COUNT(*) FROM record").Scan(&count); err != nil { + t.Fatal(err) + } + if count != 3 { + t.Errorf("count = %d, want 3", count) + } +} + +func TestImportFromDir_invalidPath(t *testing.T) { + tmpDir := t.TempDir() + dbPath := filepath.Join(tmpDir, "test.db") + + db, err := Open(context.Background(), dbPath) + if err != nil { + t.Fatalf("failed to open DB: %v", err) + } + defer db.Close() + + ctx := context.Background() + if err := CreateSchema(ctx, db); err != nil { + t.Fatalf("failed to create schema: %v", err) + } + + err = ImportFromDir(ctx, db, "/nonexistent/path") + if err == nil { + t.Error("expected error for non-existent directory") + } +} |
