feat: upgrade to core v0.8.0-alpha.1, replace banned stdlib imports
All checks were successful
Security Scan / security (push) Successful in 9s
Test / test (push) Successful in 2m33s

Replace fmt, errors, strings, path/filepath with Core primitives
across 8 files. Keep strings for SplitSeq/FieldsSeq/Builder/Repeat.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Claude 2026-03-26 13:58:50 +00:00
parent e2678f5cde
commit c3de82b207
No known key found for this signature in database
GPG key ID: AF404715446AEB41
10 changed files with 91 additions and 91 deletions

View file

@ -2,8 +2,9 @@
package store
import (
"fmt"
"testing"
core "dappco.re/go/core"
)
// Supplemental benchmarks beyond the core Set/Get/GetAll/FileBacked benchmarks
@ -14,7 +15,7 @@ func BenchmarkGetAll_VaryingSize(b *testing.B) {
sizes := []int{10, 100, 1_000, 10_000}
for _, size := range sizes {
b.Run(fmt.Sprintf("size=%d", size), func(b *testing.B) {
b.Run(core.Sprintf("size=%d", size), func(b *testing.B) {
s, err := New(":memory:")
if err != nil {
b.Fatal(err)
@ -22,7 +23,7 @@ func BenchmarkGetAll_VaryingSize(b *testing.B) {
defer s.Close()
for i := range size {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
b.ReportAllocs()
@ -48,7 +49,7 @@ func BenchmarkSetGet_Parallel(b *testing.B) {
b.RunParallel(func(pb *testing.PB) {
i := 0
for pb.Next() {
key := fmt.Sprintf("key-%d", i)
key := core.Sprintf("key-%d", i)
_ = s.Set("parallel", key, "value")
_, _ = s.Get("parallel", key)
i++
@ -64,7 +65,7 @@ func BenchmarkCount_10K(b *testing.B) {
defer s.Close()
for i := range 10_000 {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
b.ReportAllocs()
@ -84,14 +85,14 @@ func BenchmarkDelete(b *testing.B) {
// Pre-populate keys that will be deleted.
for i := range b.N {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
b.ReportAllocs()
b.ResetTimer()
for i := range b.N {
_ = s.Delete("bench", fmt.Sprintf("key-%d", i))
_ = s.Delete("bench", core.Sprintf("key-%d", i))
}
}
@ -106,7 +107,7 @@ func BenchmarkSetWithTTL(b *testing.B) {
b.ResetTimer()
for i := range b.N {
_ = s.SetWithTTL("bench", fmt.Sprintf("key-%d", i), "value", 60_000_000_000) // 60s
_ = s.SetWithTTL("bench", core.Sprintf("key-%d", i), "value", 60_000_000_000) // 60s
}
}
@ -118,7 +119,7 @@ func BenchmarkRender(b *testing.B) {
defer s.Close()
for i := range 50 {
_ = s.Set("bench", fmt.Sprintf("key%d", i), fmt.Sprintf("val%d", i))
_ = s.Set("bench", core.Sprintf("key%d", i), core.Sprintf("val%d", i))
}
tmpl := `{{ .key0 }} {{ .key25 }} {{ .key49 }}`

View file

@ -5,26 +5,25 @@ import (
"go/parser"
"go/token"
"os"
"path/filepath"
"slices"
"strings"
"testing"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestRepoConventions_Good_BannedImports(t *testing.T) {
files := repoGoFiles(t, func(name string) bool {
return strings.HasSuffix(name, ".go")
return core.HasSuffix(name, ".go")
})
var banned []string
for _, path := range files {
file := parseGoFile(t, path)
for _, spec := range file.Imports {
importPath := strings.Trim(spec.Path.Value, `"`)
if strings.HasPrefix(importPath, "forge.lthn.ai/") {
importPath := core.TrimPrefix(core.TrimSuffix(spec.Path.Value, `"`), `"`)
if core.HasPrefix(importPath, "forge.lthn.ai/") {
banned = append(banned, path+": "+importPath)
}
}
@ -36,7 +35,7 @@ func TestRepoConventions_Good_BannedImports(t *testing.T) {
func TestRepoConventions_Good_TestNaming(t *testing.T) {
files := repoGoFiles(t, func(name string) bool {
return strings.HasSuffix(name, "_test.go")
return core.HasSuffix(name, "_test.go")
})
var invalid []string
@ -48,10 +47,10 @@ func TestRepoConventions_Good_TestNaming(t *testing.T) {
continue
}
name := fn.Name.Name
if !strings.HasPrefix(name, "Test") || name == "TestMain" {
if !core.HasPrefix(name, "Test") || name == "TestMain" {
continue
}
if strings.Contains(name, "_Good") || strings.Contains(name, "_Bad") || strings.Contains(name, "_Ugly") {
if core.Contains(name, "_Good") || core.Contains(name, "_Bad") || core.Contains(name, "_Ugly") {
continue
}
invalid = append(invalid, path+": "+name)
@ -73,7 +72,7 @@ func repoGoFiles(t *testing.T, keep func(name string) bool) []string {
if entry.IsDir() || !keep(entry.Name()) {
continue
}
files = append(files, filepath.Clean(entry.Name()))
files = append(files, core.CleanPath(entry.Name(), "/"))
}
slices.Sort(files)

View file

@ -2,11 +2,10 @@ package store
import (
"database/sql"
"fmt"
"os"
"path/filepath"
"testing"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -20,7 +19,7 @@ func TestNew_Bad_SchemaConflict(t *testing.T) {
// CREATE TABLE IF NOT EXISTS kv, SQLite returns an error because the
// name "kv" is already taken by the index.
dir := t.TempDir()
dbPath := filepath.Join(dir, "conflict.db")
dbPath := core.JoinPath(dir, "conflict.db")
db, err := sql.Open("sqlite", dbPath)
require.NoError(t, err)
@ -82,7 +81,7 @@ func TestGetAll_Bad_RowsError(t *testing.T) {
// Trigger rows.Err() by corrupting the database file so that iteration
// starts successfully but encounters a malformed page mid-scan.
dir := t.TempDir()
dbPath := filepath.Join(dir, "corrupt-getall.db")
dbPath := core.JoinPath(dir, "corrupt-getall.db")
s, err := New(dbPath)
require.NoError(t, err)
@ -91,8 +90,8 @@ func TestGetAll_Bad_RowsError(t *testing.T) {
const rows = 5000
for i := range rows {
require.NoError(t, s.Set("g",
fmt.Sprintf("key-%06d", i),
fmt.Sprintf("value-with-padding-%06d-xxxxxxxxxxxxxxxxxxxxxxxx", i)))
core.Sprintf("key-%06d", i),
core.Sprintf("value-with-padding-%06d-xxxxxxxxxxxxxxxxxxxxxxxx", i)))
}
s.Close()
@ -176,7 +175,7 @@ func TestRender_Bad_ScanError(t *testing.T) {
func TestRender_Bad_RowsError(t *testing.T) {
// Same corruption technique as TestGetAll_Bad_RowsError.
dir := t.TempDir()
dbPath := filepath.Join(dir, "corrupt-render.db")
dbPath := core.JoinPath(dir, "corrupt-render.db")
s, err := New(dbPath)
require.NoError(t, err)
@ -184,8 +183,8 @@ func TestRender_Bad_RowsError(t *testing.T) {
const rows = 5000
for i := range rows {
require.NoError(t, s.Set("g",
fmt.Sprintf("key-%06d", i),
fmt.Sprintf("value-with-padding-%06d-xxxxxxxxxxxxxxxxxxxxxxxx", i)))
core.Sprintf("key-%06d", i),
core.Sprintf("value-with-padding-%06d-xxxxxxxxxxxxxxxxxxxxxxxx", i)))
}
s.Close()

View file

@ -1,12 +1,12 @@
package store
import (
"fmt"
"sync"
"sync/atomic"
"testing"
"time"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -248,7 +248,7 @@ func TestWatch_Good_BufferFullDoesNotBlock(t *testing.T) {
go func() {
defer close(done)
for i := range 32 {
require.NoError(t, s.Set("g", fmt.Sprintf("k%d", i), "v"))
require.NoError(t, s.Set("g", core.Sprintf("k%d", i), "v"))
}
}()
@ -318,7 +318,7 @@ func TestWatch_Good_ConcurrentWatchUnwatch(t *testing.T) {
// Writers — continuously mutate the store.
wg.Go(func() {
for i := range goroutines * ops {
_ = s.Set("g", fmt.Sprintf("k%d", i), "v")
_ = s.Set("g", core.Sprintf("k%d", i), "v")
}
})

1
go.mod
View file

@ -3,6 +3,7 @@ module dappco.re/go/core/store
go 1.26.0
require (
dappco.re/go/core v0.8.0-alpha.1
dappco.re/go/core/log v0.1.0
github.com/stretchr/testify v1.11.1
modernc.org/sqlite v1.47.0

2
go.sum
View file

@ -1,3 +1,5 @@
dappco.re/go/core v0.8.0-alpha.1 h1:gj7+Scv+L63Z7wMxbJYHhaRFkHJo2u4MMPuUSv/Dhtk=
dappco.re/go/core v0.8.0-alpha.1/go.mod h1:f2/tBZ3+3IqDrg2F5F598llv0nmb/4gJVCFzM5geE4A=
dappco.re/go/core/log v0.1.0 h1:pa71Vq2TD2aoEUQWFKwNcaJ3GBY8HbaNGqtE688Unyc=
dappco.re/go/core/log v0.1.0/go.mod h1:Nkqb8gsXhZAO8VLpx7B8i1iAmohhzqA20b9Zr8VUcJs=
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=

View file

@ -1,12 +1,11 @@
package store
import (
"errors"
"fmt"
"iter"
"regexp"
"time"
core "dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
@ -33,7 +32,7 @@ type ScopedStore struct {
// characters and hyphens.
func NewScoped(store *Store, namespace string) (*ScopedStore, error) {
if !validNamespace.MatchString(namespace) {
return nil, coreerr.E("store.NewScoped", fmt.Sprintf("namespace %q is invalid (must be non-empty, alphanumeric + hyphens)", namespace), nil)
return nil, coreerr.E("store.NewScoped", core.Sprintf("namespace %q is invalid (must be non-empty, alphanumeric + hyphens)", namespace), nil)
}
return &ScopedStore{store: store, namespace: namespace}, nil
}
@ -133,7 +132,7 @@ func (s *ScopedStore) checkQuota(group, key string) error {
// Key exists — this is an upsert, no quota check needed.
return nil
}
if !errors.Is(err, ErrNotFound) {
if !core.Is(err, ErrNotFound) {
// A database error occurred, not just a "not found" result.
return coreerr.E("store.ScopedStore", "quota check", err)
}
@ -145,7 +144,7 @@ func (s *ScopedStore) checkQuota(group, key string) error {
return coreerr.E("store.ScopedStore", "quota check", err)
}
if count >= s.quota.MaxKeys {
return coreerr.E("store.ScopedStore", fmt.Sprintf("key limit (%d)", s.quota.MaxKeys), ErrQuotaExceeded)
return coreerr.E("store.ScopedStore", core.Sprintf("key limit (%d)", s.quota.MaxKeys), ErrQuotaExceeded)
}
}
@ -165,7 +164,7 @@ func (s *ScopedStore) checkQuota(group, key string) error {
count++
}
if count >= s.quota.MaxGroups {
return coreerr.E("store.ScopedStore", fmt.Sprintf("group limit (%d)", s.quota.MaxGroups), ErrQuotaExceeded)
return coreerr.E("store.ScopedStore", core.Sprintf("group limit (%d)", s.quota.MaxGroups), ErrQuotaExceeded)
}
}
}

View file

@ -1,10 +1,10 @@
package store
import (
"errors"
"testing"
"time"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -85,7 +85,7 @@ func TestScopedStore_Good_PrefixedInUnderlyingStore(t *testing.T) {
// Direct access without prefix should fail.
_, err = s.Get("config", "key")
assert.True(t, errors.Is(err, ErrNotFound))
assert.True(t, core.Is(err, ErrNotFound))
}
func TestScopedStore_Good_NamespaceIsolation(t *testing.T) {
@ -116,7 +116,7 @@ func TestScopedStore_Good_Delete(t *testing.T) {
require.NoError(t, sc.Delete("g", "k"))
_, err := sc.Get("g", "k")
assert.True(t, errors.Is(err, ErrNotFound))
assert.True(t, core.Is(err, ErrNotFound))
}
func TestScopedStore_Good_DeleteGroup(t *testing.T) {
@ -187,7 +187,7 @@ func TestScopedStore_Good_SetWithTTL_Expires(t *testing.T) {
time.Sleep(5 * time.Millisecond)
_, err := sc.Get("g", "k")
assert.True(t, errors.Is(err, ErrNotFound))
assert.True(t, core.Is(err, ErrNotFound))
}
func TestScopedStore_Good_Render(t *testing.T) {
@ -221,7 +221,7 @@ func TestQuota_Good_MaxKeys(t *testing.T) {
// 6th key should fail.
err = sc.Set("g", "overflow", "v")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrQuotaExceeded), "expected ErrQuotaExceeded, got: %v", err)
assert.True(t, core.Is(err, ErrQuotaExceeded), "expected ErrQuotaExceeded, got: %v", err)
}
func TestQuota_Good_MaxKeys_AcrossGroups(t *testing.T) {
@ -236,7 +236,7 @@ func TestQuota_Good_MaxKeys_AcrossGroups(t *testing.T) {
// Total is now 3 — any new key should fail regardless of group.
err := sc.Set("g4", "d", "4")
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
}
func TestQuota_Good_UpsertDoesNotCount(t *testing.T) {
@ -303,7 +303,7 @@ func TestQuota_Good_ExpiredKeysExcluded(t *testing.T) {
// Now at 3 — next should fail.
err := sc.Set("g", "new3", "v")
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
}
func TestQuota_Good_SetWithTTL_Enforced(t *testing.T) {
@ -316,7 +316,7 @@ func TestQuota_Good_SetWithTTL_Enforced(t *testing.T) {
require.NoError(t, sc.SetWithTTL("g", "b", "2", time.Hour))
err := sc.SetWithTTL("g", "c", "3", time.Hour)
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
}
// ---------------------------------------------------------------------------
@ -336,7 +336,7 @@ func TestQuota_Good_MaxGroups(t *testing.T) {
// 4th group should fail.
err := sc.Set("g4", "k", "v")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
}
func TestQuota_Good_MaxGroups_ExistingGroupOK(t *testing.T) {
@ -405,7 +405,7 @@ func TestQuota_Good_BothLimits(t *testing.T) {
// Group limit hit.
err := sc.Set("g3", "c", "3")
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
// But adding to existing groups is fine (within key limit).
require.NoError(t, sc.Set("g1", "d", "4"))
@ -425,11 +425,11 @@ func TestQuota_Good_DoesNotAffectOtherNamespaces(t *testing.T) {
// a is at limit — but b's keys don't count against a.
err := a.Set("g", "a3", "v")
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
// b is also at limit independently.
err = b.Set("g", "b3", "v")
assert.True(t, errors.Is(err, ErrQuotaExceeded))
assert.True(t, core.Is(err, ErrQuotaExceeded))
}
// ---------------------------------------------------------------------------

View file

@ -9,6 +9,7 @@ import (
"text/template"
"time"
core "dappco.re/go/core"
coreerr "dappco.re/go/core/log"
_ "modernc.org/sqlite"
)
@ -67,7 +68,7 @@ func New(dbPath string) (*Store, error) {
// Ensure the expires_at column exists for databases created before TTL support.
if _, err := db.Exec("ALTER TABLE kv ADD COLUMN expires_at INTEGER"); err != nil {
// SQLite returns "duplicate column name" if it already exists.
if !strings.Contains(err.Error(), "duplicate column name") {
if !core.Contains(err.Error(), "duplicate column name") {
db.Close()
return nil, coreerr.E("store.New", "migration", err)
}
@ -347,9 +348,9 @@ func (s *Store) GroupsSeq(prefix string) iter.Seq2[string, error] {
// escapeLike escapes SQLite LIKE wildcards and the escape character itself.
func escapeLike(s string) string {
s = strings.ReplaceAll(s, "^", "^^")
s = strings.ReplaceAll(s, "%", "^%")
s = strings.ReplaceAll(s, "_", "^_")
s = core.Replace(s, "^", "^^")
s = core.Replace(s, "%", "^%")
s = core.Replace(s, "_", "^_")
return s
}

View file

@ -3,15 +3,13 @@ package store
import (
"context"
"database/sql"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
"sync"
"testing"
"time"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
@ -28,7 +26,7 @@ func TestNew_Good_Memory(t *testing.T) {
}
func TestNew_Good_FileBacked(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "test.db")
dbPath := core.JoinPath(t.TempDir(), "test.db")
s, err := New(dbPath)
require.NoError(t, err)
require.NotNil(t, s)
@ -58,7 +56,7 @@ func TestNew_Bad_InvalidPath(t *testing.T) {
func TestNew_Bad_CorruptFile(t *testing.T) {
// A file that exists but is not a valid SQLite database should fail.
dir := t.TempDir()
dbPath := filepath.Join(dir, "corrupt.db")
dbPath := core.JoinPath(dir, "corrupt.db")
require.NoError(t, os.WriteFile(dbPath, []byte("not a sqlite database"), 0644))
_, err := New(dbPath)
@ -69,7 +67,7 @@ func TestNew_Bad_CorruptFile(t *testing.T) {
func TestNew_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 := filepath.Join(dir, "readonly.db")
dbPath := core.JoinPath(dir, "readonly.db")
// Create a valid DB first, then make the directory read-only.
s, err := New(dbPath)
@ -90,7 +88,7 @@ func TestNew_Bad_ReadOnlyDir(t *testing.T) {
}
func TestNew_Good_WALMode(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "wal.db")
dbPath := core.JoinPath(t.TempDir(), "wal.db")
s, err := New(dbPath)
require.NoError(t, err)
defer s.Close()
@ -140,7 +138,7 @@ func TestGet_Bad_NotFound(t *testing.T) {
_, err := s.Get("config", "missing")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrNotFound), "should wrap ErrNotFound")
assert.True(t, core.Is(err, ErrNotFound), "should wrap ErrNotFound")
}
func TestGet_Bad_NonExistentGroup(t *testing.T) {
@ -149,7 +147,7 @@ func TestGet_Bad_NonExistentGroup(t *testing.T) {
_, err := s.Get("no-such-group", "key")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrNotFound))
assert.True(t, core.Is(err, ErrNotFound))
}
func TestGet_Bad_ClosedStore(t *testing.T) {
@ -233,7 +231,7 @@ func TestCount_Good_BulkInsert(t *testing.T) {
const total = 500
for i := range total {
require.NoError(t, s.Set("bulk", fmt.Sprintf("key-%04d", i), "v"))
require.NoError(t, s.Set("bulk", core.Sprintf("key-%04d", i), "v"))
}
n, err := s.Count("bulk")
require.NoError(t, err)
@ -521,7 +519,7 @@ func TestStore_Good_GroupIsolation(t *testing.T) {
// ---------------------------------------------------------------------------
func TestConcurrent_Good_ReadWrite(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "concurrent.db")
dbPath := core.JoinPath(t.TempDir(), "concurrent.db")
s, err := New(dbPath)
require.NoError(t, err)
defer s.Close()
@ -537,12 +535,12 @@ func TestConcurrent_Good_ReadWrite(t *testing.T) {
wg.Add(1)
go func(id int) {
defer wg.Done()
group := fmt.Sprintf("grp-%d", id)
group := core.Sprintf("grp-%d", id)
for i := range opsPerGoroutine {
key := fmt.Sprintf("key-%d", i)
val := fmt.Sprintf("val-%d-%d", id, i)
key := core.Sprintf("key-%d", i)
val := core.Sprintf("val-%d-%d", id, i)
if err := s.Set(group, key, val); err != nil {
errs <- fmt.Errorf("writer %d: %w", id, err)
errs <- core.Wrap(err, "writer", core.Sprintf("%d", id))
}
}
}(g)
@ -553,13 +551,13 @@ func TestConcurrent_Good_ReadWrite(t *testing.T) {
wg.Add(1)
go func(id int) {
defer wg.Done()
group := fmt.Sprintf("grp-%d", id)
group := core.Sprintf("grp-%d", id)
for i := range opsPerGoroutine {
key := fmt.Sprintf("key-%d", i)
key := core.Sprintf("key-%d", i)
_, err := s.Get(group, key)
// ErrNotFound is acceptable — the writer may not have written yet.
if err != nil && !errors.Is(err, ErrNotFound) {
errs <- fmt.Errorf("reader %d: %w", id, err)
if err != nil && !core.Is(err, ErrNotFound) {
errs <- core.Wrap(err, "reader", core.Sprintf("%d", id))
}
}
}(g)
@ -574,7 +572,7 @@ func TestConcurrent_Good_ReadWrite(t *testing.T) {
// After all writers finish, every key should be present.
for g := range goroutines {
group := fmt.Sprintf("grp-%d", g)
group := core.Sprintf("grp-%d", g)
n, err := s.Count(group)
require.NoError(t, err)
assert.Equal(t, opsPerGoroutine, n, "group %s should have all keys", group)
@ -582,13 +580,13 @@ func TestConcurrent_Good_ReadWrite(t *testing.T) {
}
func TestConcurrent_Good_GetAll(t *testing.T) {
s, err := New(filepath.Join(t.TempDir(), "getall.db"))
s, err := New(core.JoinPath(t.TempDir(), "getall.db"))
require.NoError(t, err)
defer s.Close()
// Seed data.
for i := range 50 {
require.NoError(t, s.Set("shared", fmt.Sprintf("k%d", i), fmt.Sprintf("v%d", i)))
require.NoError(t, s.Set("shared", core.Sprintf("k%d", i), core.Sprintf("v%d", i)))
}
var wg sync.WaitGroup
@ -608,7 +606,7 @@ func TestConcurrent_Good_GetAll(t *testing.T) {
}
func TestConcurrent_Good_DeleteGroup(t *testing.T) {
s, err := New(filepath.Join(t.TempDir(), "delgrp.db"))
s, err := New(core.JoinPath(t.TempDir(), "delgrp.db"))
require.NoError(t, err)
defer s.Close()
@ -617,9 +615,9 @@ func TestConcurrent_Good_DeleteGroup(t *testing.T) {
wg.Add(1)
go func(id int) {
defer wg.Done()
grp := fmt.Sprintf("g%d", id)
grp := core.Sprintf("g%d", id)
for i := range 20 {
_ = s.Set(grp, fmt.Sprintf("k%d", i), "v")
_ = s.Set(grp, core.Sprintf("k%d", i), "v")
}
_ = s.DeleteGroup(grp)
}(g)
@ -637,7 +635,7 @@ func TestErrNotFound_Good_Is(t *testing.T) {
_, err := s.Get("g", "k")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrNotFound), "error should be ErrNotFound via errors.Is")
assert.True(t, core.Is(err, ErrNotFound), "error should be ErrNotFound via core.Is")
assert.Contains(t, err.Error(), "g/k", "error message should include group/key")
}
@ -651,7 +649,7 @@ func BenchmarkSet(b *testing.B) {
b.ResetTimer()
for i := range b.N {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
}
@ -662,12 +660,12 @@ func BenchmarkGet(b *testing.B) {
// Pre-populate.
const keys = 10000
for i := range keys {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
b.ResetTimer()
for i := range b.N {
_, _ = s.Get("bench", fmt.Sprintf("key-%d", i%keys))
_, _ = s.Get("bench", core.Sprintf("key-%d", i%keys))
}
}
@ -677,7 +675,7 @@ func BenchmarkGetAll(b *testing.B) {
const keys = 10000
for i := range keys {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
b.ResetTimer()
@ -687,13 +685,13 @@ func BenchmarkGetAll(b *testing.B) {
}
func BenchmarkSet_FileBacked(b *testing.B) {
dbPath := filepath.Join(b.TempDir(), "bench.db")
dbPath := core.JoinPath(b.TempDir(), "bench.db")
s, _ := New(dbPath)
defer s.Close()
b.ResetTimer()
for i := range b.N {
_ = s.Set("bench", fmt.Sprintf("key-%d", i), "value")
_ = s.Set("bench", core.Sprintf("key-%d", i), "value")
}
}
@ -741,7 +739,7 @@ func TestSetWithTTL_Good_ExpiresOnGet(t *testing.T) {
_, err := s.Get("g", "ephemeral")
require.Error(t, err)
assert.True(t, errors.Is(err, ErrNotFound), "expired key should be ErrNotFound")
assert.True(t, core.Is(err, ErrNotFound), "expired key should be ErrNotFound")
}
func TestSetWithTTL_Good_ExcludedFromCount(t *testing.T) {
@ -901,7 +899,7 @@ func TestPurgeExpired_Good_BackgroundPurge(t *testing.T) {
// ---------------------------------------------------------------------------
func TestSchemaUpgrade_Good_ExistingDB(t *testing.T) {
dbPath := filepath.Join(t.TempDir(), "upgrade.db")
dbPath := core.JoinPath(t.TempDir(), "upgrade.db")
// Open, write, close.
s1, err := New(dbPath)
@ -927,7 +925,7 @@ func TestSchemaUpgrade_Good_ExistingDB(t *testing.T) {
func TestSchemaUpgrade_Good_PreTTLDatabase(t *testing.T) {
// Simulate a database created before TTL support (no expires_at column).
dbPath := filepath.Join(t.TempDir(), "pre-ttl.db")
dbPath := core.JoinPath(t.TempDir(), "pre-ttl.db")
db, err := sql.Open("sqlite", dbPath)
require.NoError(t, err)
db.SetMaxOpenConns(1)
@ -966,7 +964,7 @@ func TestSchemaUpgrade_Good_PreTTLDatabase(t *testing.T) {
// ---------------------------------------------------------------------------
func TestConcurrent_Good_TTL(t *testing.T) {
s, err := New(filepath.Join(t.TempDir(), "concurrent-ttl.db"))
s, err := New(core.JoinPath(t.TempDir(), "concurrent-ttl.db"))
require.NoError(t, err)
defer s.Close()
@ -978,9 +976,9 @@ func TestConcurrent_Good_TTL(t *testing.T) {
wg.Add(1)
go func(id int) {
defer wg.Done()
grp := fmt.Sprintf("ttl-%d", id)
grp := core.Sprintf("ttl-%d", id)
for i := range ops {
key := fmt.Sprintf("k%d", i)
key := core.Sprintf("k%d", i)
if i%2 == 0 {
_ = s.SetWithTTL(grp, key, "v", 50*time.Millisecond)
} else {
@ -995,7 +993,7 @@ func TestConcurrent_Good_TTL(t *testing.T) {
time.Sleep(60 * time.Millisecond)
for g := range goroutines {
grp := fmt.Sprintf("ttl-%d", g)
grp := core.Sprintf("ttl-%d", g)
n, err := s.Count(grp)
require.NoError(t, err)
assert.Equal(t, ops/2, n, "only non-TTL keys should remain in %s", grp)