test(store): improve AX coverage and error paths
All checks were successful
Security Scan / security (push) Successful in 8s
Test / test (push) Successful in 1m39s

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Virgil 2026-03-30 18:08:33 +00:00
parent 6261ea2afb
commit bf3db41d9f
4 changed files with 488 additions and 0 deletions

View file

@ -1,7 +1,11 @@
package store
import (
"context"
"database/sql"
"database/sql/driver"
"io"
"sync"
"testing"
core "dappco.re/go/core"
@ -215,3 +219,402 @@ func TestCoverage_Render_Bad_RowsError(t *testing.T) {
require.Error(t, err, "Render should fail on corrupted database pages")
assert.Contains(t, err.Error(), "store.All: rows")
}
// ---------------------------------------------------------------------------
// GroupsSeq — defensive error paths
// ---------------------------------------------------------------------------
func TestCoverage_GroupsSeq_Bad_ScanError(t *testing.T) {
// Trigger a scan error by inserting a row with a NULL group name. The
// production code scans into a plain string, which cannot represent NULL.
s, err := New(":memory:")
require.NoError(t, err)
defer s.Close()
_, err = s.database.Exec("ALTER TABLE entries RENAME TO entries_backup")
require.NoError(t, err)
_, err = s.database.Exec(`CREATE TABLE entries (
group_name TEXT,
entry_key TEXT,
entry_value TEXT,
expires_at INTEGER
)`)
require.NoError(t, err)
_, err = s.database.Exec("INSERT INTO entries SELECT * FROM entries_backup")
require.NoError(t, err)
_, err = s.database.Exec("INSERT INTO entries (group_name, entry_key, entry_value) VALUES (NULL, 'k', 'v')")
require.NoError(t, err)
_, err = s.database.Exec("DROP TABLE entries_backup")
require.NoError(t, err)
for groupName, iterationErr := range s.GroupsSeq("") {
require.Error(t, iterationErr)
assert.Empty(t, groupName)
break
}
}
func TestCoverage_GroupsSeq_Bad_RowsError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
groupRows: [][]driver.Value{
{"group-a"},
},
groupRowsErr: core.E("stubSQLiteScenario", "rows iteration failed", nil),
groupRowsErrIndex: 0,
})
defer database.Close()
storeInstance := &Store{
database: database,
cancelPurge: func() {},
}
for groupName, iterationErr := range storeInstance.GroupsSeq("") {
require.Error(t, iterationErr, "GroupsSeq should fail on corrupted database pages")
assert.Empty(t, groupName)
break
}
}
// ---------------------------------------------------------------------------
// Stubbed SQLite driver coverage
// ---------------------------------------------------------------------------
func TestCoverage_EnsureSchema_Bad_TableExistsQueryError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableExistsErr: core.E("stubSQLiteScenario", "sqlite master query failed", nil),
})
defer database.Close()
err := ensureSchema(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "sqlite master query failed")
}
func TestCoverage_EnsureSchema_Good_ExistingEntriesAndLegacyMigration(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableExistsFound: true,
tableInfoRows: [][]driver.Value{
{0, "expires_at", "INTEGER", 0, nil, 0},
},
})
defer database.Close()
require.NoError(t, ensureSchema(database))
}
func TestCoverage_EnsureSchema_Bad_ExpiryColumnQueryError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableExistsFound: true,
tableInfoErr: core.E("stubSQLiteScenario", "table_info query failed", nil),
})
defer database.Close()
err := ensureSchema(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "table_info query failed")
}
func TestCoverage_EnsureSchema_Bad_MigrationError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableExistsFound: true,
tableInfoRows: [][]driver.Value{
{0, "expires_at", "INTEGER", 0, nil, 0},
},
insertErr: core.E("stubSQLiteScenario", "insert failed", nil),
})
defer database.Close()
err := ensureSchema(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "insert failed")
}
func TestCoverage_EnsureSchema_Bad_MigrationCommitError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableExistsFound: true,
tableInfoRows: [][]driver.Value{
{0, "expires_at", "INTEGER", 0, nil, 0},
},
commitErr: core.E("stubSQLiteScenario", "commit failed", nil),
})
defer database.Close()
err := ensureSchema(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "commit failed")
}
func TestCoverage_TableHasColumn_Bad_QueryError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoErr: core.E("stubSQLiteScenario", "table_info query failed", nil),
})
defer database.Close()
_, err := tableHasColumn(database, "entries", "expires_at")
require.Error(t, err)
assert.Contains(t, err.Error(), "table_info query failed")
}
func TestCoverage_EnsureExpiryColumn_Good_DuplicateColumn(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoRows: [][]driver.Value{
{0, "entry_key", "TEXT", 1, nil, 0},
},
alterTableErr: core.E("stubSQLiteScenario", "duplicate column name: expires_at", nil),
})
defer database.Close()
require.NoError(t, ensureExpiryColumn(database))
}
func TestCoverage_EnsureExpiryColumn_Bad_AlterTableError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoRows: [][]driver.Value{
{0, "entry_key", "TEXT", 1, nil, 0},
},
alterTableErr: core.E("stubSQLiteScenario", "permission denied", nil),
})
defer database.Close()
err := ensureExpiryColumn(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "permission denied")
}
func TestCoverage_MigrateLegacyEntriesTable_Bad_InsertError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoRows: [][]driver.Value{
{0, "grp", "TEXT", 1, nil, 0},
},
insertErr: core.E("stubSQLiteScenario", "insert failed", nil),
})
defer database.Close()
err := migrateLegacyEntriesTable(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "insert failed")
}
func TestCoverage_MigrateLegacyEntriesTable_Bad_BeginError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
beginErr: core.E("stubSQLiteScenario", "begin failed", nil),
})
defer database.Close()
err := migrateLegacyEntriesTable(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "begin failed")
}
func TestCoverage_MigrateLegacyEntriesTable_Good_CreatesAndMigratesLegacyRows(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoRows: [][]driver.Value{
{0, "grp", "TEXT", 1, nil, 0},
},
})
defer database.Close()
require.NoError(t, migrateLegacyEntriesTable(database))
}
func TestCoverage_MigrateLegacyEntriesTable_Bad_TableInfoError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{
tableInfoErr: core.E("stubSQLiteScenario", "table_info query failed", nil),
})
defer database.Close()
err := migrateLegacyEntriesTable(database)
require.Error(t, err)
assert.Contains(t, err.Error(), "table_info query failed")
}
type stubSQLiteScenario struct {
tableExistsErr error
tableExistsFound bool
tableInfoErr error
tableInfoRows [][]driver.Value
groupRows [][]driver.Value
groupRowsErr error
groupRowsErrIndex int
alterTableErr error
createTableErr error
insertErr error
dropTableErr error
beginErr error
commitErr error
rollbackErr error
}
type stubSQLiteDriver struct{}
type stubSQLiteConn struct {
scenario *stubSQLiteScenario
}
type stubSQLiteTx struct {
scenario *stubSQLiteScenario
}
type stubSQLiteRows struct {
columns []string
rows [][]driver.Value
index int
nextErr error
nextErrIndex int
}
type stubSQLiteResult struct{}
var (
stubSQLiteDriverOnce sync.Once
stubSQLiteScenarios sync.Map
)
const stubSQLiteDriverName = "stub-sqlite"
func openStubSQLiteDatabase(t *testing.T, scenario stubSQLiteScenario) (*sql.DB, string) {
t.Helper()
stubSQLiteDriverOnce.Do(func() {
sql.Register(stubSQLiteDriverName, stubSQLiteDriver{})
})
databasePath := t.Name()
stubSQLiteScenarios.Store(databasePath, &scenario)
t.Cleanup(func() {
stubSQLiteScenarios.Delete(databasePath)
})
database, err := sql.Open(stubSQLiteDriverName, databasePath)
require.NoError(t, err)
return database, databasePath
}
func (stubSQLiteDriver) Open(databasePath string) (driver.Conn, error) {
scenarioValue, ok := stubSQLiteScenarios.Load(databasePath)
if !ok {
return nil, core.E("stubSQLiteDriver.Open", "missing scenario", nil)
}
return &stubSQLiteConn{scenario: scenarioValue.(*stubSQLiteScenario)}, nil
}
func (conn *stubSQLiteConn) Prepare(query string) (driver.Stmt, error) {
return nil, core.E("stubSQLiteConn.Prepare", "not implemented", nil)
}
func (conn *stubSQLiteConn) Close() error {
return nil
}
func (conn *stubSQLiteConn) Begin() (driver.Tx, error) {
return conn.BeginTx(context.Background(), driver.TxOptions{})
}
func (conn *stubSQLiteConn) BeginTx(ctx context.Context, opts driver.TxOptions) (driver.Tx, error) {
if conn.scenario.beginErr != nil {
return nil, conn.scenario.beginErr
}
return &stubSQLiteTx{scenario: conn.scenario}, nil
}
func (conn *stubSQLiteConn) ExecContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Result, error) {
switch {
case core.Contains(query, "ALTER TABLE entries ADD COLUMN expires_at INTEGER"):
if conn.scenario.alterTableErr != nil {
return nil, conn.scenario.alterTableErr
}
case core.Contains(query, "CREATE TABLE IF NOT EXISTS entries"):
if conn.scenario.createTableErr != nil {
return nil, conn.scenario.createTableErr
}
case core.Contains(query, "INSERT OR IGNORE INTO entries"):
if conn.scenario.insertErr != nil {
return nil, conn.scenario.insertErr
}
case core.Contains(query, "DROP TABLE kv"):
if conn.scenario.dropTableErr != nil {
return nil, conn.scenario.dropTableErr
}
}
return stubSQLiteResult{}, nil
}
func (conn *stubSQLiteConn) QueryContext(ctx context.Context, query string, args []driver.NamedValue) (driver.Rows, error) {
switch {
case core.Contains(query, "sqlite_master"):
if conn.scenario.tableExistsErr != nil {
return nil, conn.scenario.tableExistsErr
}
if conn.scenario.tableExistsFound {
return &stubSQLiteRows{
columns: []string{"name"},
rows: [][]driver.Value{{"entries"}},
}, nil
}
return &stubSQLiteRows{columns: []string{"name"}}, nil
case core.Contains(query, "SELECT DISTINCT "+entryGroupColumn):
return &stubSQLiteRows{
columns: []string{entryGroupColumn},
rows: conn.scenario.groupRows,
nextErr: conn.scenario.groupRowsErr,
nextErrIndex: conn.scenario.groupRowsErrIndex,
}, nil
case core.HasPrefix(query, "PRAGMA table_info("):
if conn.scenario.tableInfoErr != nil {
return nil, conn.scenario.tableInfoErr
}
return &stubSQLiteRows{
columns: []string{"cid", "name", "type", "notnull", "dflt_value", "pk"},
rows: conn.scenario.tableInfoRows,
}, nil
}
return nil, core.E("stubSQLiteConn.QueryContext", "unexpected query", nil)
}
func (tx *stubSQLiteTx) Commit() error {
if tx.scenario.commitErr != nil {
return tx.scenario.commitErr
}
return nil
}
func (tx *stubSQLiteTx) Rollback() error {
if tx.scenario.rollbackErr != nil {
return tx.scenario.rollbackErr
}
return nil
}
func (rows *stubSQLiteRows) Columns() []string {
return rows.columns
}
func (rows *stubSQLiteRows) Close() error {
return nil
}
func (rows *stubSQLiteRows) Next(dest []driver.Value) error {
if rows.nextErr != nil && rows.index == rows.nextErrIndex {
rows.index++
return rows.nextErr
}
if rows.index >= len(rows.rows) {
return io.EOF
}
row := rows.rows[rows.index]
rows.index++
for i := range dest {
dest[i] = nil
}
copy(dest, row)
return nil
}
func (stubSQLiteResult) LastInsertId() (int64, error) {
return 0, nil
}
func (stubSQLiteResult) RowsAffected() (int64, error) {
return 0, nil
}

