From 23f207db3fd03083c1a3098c89e3afd2589d437d Mon Sep 17 00:00:00 2001 From: Virgil Date: Mon, 30 Mar 2026 18:37:07 +0000 Subject: [PATCH] refactor(store): tighten AX naming and error contexts Co-Authored-By: Virgil --- coverage_test.go | 2 +- docs/RFC-STORE.md | 2 +- docs/architecture.md | 2 +- docs/history.md | 12 ++++----- scope.go | 6 ++--- scope_test.go | 4 +-- store.go | 64 ++++++++++++++++++++++---------------------- store_test.go | 62 +++++++++++++++++++++--------------------- 8 files changed, 77 insertions(+), 77 deletions(-) diff --git a/coverage_test.go b/coverage_test.go index 00f22cc..78352b7 100644 --- a/coverage_test.go +++ b/coverage_test.go @@ -36,7 +36,7 @@ func TestCoverage_New_Bad_SchemaConflict(t *testing.T) { _, err = New(databasePath) require.Error(t, err, "New should fail when an index named entries already exists") - assert.Contains(t, err.Error(), "store.New: schema") + assert.Contains(t, err.Error(), "store.New: ensure schema") } // --------------------------------------------------------------------------- diff --git a/docs/RFC-STORE.md b/docs/RFC-STORE.md index b1732cf..978dce0 100644 --- a/docs/RFC-STORE.md +++ b/docs/RFC-STORE.md @@ -36,7 +36,7 @@ The public surface is intentionally small. Names are descriptive, comments show - `Close() error` stops the background purge goroutine and closes the database. - `Get(group, key string) (string, error)` returns a stored value or `NotFoundError`. - `Set(group, key, value string) error` stores a value and clears any existing TTL. -- `SetWithTTL(group, key, value string, ttl time.Duration) error` stores a value that expires after the supplied duration. +- `SetWithTTL(group, key, value string, timeToLive time.Duration) error` stores a value that expires after the supplied duration. - `Delete(group, key string) error` removes one key. - `DeleteGroup(group string) error` removes every key in a group. - `Count(group string) (int, error)` counts non-expired keys in one group. diff --git a/docs/architecture.md b/docs/architecture.md index 6b4bba8..97ee7a6 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -116,7 +116,7 @@ if err != nil { // renderedTemplate: {"pool":"pool.lthn.io:3333","wallet":"iz..."} ``` -Template parse errors and execution errors are both returned as wrapped errors with context (e.g., `store.Render: parse: ...` and `store.Render: exec: ...`). +Template parse errors and execution errors are both returned as wrapped errors with context (e.g., `store.Render: parse template: ...` and `store.Render: execute template: ...`). Missing template variables do not return an error by default -- Go's `text/template` renders them as ``. Applications requiring strict variable presence should validate data beforehand. diff --git a/docs/history.md b/docs/history.md index 5aaa8c5..7b7a11a 100644 --- a/docs/history.md +++ b/docs/history.md @@ -64,7 +64,7 @@ Added optional time-to-live for keys. ### Changes - `expires_at INTEGER` nullable column added to the key-value schema. -- `SetWithTTL(group, key, value string, ttl time.Duration)` stores the current time plus TTL as a Unix millisecond timestamp in `expires_at`. +- `SetWithTTL(group, key, value string, timeToLive time.Duration)` stores the current time plus TTL as a Unix millisecond timestamp in `expires_at`. - `Get()` performs lazy deletion: if a key is found with an `expires_at` in the past, it is deleted and `NotFoundError` is returned. - `Count()`, `GetAll()`, and `Render()` include `(expires_at IS NULL OR expires_at > ?)` in all queries, excluding expired keys from results. - `PurgeExpired()` public method deletes all physically stored expired rows and returns the count removed. @@ -186,11 +186,11 @@ Renamed the internal SQLite schema to use descriptive names that are easier for `coverage_test.go` exercises defensive error paths that integration tests cannot reach through normal usage: -- Schema conflict: pre-existing SQLite index named `entries` causes `New()` to return `store.New: schema: ...`. -- `GetAll` scan error: NULL key in a row (requires manually altering the schema to remove the NOT NULL constraint). -- `GetAll` rows iteration error: physically corrupting database pages mid-file to trigger `rows.Err()` during multi-page scans. -- `Render` scan error: same NULL-key technique. -- `Render` rows iteration error: same corruption technique. +- Schema conflict: pre-existing SQLite index named `entries` causes `New()` to return `store.New: ensure schema: ...`. +- `GetAll` scan error: NULL key in a row (requires manually altering the schema to remove the NOT NULL constraint) to trigger `store.All: scan row: ...`. +- `GetAll` rows iteration error: physically corrupting database pages mid-file to trigger `store.All: rows iteration: ...`. +- `Render` scan error: same NULL-key technique, surfaced as `store.All: scan row: ...`. +- `Render` rows iteration error: same corruption technique, surfaced as `store.All: rows iteration: ...`. These tests exercise correct defensive code. They must continue to pass but are not indicative of real failure modes in production. diff --git a/scope.go b/scope.go index 7bccc5d..d9e16c4 100644 --- a/scope.go +++ b/scope.go @@ -29,7 +29,7 @@ type ScopedStore struct { // Usage example: `scopedStore, err := store.NewScoped(storeInstance, "tenant-a"); if err != nil { return }` func NewScoped(storeInstance *Store, namespace string) (*ScopedStore, error) { if !validNamespace.MatchString(namespace) { - return nil, core.E("store.NewScoped", core.Sprintf("namespace %q is invalid (must be non-empty, alphanumeric + hyphens)", namespace), nil) + return nil, core.E("store.NewScoped", core.Sprintf("namespace %q is invalid; use names like %q or %q", namespace, "tenant-a", "tenant-42"), nil) } scopedStore := &ScopedStore{storeInstance: storeInstance, namespace: namespace} return scopedStore, nil @@ -68,11 +68,11 @@ func (scopedStore *ScopedStore) Set(group, key, value string) error { } // Usage example: `if err := scopedStore.SetWithTTL("sessions", "token", "abc123", time.Hour); err != nil { return }` -func (scopedStore *ScopedStore) SetWithTTL(group, key, value string, ttl time.Duration) error { +func (scopedStore *ScopedStore) SetWithTTL(group, key, value string, timeToLive time.Duration) error { if err := scopedStore.checkQuota("store.ScopedStore.SetWithTTL", group, key); err != nil { return err } - return scopedStore.storeInstance.SetWithTTL(scopedStore.namespacedGroup(group), key, value, ttl) + return scopedStore.storeInstance.SetWithTTL(scopedStore.namespacedGroup(group), key, value, timeToLive) } // Usage example: `if err := scopedStore.Delete("config", "theme"); err != nil { return }` diff --git a/scope_test.go b/scope_test.go index 47bbf08..e0464b1 100644 --- a/scope_test.go +++ b/scope_test.go @@ -157,9 +157,9 @@ func TestScope_ScopedStore_Good_GetAll(t *testing.T) { require.NoError(t, err) assert.Equal(t, map[string]string{"x": "1", "y": "2"}, all) - allB, err := betaStore.GetAll("items") + betaEntries, err := betaStore.GetAll("items") require.NoError(t, err) - assert.Equal(t, map[string]string{"z": "3"}, allB) + assert.Equal(t, map[string]string{"z": "3"}, betaEntries) } func TestScope_ScopedStore_Good_All(t *testing.T) { diff --git a/store.go b/store.go index 6309982..9b43bbf 100644 --- a/store.go +++ b/store.go @@ -47,7 +47,7 @@ type Store struct { func New(databasePath string) (*Store, error) { sqliteDatabase, err := sql.Open("sqlite", databasePath) if err != nil { - return nil, core.E("store.New", "open", err) + return nil, core.E("store.New", "open database", err) } // Serialise all access through a single connection. SQLite only supports // one writer at a time; using a pool causes SQLITE_BUSY under contention @@ -56,15 +56,15 @@ func New(databasePath string) (*Store, error) { sqliteDatabase.SetMaxOpenConns(1) if _, err := sqliteDatabase.Exec("PRAGMA journal_mode=WAL"); err != nil { sqliteDatabase.Close() - return nil, core.E("store.New", "WAL", err) + return nil, core.E("store.New", "set WAL journal mode", err) } if _, err := sqliteDatabase.Exec("PRAGMA busy_timeout=5000"); err != nil { sqliteDatabase.Close() - return nil, core.E("store.New", "busy_timeout", err) + return nil, core.E("store.New", "set busy timeout", err) } if err := ensureSchema(sqliteDatabase); err != nil { sqliteDatabase.Close() - return nil, err + return nil, core.E("store.New", "ensure schema", err) } purgeContext, cancel := context.WithCancel(context.Background()) @@ -95,11 +95,11 @@ func (storeInstance *Store) Get(group, key string) (string, error) { return "", core.E("store.Get", core.Concat(group, "/", key), NotFoundError) } if err != nil { - return "", core.E("store.Get", "query", err) + return "", core.E("store.Get", "query row", err) } if expiresAt.Valid && expiresAt.Int64 <= time.Now().UnixMilli() { if _, err := storeInstance.database.Exec("DELETE FROM "+entriesTableName+" WHERE "+entryGroupColumn+" = ? AND "+entryKeyColumn+" = ?", group, key); err != nil { - return "", core.E("store.Get", "lazy delete", err) + return "", core.E("store.Get", "delete expired row", err) } return "", core.E("store.Get", core.Concat(group, "/", key), NotFoundError) } @@ -114,22 +114,22 @@ func (storeInstance *Store) Set(group, key, value string) error { group, key, value, ) if err != nil { - return core.E("store.Set", "exec", err) + return core.E("store.Set", "execute upsert", err) } storeInstance.notify(Event{Type: EventSet, Group: group, Key: key, Value: value, Timestamp: time.Now()}) return nil } // Usage example: `if err := storeInstance.SetWithTTL("session", "token", "abc123", time.Minute); err != nil { return }` -func (storeInstance *Store) SetWithTTL(group, key, value string, ttl time.Duration) error { - expiresAt := time.Now().Add(ttl).UnixMilli() +func (storeInstance *Store) SetWithTTL(group, key, value string, timeToLive time.Duration) error { + expiresAt := time.Now().Add(timeToLive).UnixMilli() _, err := storeInstance.database.Exec( "INSERT INTO "+entriesTableName+" ("+entryGroupColumn+", "+entryKeyColumn+", "+entryValueColumn+", expires_at) VALUES (?, ?, ?, ?) "+ "ON CONFLICT("+entryGroupColumn+", "+entryKeyColumn+") DO UPDATE SET "+entryValueColumn+" = excluded."+entryValueColumn+", expires_at = excluded.expires_at", group, key, value, expiresAt, ) if err != nil { - return core.E("store.SetWithTTL", "exec", err) + return core.E("store.SetWithTTL", "execute upsert with expiry", err) } storeInstance.notify(Event{Type: EventSet, Group: group, Key: key, Value: value, Timestamp: time.Now()}) return nil @@ -139,7 +139,7 @@ func (storeInstance *Store) SetWithTTL(group, key, value string, ttl time.Durati func (storeInstance *Store) Delete(group, key string) error { _, err := storeInstance.database.Exec("DELETE FROM "+entriesTableName+" WHERE "+entryGroupColumn+" = ? AND "+entryKeyColumn+" = ?", group, key) if err != nil { - return core.E("store.Delete", "exec", err) + return core.E("store.Delete", "delete row", err) } storeInstance.notify(Event{Type: EventDelete, Group: group, Key: key, Timestamp: time.Now()}) return nil @@ -153,7 +153,7 @@ func (storeInstance *Store) Count(group string) (int, error) { group, time.Now().UnixMilli(), ).Scan(&count) if err != nil { - return 0, core.E("store.Count", "query", err) + return 0, core.E("store.Count", "count rows", err) } return count, nil } @@ -162,7 +162,7 @@ func (storeInstance *Store) Count(group string) (int, error) { func (storeInstance *Store) DeleteGroup(group string) error { _, err := storeInstance.database.Exec("DELETE FROM "+entriesTableName+" WHERE "+entryGroupColumn+" = ?", group) if err != nil { - return core.E("store.DeleteGroup", "exec", err) + return core.E("store.DeleteGroup", "delete group", err) } storeInstance.notify(Event{Type: EventDeleteGroup, Group: group, Timestamp: time.Now()}) return nil @@ -181,7 +181,7 @@ func (storeInstance *Store) GetAll(group string) (map[string]string, error) { entriesByKey := make(map[string]string) for entry, err := range storeInstance.All(group) { if err != nil { - return nil, core.E("store.GetAll", "iterate", err) + return nil, core.E("store.GetAll", "iterate rows", err) } entriesByKey[entry.Key] = entry.Value } @@ -196,7 +196,7 @@ func (storeInstance *Store) All(group string) iter.Seq2[KeyValue, error] { group, time.Now().UnixMilli(), ) if err != nil { - yield(KeyValue{}, core.E("store.All", "query", err)) + yield(KeyValue{}, core.E("store.All", "query rows", err)) return } defer rows.Close() @@ -204,7 +204,7 @@ func (storeInstance *Store) All(group string) iter.Seq2[KeyValue, error] { for rows.Next() { var entry KeyValue if err := rows.Scan(&entry.Key, &entry.Value); err != nil { - if !yield(KeyValue{}, core.E("store.All", "scan", err)) { + if !yield(KeyValue{}, core.E("store.All", "scan row", err)) { return } continue @@ -214,7 +214,7 @@ func (storeInstance *Store) All(group string) iter.Seq2[KeyValue, error] { } } if err := rows.Err(); err != nil { - yield(KeyValue{}, core.E("store.All", "rows", err)) + yield(KeyValue{}, core.E("store.All", "rows iteration", err)) } } } @@ -242,18 +242,18 @@ func (storeInstance *Store) Render(templateSource, group string) (string, error) templateData := make(map[string]string) for entry, err := range storeInstance.All(group) { if err != nil { - return "", core.E("store.Render", "iterate", err) + return "", core.E("store.Render", "iterate rows", err) } templateData[entry.Key] = entry.Value } renderTemplate, err := template.New("render").Parse(templateSource) if err != nil { - return "", core.E("store.Render", "parse", err) + return "", core.E("store.Render", "parse template", err) } builder := core.NewBuilder() if err := renderTemplate.Execute(builder, templateData); err != nil { - return "", core.E("store.Render", "exec", err) + return "", core.E("store.Render", "execute template", err) } return builder.String(), nil } @@ -274,7 +274,7 @@ func (storeInstance *Store) CountAll(groupPrefix string) (int, error) { ).Scan(&count) } if err != nil { - return 0, core.E("store.CountAll", "query", err) + return 0, core.E("store.CountAll", "count rows", err) } return count, nil } @@ -309,7 +309,7 @@ func (storeInstance *Store) GroupsSeq(groupPrefix string) iter.Seq2[string, erro ) } if err != nil { - yield("", core.E("store.GroupsSeq", "query", err)) + yield("", core.E("store.GroupsSeq", "query group names", err)) return } defer rows.Close() @@ -317,7 +317,7 @@ func (storeInstance *Store) GroupsSeq(groupPrefix string) iter.Seq2[string, erro for rows.Next() { var groupName string if err := rows.Scan(&groupName); err != nil { - if !yield("", core.E("store.GroupsSeq", "scan", err)) { + if !yield("", core.E("store.GroupsSeq", "scan group name", err)) { return } continue @@ -327,7 +327,7 @@ func (storeInstance *Store) GroupsSeq(groupPrefix string) iter.Seq2[string, erro } } if err := rows.Err(); err != nil { - yield("", core.E("store.GroupsSeq", "rows", err)) + yield("", core.E("store.GroupsSeq", "rows iteration", err)) } } } @@ -346,11 +346,11 @@ func (storeInstance *Store) PurgeExpired() (int64, error) { deleteResult, err := storeInstance.database.Exec("DELETE FROM "+entriesTableName+" WHERE expires_at IS NOT NULL AND expires_at <= ?", time.Now().UnixMilli()) if err != nil { - return 0, core.E("store.PurgeExpired", "exec", err) + return 0, core.E("store.PurgeExpired", "delete expired rows", err) } removedRows, rowsAffectedErr := deleteResult.RowsAffected() if rowsAffectedErr != nil { - return 0, core.E("store.PurgeExpired", "rows affected", rowsAffectedErr) + return 0, core.E("store.PurgeExpired", "count deleted rows", rowsAffectedErr) } return removedRows, nil } @@ -431,21 +431,21 @@ const createEntriesTableSQL = `CREATE TABLE IF NOT EXISTS entries ( func ensureSchema(database *sql.DB) error { entriesTableExists, err := tableExists(database, entriesTableName) if err != nil { - return core.E("store.New", "schema", err) + return core.E("store.ensureSchema", "schema", err) } legacyEntriesTableExists, err := tableExists(database, legacyKeyValueTableName) if err != nil { - return core.E("store.New", "schema", err) + return core.E("store.ensureSchema", "schema", err) } if entriesTableExists { if err := ensureExpiryColumn(database); err != nil { - return core.E("store.New", "migration", err) + return core.E("store.ensureSchema", "migration", err) } if legacyEntriesTableExists { if err := migrateLegacyEntriesTable(database); err != nil { - return core.E("store.New", "migration", err) + return core.E("store.ensureSchema", "migration", err) } } return nil @@ -453,13 +453,13 @@ func ensureSchema(database *sql.DB) error { if legacyEntriesTableExists { if err := migrateLegacyEntriesTable(database); err != nil { - return core.E("store.New", "migration", err) + return core.E("store.ensureSchema", "migration", err) } return nil } if _, err := database.Exec(createEntriesTableSQL); err != nil { - return core.E("store.New", "schema", err) + return core.E("store.ensureSchema", "schema", err) } return nil } diff --git a/store_test.go b/store_test.go index 83f4546..37f9160 100644 --- a/store_test.go +++ b/store_test.go @@ -26,8 +26,8 @@ func TestStore_New_Good_Memory(t *testing.T) { } func TestStore_New_Good_FileBacked(t *testing.T) { - dbPath := testPath(t, "test.db") - storeInstance, err := New(dbPath) + databasePath := testPath(t, "test.db") + storeInstance, err := New(databasePath) require.NoError(t, err) require.NotNil(t, storeInstance) defer storeInstance.Close() @@ -36,7 +36,7 @@ func TestStore_New_Good_FileBacked(t *testing.T) { require.NoError(t, storeInstance.Set("g", "k", "v")) require.NoError(t, storeInstance.Close()) - reopenedStore, err := New(dbPath) + reopenedStore, err := New(databasePath) require.NoError(t, err) defer reopenedStore.Close() @@ -55,10 +55,10 @@ func TestStore_New_Bad_InvalidPath(t *testing.T) { func TestStore_New_Bad_CorruptFile(t *testing.T) { // A file that exists but is not a valid SQLite database should fail. - dbPath := testPath(t, "corrupt.db") - requireCoreOK(t, testFilesystem().Write(dbPath, "not a sqlite database")) + databasePath := testPath(t, "corrupt.db") + requireCoreOK(t, testFilesystem().Write(databasePath, "not a sqlite database")) - _, err := New(dbPath) + _, err := New(databasePath) require.Error(t, err) assert.Contains(t, err.Error(), "store.New") } @@ -66,20 +66,20 @@ func TestStore_New_Bad_CorruptFile(t *testing.T) { func TestStore_New_Bad_ReadOnlyDir(t *testing.T) { // A path in a read-only directory should fail when SQLite tries to create the WAL file. dir := t.TempDir() - dbPath := core.Path(dir, "readonly.db") + databasePath := core.Path(dir, "readonly.db") // Create a valid database first, then make the directory read-only. - storeInstance, err := New(dbPath) + storeInstance, err := New(databasePath) require.NoError(t, err) require.NoError(t, storeInstance.Close()) // Remove WAL/SHM files and make directory read-only. - _ = testFilesystem().Delete(dbPath + "-wal") - _ = testFilesystem().Delete(dbPath + "-shm") + _ = testFilesystem().Delete(databasePath + "-wal") + _ = testFilesystem().Delete(databasePath + "-shm") require.NoError(t, syscall.Chmod(dir, 0555)) defer func() { _ = syscall.Chmod(dir, 0755) }() // restore for cleanup - _, err = New(dbPath) + _, err = New(databasePath) // May or may not fail depending on OS/filesystem — just exercise the code path. if err != nil { assert.Contains(t, err.Error(), "store.New") @@ -87,8 +87,8 @@ func TestStore_New_Bad_ReadOnlyDir(t *testing.T) { } func TestStore_New_Good_WALMode(t *testing.T) { - dbPath := testPath(t, "wal.db") - storeInstance, err := New(dbPath) + databasePath := testPath(t, "wal.db") + storeInstance, err := New(databasePath) require.NoError(t, err) defer storeInstance.Close() @@ -789,8 +789,8 @@ func TestStore_GroupIsolation_Good(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Concurrent_Good_ReadWrite(t *testing.T) { - dbPath := testPath(t, "concurrent.db") - storeInstance, err := New(dbPath) + databasePath := testPath(t, "concurrent.db") + storeInstance, err := New(databasePath) require.NoError(t, err) defer storeInstance.Close() @@ -955,8 +955,8 @@ func BenchmarkGetAll(benchmark *testing.B) { } func BenchmarkSet_FileBacked(benchmark *testing.B) { - dbPath := testPath(benchmark, "bench.db") - storeInstance, _ := New(dbPath) + databasePath := testPath(benchmark, "bench.db") + storeInstance, _ := New(databasePath) defer storeInstance.Close() benchmark.ResetTimer() @@ -1157,9 +1157,9 @@ func TestStore_PurgeExpired_Good_BackgroundPurge(t *testing.T) { storeInstance.cancelPurge() storeInstance.purgeWaitGroup.Wait() storeInstance.purgeInterval = 20 * time.Millisecond - ctx, cancel := context.WithCancel(context.Background()) + purgeContext, cancel := context.WithCancel(context.Background()) storeInstance.cancelPurge = cancel - storeInstance.startBackgroundPurge(ctx) + storeInstance.startBackgroundPurge(purgeContext) defer storeInstance.Close() require.NoError(t, storeInstance.SetWithTTL("g", "ephemeral", "v", 1*time.Millisecond)) @@ -1181,16 +1181,16 @@ func TestStore_PurgeExpired_Good_BackgroundPurge(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_SchemaUpgrade_Good_ExistingDB(t *testing.T) { - dbPath := testPath(t, "upgrade.db") + databasePath := testPath(t, "upgrade.db") // Open, write, close. - initialStore, err := New(dbPath) + initialStore, err := New(databasePath) require.NoError(t, err) require.NoError(t, initialStore.Set("g", "k", "v")) require.NoError(t, initialStore.Close()) // Reopen — the ALTER TABLE ADD COLUMN should be a no-op. - reopenedStore, err := New(dbPath) + reopenedStore, err := New(databasePath) require.NoError(t, err) defer reopenedStore.Close() @@ -1206,8 +1206,8 @@ func TestStore_SchemaUpgrade_Good_ExistingDB(t *testing.T) { } func TestStore_SchemaUpgrade_Good_EntriesWithoutExpiryColumn(t *testing.T) { - dbPath := testPath(t, "entries-no-expiry.db") - database, err := sql.Open("sqlite", dbPath) + databasePath := testPath(t, "entries-no-expiry.db") + database, err := sql.Open("sqlite", databasePath) require.NoError(t, err) database.SetMaxOpenConns(1) _, err = database.Exec("PRAGMA journal_mode=WAL") @@ -1223,7 +1223,7 @@ func TestStore_SchemaUpgrade_Good_EntriesWithoutExpiryColumn(t *testing.T) { require.NoError(t, err) require.NoError(t, database.Close()) - storeInstance, err := New(dbPath) + storeInstance, err := New(databasePath) require.NoError(t, err) defer storeInstance.Close() @@ -1238,8 +1238,8 @@ func TestStore_SchemaUpgrade_Good_EntriesWithoutExpiryColumn(t *testing.T) { } func TestStore_SchemaUpgrade_Good_LegacyAndCurrentTables(t *testing.T) { - dbPath := testPath(t, "entries-and-legacy.db") - database, err := sql.Open("sqlite", dbPath) + databasePath := testPath(t, "entries-and-legacy.db") + database, err := sql.Open("sqlite", databasePath) require.NoError(t, err) database.SetMaxOpenConns(1) _, err = database.Exec("PRAGMA journal_mode=WAL") @@ -1265,7 +1265,7 @@ func TestStore_SchemaUpgrade_Good_LegacyAndCurrentTables(t *testing.T) { require.NoError(t, err) require.NoError(t, database.Close()) - storeInstance, err := New(dbPath) + storeInstance, err := New(databasePath) require.NoError(t, err) defer storeInstance.Close() @@ -1281,8 +1281,8 @@ func TestStore_SchemaUpgrade_Good_LegacyAndCurrentTables(t *testing.T) { func TestStore_SchemaUpgrade_Good_PreTTLDatabase(t *testing.T) { // Simulate a database created before the AX schema rename and TTL support. // The legacy key-value table has no expires_at column yet. - dbPath := testPath(t, "pre-ttl.db") - database, err := sql.Open("sqlite", dbPath) + databasePath := testPath(t, "pre-ttl.db") + database, err := sql.Open("sqlite", databasePath) require.NoError(t, err) database.SetMaxOpenConns(1) _, err = database.Exec("PRAGMA journal_mode=WAL") @@ -1299,7 +1299,7 @@ func TestStore_SchemaUpgrade_Good_PreTTLDatabase(t *testing.T) { require.NoError(t, database.Close()) // Open with New — should migrate the legacy table into the descriptive schema. - storeInstance, err := New(dbPath) + storeInstance, err := New(databasePath) require.NoError(t, err) defer storeInstance.Close() -- 2.45.3