View file

@ -66,6 +66,23 @@ func TestEvents_Watch_Good_WildcardKey(t *testing.T) {
assert.Equal(t, "colour", received[1].Key)
}
func TestEvents_Watch_Good_GroupMismatch(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
w := s.Watch("config", "*")
defer s.Unwatch(w)
require.NoError(t, s.Set("other", "theme", "dark"))
select {
case e := <-w.Events:
t.Fatalf("unexpected event for non-matching group: %+v", e)
case <-time.After(50 * time.Millisecond):
// Expected: no event.
}
}
// ---------------------------------------------------------------------------
// Watch — wildcard ("*", "*") matches everything
// ---------------------------------------------------------------------------
@ -120,6 +137,13 @@ func TestEvents_Unwatch_Good_Idempotent(t *testing.T) {
s.Unwatch(w) // second call is a no-op
}
func TestEvents_Unwatch_Good_NilWatcher(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
s.Unwatch(nil)
}
// ---------------------------------------------------------------------------
// Delete triggers event
// ---------------------------------------------------------------------------

View file

@ -55,6 +55,15 @@ func TestScope_NewScoped_Bad_InvalidChars(t *testing.T) {
}
}
func TestScope_NewScopedWithQuota_Bad_InvalidNamespace(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
_, err := NewScopedWithQuota(s, "tenant_a", QuotaConfig{MaxKeys: 1})
require.Error(t, err)
assert.Contains(t, err.Error(), "store.NewScoped")
}
// ---------------------------------------------------------------------------
// ScopedStore — basic CRUD
// ---------------------------------------------------------------------------
@ -153,6 +162,23 @@ func TestScope_ScopedStore_Good_GetAll(t *testing.T) {
assert.Equal(t, map[string]string{"z": "3"}, allB)
}
func TestScope_ScopedStore_Good_All(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
scopedStore, _ := NewScoped(s, "tenant-a")
require.NoError(t, scopedStore.Set("items", "first", "1"))
require.NoError(t, scopedStore.Set("items", "second", "2"))
var keys []string
for entry, err := range scopedStore.All("items") {
require.NoError(t, err)
keys = append(keys, entry.Key)
}
assert.ElementsMatch(t, []string{"first", "second"}, keys)
}
func TestScope_ScopedStore_Good_Count(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
@ -224,6 +250,23 @@ func TestScope_Quota_Good_MaxKeys(t *testing.T) {
assert.True(t, core.Is(err, QuotaExceededError), "expected QuotaExceededError, got: %v", err)
}
func TestScope_Quota_Bad_QuotaCheckQueryError(t *testing.T) {
database, _ := openStubSQLiteDatabase(t, stubSQLiteScenario{})
defer database.Close()
storeInstance := &Store{
database: database,
cancelPurge: func() {},
}
scopedStore, err := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 1})
require.NoError(t, err)
err = scopedStore.Set("config", "theme", "dark")
require.Error(t, err)
assert.Contains(t, err.Error(), "quota check")
}
func TestScope_Quota_Good_MaxKeys_AcrossGroups(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()

View file

@ -382,6 +382,24 @@ func TestStore_GroupsSeq_Good_StopsEarly(t *testing.T) {
assert.Len(t, seen, 1)
}
func TestStore_GroupsSeq_Good_PrefixStopsEarly(t *testing.T) {
s, _ := New(":memory:")
defer s.Close()
require.NoError(t, s.Set("alpha", "a", "1"))
require.NoError(t, s.Set("beta", "b", "2"))
groups := s.GroupsSeq("alpha")
var seen []string
for group, err := range groups {
require.NoError(t, err)
seen = append(seen, group)
break
}
assert.Equal(t, []string{"alpha"}, seen)
}
func TestStore_GroupsSeq_Bad_ClosedStore(t *testing.T) {
s, _ := New(":memory:")
s.Close()