diff --git a/events_test.go b/events_test.go index ffaaf44..572c1ad 100644 --- a/events_test.go +++ b/events_test.go @@ -16,31 +16,31 @@ import ( // --------------------------------------------------------------------------- func TestEvents_Watch_Good_SpecificKey(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("config", "theme") - defer s.Unwatch(w) + watcher := storeInstance.Watch("config", "theme") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("config", "theme", "dark")) + require.NoError(t, storeInstance.Set("config", "theme", "dark")) select { - case e := <-w.Events: - assert.Equal(t, EventSet, e.Type) - assert.Equal(t, "config", e.Group) - assert.Equal(t, "theme", e.Key) - assert.Equal(t, "dark", e.Value) - assert.False(t, e.Timestamp.IsZero()) + case event := <-watcher.Events: + assert.Equal(t, EventSet, event.Type) + assert.Equal(t, "config", event.Group) + assert.Equal(t, "theme", event.Key) + assert.Equal(t, "dark", event.Value) + assert.False(t, event.Timestamp.IsZero()) case <-time.After(time.Second): t.Fatal("timed out waiting for event") } // A Set to a different key in the same group should NOT trigger this watcher. - require.NoError(t, s.Set("config", "colour", "blue")) + require.NoError(t, storeInstance.Set("config", "colour", "blue")) select { - case e := <-w.Events: - t.Fatalf("unexpected event for non-matching key: %+v", e) + case event := <-watcher.Events: + t.Fatalf("unexpected event for non-matching key: %+v", event) case <-time.After(50 * time.Millisecond): // Expected: no event. } @@ -51,33 +51,33 @@ func TestEvents_Watch_Good_SpecificKey(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_WildcardKey(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("config", "*") - defer s.Unwatch(w) + watcher := storeInstance.Watch("config", "*") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("config", "theme", "dark")) - require.NoError(t, s.Set("config", "colour", "blue")) + require.NoError(t, storeInstance.Set("config", "theme", "dark")) + require.NoError(t, storeInstance.Set("config", "colour", "blue")) - received := drainEvents(w.Events, 2, time.Second) + received := drainEvents(watcher.Events, 2, time.Second) require.Len(t, received, 2) assert.Equal(t, "theme", received[0].Key) assert.Equal(t, "colour", received[1].Key) } func TestEvents_Watch_Good_GroupMismatch(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("config", "*") - defer s.Unwatch(w) + watcher := storeInstance.Watch("config", "*") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("other", "theme", "dark")) + require.NoError(t, storeInstance.Set("other", "theme", "dark")) select { - case e := <-w.Events: - t.Fatalf("unexpected event for non-matching group: %+v", e) + case event := <-watcher.Events: + t.Fatalf("unexpected event for non-matching group: %+v", event) case <-time.After(50 * time.Millisecond): // Expected: no event. } @@ -88,18 +88,18 @@ func TestEvents_Watch_Good_GroupMismatch(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_WildcardAll(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("*", "*") - defer s.Unwatch(w) + watcher := storeInstance.Watch("*", "*") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("g1", "k1", "v1")) - require.NoError(t, s.Set("g2", "k2", "v2")) - require.NoError(t, s.Delete("g1", "k1")) - require.NoError(t, s.DeleteGroup("g2")) + require.NoError(t, storeInstance.Set("g1", "k1", "v1")) + require.NoError(t, storeInstance.Set("g2", "k2", "v2")) + require.NoError(t, storeInstance.Delete("g1", "k1")) + require.NoError(t, storeInstance.DeleteGroup("g2")) - received := drainEvents(w.Events, 4, time.Second) + received := drainEvents(watcher.Events, 4, time.Second) require.Len(t, received, 4) assert.Equal(t, EventSet, received[0].Type) assert.Equal(t, EventSet, received[1].Type) @@ -112,36 +112,36 @@ func TestEvents_Watch_Good_WildcardAll(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Unwatch_Good_StopsDelivery(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("g", "k") - s.Unwatch(w) + watcher := storeInstance.Watch("g", "k") + storeInstance.Unwatch(watcher) // Channel should be closed. - _, open := <-w.Events + _, open := <-watcher.Events assert.False(t, open, "channel should be closed after Unwatch") // Set after Unwatch should not panic or block. - require.NoError(t, s.Set("g", "k", "v")) + require.NoError(t, storeInstance.Set("g", "k", "v")) } func TestEvents_Unwatch_Good_Idempotent(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("g", "k") + watcher := storeInstance.Watch("g", "k") // Calling Unwatch multiple times should not panic. - s.Unwatch(w) - s.Unwatch(w) // second call is a no-op + storeInstance.Unwatch(watcher) + storeInstance.Unwatch(watcher) // second call is a no-op } func TestEvents_Unwatch_Good_NilWatcher(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - s.Unwatch(nil) + storeInstance.Unwatch(nil) } // --------------------------------------------------------------------------- @@ -149,24 +149,24 @@ func TestEvents_Unwatch_Good_NilWatcher(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_DeleteEvent(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("g", "k") - defer s.Unwatch(w) + watcher := storeInstance.Watch("g", "k") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("g", "k", "v")) + require.NoError(t, storeInstance.Set("g", "k", "v")) // Drain the Set event. - <-w.Events + <-watcher.Events - require.NoError(t, s.Delete("g", "k")) + require.NoError(t, storeInstance.Delete("g", "k")) select { - case e := <-w.Events: - assert.Equal(t, EventDelete, e.Type) - assert.Equal(t, "g", e.Group) - assert.Equal(t, "k", e.Key) - assert.Empty(t, e.Value, "Delete events should have empty Value") + case event := <-watcher.Events: + assert.Equal(t, EventDelete, event.Type) + assert.Equal(t, "g", event.Group) + assert.Equal(t, "k", event.Key) + assert.Empty(t, event.Value, "Delete events should have empty Value") case <-time.After(time.Second): t.Fatal("timed out waiting for delete event") } @@ -177,26 +177,26 @@ func TestEvents_Watch_Good_DeleteEvent(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_DeleteGroupEvent(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // A wildcard-key watcher for the group should receive DeleteGroup events. - w := s.Watch("g", "*") - defer s.Unwatch(w) + watcher := storeInstance.Watch("g", "*") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.Set("g", "a", "1")) - require.NoError(t, s.Set("g", "b", "2")) + require.NoError(t, storeInstance.Set("g", "a", "1")) + require.NoError(t, storeInstance.Set("g", "b", "2")) // Drain Set events. - <-w.Events - <-w.Events + <-watcher.Events + <-watcher.Events - require.NoError(t, s.DeleteGroup("g")) + require.NoError(t, storeInstance.DeleteGroup("g")) select { - case e := <-w.Events: - assert.Equal(t, EventDeleteGroup, e.Type) - assert.Equal(t, "g", e.Group) - assert.Empty(t, e.Key, "DeleteGroup events should have empty Key") + case event := <-watcher.Events: + assert.Equal(t, EventDeleteGroup, event.Type) + assert.Equal(t, "g", event.Group) + assert.Empty(t, event.Key, "DeleteGroup events should have empty Key") case <-time.After(time.Second): t.Fatal("timed out waiting for delete_group event") } @@ -207,21 +207,21 @@ func TestEvents_Watch_Good_DeleteGroupEvent(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_OnChange_Good_Fires(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() var events []Event var mu sync.Mutex - unreg := s.OnChange(func(e Event) { + unregister := storeInstance.OnChange(func(event Event) { mu.Lock() - events = append(events, e) + events = append(events, event) mu.Unlock() }) - defer unreg() + defer unregister() - require.NoError(t, s.Set("g", "k", "v")) - require.NoError(t, s.Delete("g", "k")) + require.NoError(t, storeInstance.Set("g", "k", "v")) + require.NoError(t, storeInstance.Delete("g", "k")) mu.Lock() defer mu.Unlock() @@ -235,25 +235,25 @@ func TestEvents_OnChange_Good_Fires(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_OnChange_Good_Unregister(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() var count atomic.Int32 - unreg := s.OnChange(func(e Event) { + unregister := storeInstance.OnChange(func(event Event) { count.Add(1) }) - require.NoError(t, s.Set("g", "k", "v1")) + require.NoError(t, storeInstance.Set("g", "k", "v1")) assert.Equal(t, int32(1), count.Load()) - unreg() + unregister() - require.NoError(t, s.Set("g", "k", "v2")) + require.NoError(t, storeInstance.Set("g", "k", "v2")) assert.Equal(t, int32(1), count.Load(), "callback should not fire after unregister") - // Calling unreg again should not panic. - unreg() + // Calling unregister again should not panic. + unregister() } // --------------------------------------------------------------------------- @@ -261,16 +261,16 @@ func TestEvents_OnChange_Good_Unregister(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_OnChange_Good_ReentrantSubscriptions(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() var callbackCount atomic.Int32 var unregister func() - unregister = s.OnChange(func(event Event) { + unregister = storeInstance.OnChange(func(event Event) { callbackCount.Add(1) - nestedWatcher := s.Watch("nested", "*") - s.Unwatch(nestedWatcher) + nestedWatcher := storeInstance.Watch("nested", "*") + storeInstance.Unwatch(nestedWatcher) if unregister != nil { unregister() @@ -279,7 +279,7 @@ func TestEvents_OnChange_Good_ReentrantSubscriptions(t *testing.T) { writeDone := make(chan error, 1) go func() { - writeDone <- s.Set("g", "k", "v") + writeDone <- storeInstance.Set("g", "k", "v") }() select { @@ -292,7 +292,7 @@ func TestEvents_OnChange_Good_ReentrantSubscriptions(t *testing.T) { assert.Equal(t, int32(1), callbackCount.Load()) // The callback unregistered itself, so later writes should not increment it. - require.NoError(t, s.Set("g", "k", "v2")) + require.NoError(t, storeInstance.Set("g", "k", "v2")) assert.Equal(t, int32(1), callbackCount.Load()) } @@ -301,18 +301,18 @@ func TestEvents_OnChange_Good_ReentrantSubscriptions(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_BufferFullDoesNotBlock(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("g", "*") - defer s.Unwatch(w) + watcher := storeInstance.Watch("g", "*") + defer storeInstance.Unwatch(watcher) // Fill the buffer (cap 16) plus extra writes. None should block. done := make(chan struct{}) go func() { defer close(done) for i := range 32 { - require.NoError(t, s.Set("g", core.Sprintf("k%d", i), "v")) + require.NoError(t, storeInstance.Set("g", core.Sprintf("k%d", i), "v")) } }() @@ -327,7 +327,7 @@ func TestEvents_Watch_Good_BufferFullDoesNotBlock(t *testing.T) { var received int for range watcherEventBufferCapacity { select { - case <-w.Events: + case <-watcher.Events: received++ default: } @@ -340,29 +340,29 @@ func TestEvents_Watch_Good_BufferFullDoesNotBlock(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_MultipleWatchersSameKey(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w1 := s.Watch("g", "k") - w2 := s.Watch("g", "k") - defer s.Unwatch(w1) - defer s.Unwatch(w2) + firstWatcher := storeInstance.Watch("g", "k") + secondWatcher := storeInstance.Watch("g", "k") + defer storeInstance.Unwatch(firstWatcher) + defer storeInstance.Unwatch(secondWatcher) - require.NoError(t, s.Set("g", "k", "v")) + require.NoError(t, storeInstance.Set("g", "k", "v")) // Both watchers should receive the event independently. select { - case e := <-w1.Events: - assert.Equal(t, EventSet, e.Type) + case event := <-firstWatcher.Events: + assert.Equal(t, EventSet, event.Type) case <-time.After(time.Second): - t.Fatal("w1 timed out") + t.Fatal("firstWatcher timed out") } select { - case e := <-w2.Events: - assert.Equal(t, EventSet, e.Type) + case event := <-secondWatcher.Events: + assert.Equal(t, EventSet, event.Type) case <-time.After(time.Second): - t.Fatal("w2 timed out") + t.Fatal("secondWatcher timed out") } } @@ -371,39 +371,39 @@ func TestEvents_Watch_Good_MultipleWatchersSameKey(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_ConcurrentWatchUnwatch(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() const goroutines = 10 const ops = 50 - var wg sync.WaitGroup + var waitGroup sync.WaitGroup // Writers — continuously mutate the store. - wg.Go(func() { + waitGroup.Go(func() { for i := range goroutines * ops { - _ = s.Set("g", core.Sprintf("k%d", i), "v") + _ = storeInstance.Set("g", core.Sprintf("k%d", i), "v") } }) // Watchers — add and remove watchers concurrently. for range goroutines { - wg.Go(func() { + waitGroup.Go(func() { for range ops { - w := s.Watch("g", "*") + watcher := storeInstance.Watch("g", "*") // Drain a few events to exercise the channel path. for range 3 { select { - case <-w.Events: + case <-watcher.Events: case <-time.After(time.Millisecond): } } - s.Unwatch(w) + storeInstance.Unwatch(watcher) } }) } - wg.Wait() + waitGroup.Wait() // If we got here without a data race or panic, the test passes. } @@ -412,24 +412,24 @@ func TestEvents_Watch_Good_ConcurrentWatchUnwatch(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_ScopedStoreEvents(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, err := NewScoped(s, "tenant-a") + scopedStore, err := NewScoped(storeInstance, "tenant-a") require.NoError(t, err) // Watch on the underlying store with the full prefixed group name. - w := s.Watch("tenant-a:config", "theme") - defer s.Unwatch(w) + watcher := storeInstance.Watch("tenant-a:config", "theme") + defer storeInstance.Unwatch(watcher) - require.NoError(t, sc.Set("config", "theme", "dark")) + require.NoError(t, scopedStore.Set("config", "theme", "dark")) select { - case e := <-w.Events: - assert.Equal(t, EventSet, e.Type) - assert.Equal(t, "tenant-a:config", e.Group) - assert.Equal(t, "theme", e.Key) - assert.Equal(t, "dark", e.Value) + case event := <-watcher.Events: + assert.Equal(t, EventSet, event.Type) + assert.Equal(t, "tenant-a:config", event.Group) + assert.Equal(t, "theme", event.Key) + assert.Equal(t, "dark", event.Value) case <-time.After(time.Second): t.Fatal("timed out waiting for scoped store event") } @@ -451,20 +451,20 @@ func TestEvents_EventType_Good_String(t *testing.T) { // --------------------------------------------------------------------------- func TestEvents_Watch_Good_SetWithTTLEmitsEvent(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - w := s.Watch("g", "k") - defer s.Unwatch(w) + watcher := storeInstance.Watch("g", "k") + defer storeInstance.Unwatch(watcher) - require.NoError(t, s.SetWithTTL("g", "k", "ttl-val", time.Hour)) + require.NoError(t, storeInstance.SetWithTTL("g", "k", "ttl-val", time.Hour)) select { - case e := <-w.Events: - assert.Equal(t, EventSet, e.Type) - assert.Equal(t, "g", e.Group) - assert.Equal(t, "k", e.Key) - assert.Equal(t, "ttl-val", e.Value) + case event := <-watcher.Events: + assert.Equal(t, EventSet, event.Type) + assert.Equal(t, "g", event.Group) + assert.Equal(t, "k", event.Key) + assert.Equal(t, "ttl-val", event.Value) case <-time.After(time.Second): t.Fatal("timed out waiting for SetWithTTL event") } @@ -475,13 +475,13 @@ func TestEvents_Watch_Good_SetWithTTLEmitsEvent(t *testing.T) { // --------------------------------------------------------------------------- // drainEvents collects up to n events from ch within the given timeout. -func drainEvents(ch <-chan Event, n int, timeout time.Duration) []Event { +func drainEvents(ch <-chan Event, count int, timeout time.Duration) []Event { var events []Event deadline := time.After(timeout) - for range n { + for range count { select { - case e := <-ch: - events = append(events, e) + case event := <-ch: + events = append(events, event) case <-deadline: return events } diff --git a/scope_test.go b/scope_test.go index 10d21cc..47bbf08 100644 --- a/scope_test.go +++ b/scope_test.go @@ -14,52 +14,52 @@ import ( // --------------------------------------------------------------------------- func TestScope_NewScoped_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, err := NewScoped(s, "tenant-1") + scopedStore, err := NewScoped(storeInstance, "tenant-1") require.NoError(t, err) - require.NotNil(t, sc) - assert.Equal(t, "tenant-1", sc.Namespace()) + require.NotNil(t, scopedStore) + assert.Equal(t, "tenant-1", scopedStore.Namespace()) } func TestScope_NewScoped_Good_AlphanumericHyphens(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() valid := []string{"abc", "ABC", "123", "a-b-c", "tenant-42", "A1-B2"} - for _, ns := range valid { - sc, err := NewScoped(s, ns) - require.NoError(t, err, "namespace %q should be valid", ns) - require.NotNil(t, sc) + for _, namespace := range valid { + scopedStore, err := NewScoped(storeInstance, namespace) + require.NoError(t, err, "namespace %q should be valid", namespace) + require.NotNil(t, scopedStore) } } func TestScope_NewScoped_Bad_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := NewScoped(s, "") + _, err := NewScoped(storeInstance, "") require.Error(t, err) assert.Contains(t, err.Error(), "invalid") } func TestScope_NewScoped_Bad_InvalidChars(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() invalid := []string{"foo.bar", "foo:bar", "foo bar", "foo/bar", "foo_bar", "tenant!", "@ns"} - for _, ns := range invalid { - _, err := NewScoped(s, ns) - require.Error(t, err, "namespace %q should be invalid", ns) + for _, namespace := range invalid { + _, err := NewScoped(storeInstance, namespace) + require.Error(t, err, "namespace %q should be invalid", namespace) } } func TestScope_NewScopedWithQuota_Bad_InvalidNamespace(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := NewScopedWithQuota(s, "tenant_a", QuotaConfig{MaxKeys: 1}) + _, err := NewScopedWithQuota(storeInstance, "tenant_a", QuotaConfig{MaxKeys: 1}) require.Error(t, err) assert.Contains(t, err.Error(), "store.NewScoped") } @@ -69,104 +69,104 @@ func TestScope_NewScopedWithQuota_Bad_InvalidNamespace(t *testing.T) { // --------------------------------------------------------------------------- func TestScope_ScopedStore_Good_SetGet(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("config", "theme", "dark")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("config", "theme", "dark")) - val, err := sc.Get("config", "theme") + value, err := scopedStore.Get("config", "theme") require.NoError(t, err) - assert.Equal(t, "dark", val) + assert.Equal(t, "dark", value) } func TestScope_ScopedStore_Good_PrefixedInUnderlyingStore(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("config", "key", "val")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("config", "key", "val")) // The underlying store should have the prefixed group name. - val, err := s.Get("tenant-a:config", "key") + value, err := storeInstance.Get("tenant-a:config", "key") require.NoError(t, err) - assert.Equal(t, "val", val) + assert.Equal(t, "val", value) // Direct access without prefix should fail. - _, err = s.Get("config", "key") + _, err = storeInstance.Get("config", "key") assert.True(t, core.Is(err, NotFoundError)) } func TestScope_ScopedStore_Good_NamespaceIsolation(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - a, _ := NewScoped(s, "tenant-a") - b, _ := NewScoped(s, "tenant-b") + alphaStore, _ := NewScoped(storeInstance, "tenant-a") + betaStore, _ := NewScoped(storeInstance, "tenant-b") - require.NoError(t, a.Set("config", "colour", "blue")) - require.NoError(t, b.Set("config", "colour", "red")) + require.NoError(t, alphaStore.Set("config", "colour", "blue")) + require.NoError(t, betaStore.Set("config", "colour", "red")) - va, err := a.Get("config", "colour") + alphaValue, err := alphaStore.Get("config", "colour") require.NoError(t, err) - assert.Equal(t, "blue", va) + assert.Equal(t, "blue", alphaValue) - vb, err := b.Get("config", "colour") + betaValue, err := betaStore.Get("config", "colour") require.NoError(t, err) - assert.Equal(t, "red", vb) + assert.Equal(t, "red", betaValue) } func TestScope_ScopedStore_Good_Delete(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("g", "k", "v")) - require.NoError(t, sc.Delete("g", "k")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("g", "k", "v")) + require.NoError(t, scopedStore.Delete("g", "k")) - _, err := sc.Get("g", "k") + _, err := scopedStore.Get("g", "k") assert.True(t, core.Is(err, NotFoundError)) } func TestScope_ScopedStore_Good_DeleteGroup(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("g", "a", "1")) - require.NoError(t, sc.Set("g", "b", "2")) - require.NoError(t, sc.DeleteGroup("g")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("g", "a", "1")) + require.NoError(t, scopedStore.Set("g", "b", "2")) + require.NoError(t, scopedStore.DeleteGroup("g")) - n, err := sc.Count("g") + count, err := scopedStore.Count("g") require.NoError(t, err) - assert.Equal(t, 0, n) + assert.Equal(t, 0, count) } func TestScope_ScopedStore_Good_GetAll(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - a, _ := NewScoped(s, "tenant-a") - b, _ := NewScoped(s, "tenant-b") + alphaStore, _ := NewScoped(storeInstance, "tenant-a") + betaStore, _ := NewScoped(storeInstance, "tenant-b") - require.NoError(t, a.Set("items", "x", "1")) - require.NoError(t, a.Set("items", "y", "2")) - require.NoError(t, b.Set("items", "z", "3")) + require.NoError(t, alphaStore.Set("items", "x", "1")) + require.NoError(t, alphaStore.Set("items", "y", "2")) + require.NoError(t, betaStore.Set("items", "z", "3")) - all, err := a.GetAll("items") + all, err := alphaStore.GetAll("items") require.NoError(t, err) assert.Equal(t, map[string]string{"x": "1", "y": "2"}, all) - allB, err := b.GetAll("items") + allB, err := betaStore.GetAll("items") require.NoError(t, err) assert.Equal(t, map[string]string{"z": "3"}, allB) } func TestScope_ScopedStore_Good_All(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - scopedStore, _ := NewScoped(s, "tenant-a") + scopedStore, _ := NewScoped(storeInstance, "tenant-a") require.NoError(t, scopedStore.Set("items", "first", "1")) require.NoError(t, scopedStore.Set("items", "second", "2")) @@ -180,52 +180,52 @@ func TestScope_ScopedStore_Good_All(t *testing.T) { } func TestScope_ScopedStore_Good_Count(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("g", "a", "1")) - require.NoError(t, sc.Set("g", "b", "2")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("g", "a", "1")) + require.NoError(t, scopedStore.Set("g", "b", "2")) - n, err := sc.Count("g") + count, err := scopedStore.Count("g") require.NoError(t, err) - assert.Equal(t, 2, n) + assert.Equal(t, 2, count) } func TestScope_ScopedStore_Good_SetWithTTL(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.SetWithTTL("g", "k", "v", time.Hour)) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.SetWithTTL("g", "k", "v", time.Hour)) - val, err := sc.Get("g", "k") + value, err := scopedStore.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) } func TestScope_ScopedStore_Good_SetWithTTL_Expires(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.SetWithTTL("g", "k", "v", 1*time.Millisecond)) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.SetWithTTL("g", "k", "v", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - _, err := sc.Get("g", "k") + _, err := scopedStore.Get("g", "k") assert.True(t, core.Is(err, NotFoundError)) } func TestScope_ScopedStore_Good_Render(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScoped(s, "tenant-a") - require.NoError(t, sc.Set("user", "name", "Alice")) + scopedStore, _ := NewScoped(storeInstance, "tenant-a") + require.NoError(t, scopedStore.Set("user", "name", "Alice")) - out, err := sc.Render("Hello {{ .name }}", "user") + renderedTemplate, err := scopedStore.Render("Hello {{ .name }}", "user") require.NoError(t, err) - assert.Equal(t, "Hello Alice", out) + assert.Equal(t, "Hello Alice", renderedTemplate) } // --------------------------------------------------------------------------- @@ -233,19 +233,19 @@ func TestScope_ScopedStore_Good_Render(t *testing.T) { // --------------------------------------------------------------------------- func TestScope_Quota_Good_MaxKeys(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, err := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 5}) + scopedStore, err := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 5}) require.NoError(t, err) // Insert 5 keys across different groups — should be fine. for i := range 5 { - require.NoError(t, sc.Set("g", keyName(i), "v")) + require.NoError(t, scopedStore.Set("g", keyName(i), "v")) } // 6th key should fail. - err = sc.Set("g", "overflow", "v") + err = scopedStore.Set("g", "overflow", "v") require.Error(t, err) assert.True(t, core.Is(err, QuotaExceededError), "expected QuotaExceededError, got: %v", err) } @@ -268,97 +268,97 @@ func TestScope_Quota_Bad_QuotaCheckQueryError(t *testing.T) { } func TestScope_Quota_Good_MaxKeys_AcrossGroups(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 3}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 3}) - require.NoError(t, sc.Set("g1", "a", "1")) - require.NoError(t, sc.Set("g2", "b", "2")) - require.NoError(t, sc.Set("g3", "c", "3")) + require.NoError(t, scopedStore.Set("g1", "a", "1")) + require.NoError(t, scopedStore.Set("g2", "b", "2")) + require.NoError(t, scopedStore.Set("g3", "c", "3")) // Total is now 3 — any new key should fail regardless of group. - err := sc.Set("g4", "d", "4") + err := scopedStore.Set("g4", "d", "4") assert.True(t, core.Is(err, QuotaExceededError)) } func TestScope_Quota_Good_UpsertDoesNotCount(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 3}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 3}) - require.NoError(t, sc.Set("g", "a", "1")) - require.NoError(t, sc.Set("g", "b", "2")) - require.NoError(t, sc.Set("g", "c", "3")) + require.NoError(t, scopedStore.Set("g", "a", "1")) + require.NoError(t, scopedStore.Set("g", "b", "2")) + require.NoError(t, scopedStore.Set("g", "c", "3")) // Upserting existing key should succeed. - require.NoError(t, sc.Set("g", "a", "updated")) + require.NoError(t, scopedStore.Set("g", "a", "updated")) - val, err := sc.Get("g", "a") + value, err := scopedStore.Get("g", "a") require.NoError(t, err) - assert.Equal(t, "updated", val) + assert.Equal(t, "updated", value) } func TestScope_Quota_Good_DeleteAndReInsert(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 3}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 3}) - require.NoError(t, sc.Set("g", "a", "1")) - require.NoError(t, sc.Set("g", "b", "2")) - require.NoError(t, sc.Set("g", "c", "3")) + require.NoError(t, scopedStore.Set("g", "a", "1")) + require.NoError(t, scopedStore.Set("g", "b", "2")) + require.NoError(t, scopedStore.Set("g", "c", "3")) // Delete one key, then insert a new one — should work. - require.NoError(t, sc.Delete("g", "c")) - require.NoError(t, sc.Set("g", "d", "4")) + require.NoError(t, scopedStore.Delete("g", "c")) + require.NoError(t, scopedStore.Set("g", "d", "4")) } func TestScope_Quota_Good_ZeroMeansUnlimited(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 0, MaxGroups: 0}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 0, MaxGroups: 0}) // Should be able to insert many keys and groups without error. for i := range 100 { - require.NoError(t, sc.Set("g", keyName(i), "v")) + require.NoError(t, scopedStore.Set("g", keyName(i), "v")) } } func TestScope_Quota_Good_ExpiredKeysExcluded(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 3}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 3}) // Insert 3 keys, 2 with short TTL. - require.NoError(t, sc.SetWithTTL("g", "temp1", "v", 1*time.Millisecond)) - require.NoError(t, sc.SetWithTTL("g", "temp2", "v", 1*time.Millisecond)) - require.NoError(t, sc.Set("g", "permanent", "v")) + require.NoError(t, scopedStore.SetWithTTL("g", "temp1", "v", 1*time.Millisecond)) + require.NoError(t, scopedStore.SetWithTTL("g", "temp2", "v", 1*time.Millisecond)) + require.NoError(t, scopedStore.Set("g", "permanent", "v")) time.Sleep(5 * time.Millisecond) // After expiry, only 1 key counts — should be able to insert 2 more. - require.NoError(t, sc.Set("g", "new1", "v")) - require.NoError(t, sc.Set("g", "new2", "v")) + require.NoError(t, scopedStore.Set("g", "new1", "v")) + require.NoError(t, scopedStore.Set("g", "new2", "v")) // Now at 3 — next should fail. - err := sc.Set("g", "new3", "v") + err := scopedStore.Set("g", "new3", "v") assert.True(t, core.Is(err, QuotaExceededError)) } func TestScope_Quota_Good_SetWithTTL_Enforced(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 2}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 2}) - require.NoError(t, sc.SetWithTTL("g", "a", "1", time.Hour)) - require.NoError(t, sc.SetWithTTL("g", "b", "2", time.Hour)) + require.NoError(t, scopedStore.SetWithTTL("g", "a", "1", time.Hour)) + require.NoError(t, scopedStore.SetWithTTL("g", "b", "2", time.Hour)) - err := sc.SetWithTTL("g", "c", "3", time.Hour) + err := scopedStore.SetWithTTL("g", "c", "3", time.Hour) assert.True(t, core.Is(err, QuotaExceededError)) } @@ -367,111 +367,111 @@ func TestScope_Quota_Good_SetWithTTL_Enforced(t *testing.T) { // --------------------------------------------------------------------------- func TestScope_Quota_Good_MaxGroups(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxGroups: 3}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxGroups: 3}) - require.NoError(t, sc.Set("g1", "k", "v")) - require.NoError(t, sc.Set("g2", "k", "v")) - require.NoError(t, sc.Set("g3", "k", "v")) + require.NoError(t, scopedStore.Set("g1", "k", "v")) + require.NoError(t, scopedStore.Set("g2", "k", "v")) + require.NoError(t, scopedStore.Set("g3", "k", "v")) // 4th group should fail. - err := sc.Set("g4", "k", "v") + err := scopedStore.Set("g4", "k", "v") require.Error(t, err) assert.True(t, core.Is(err, QuotaExceededError)) } func TestScope_Quota_Good_MaxGroups_ExistingGroupOK(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxGroups: 2}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxGroups: 2}) - require.NoError(t, sc.Set("g1", "a", "1")) - require.NoError(t, sc.Set("g2", "b", "2")) + require.NoError(t, scopedStore.Set("g1", "a", "1")) + require.NoError(t, scopedStore.Set("g2", "b", "2")) // Adding more keys to existing groups should be fine. - require.NoError(t, sc.Set("g1", "c", "3")) - require.NoError(t, sc.Set("g2", "d", "4")) + require.NoError(t, scopedStore.Set("g1", "c", "3")) + require.NoError(t, scopedStore.Set("g2", "d", "4")) } func TestScope_Quota_Good_MaxGroups_DeleteAndRecreate(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxGroups: 2}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxGroups: 2}) - require.NoError(t, sc.Set("g1", "k", "v")) - require.NoError(t, sc.Set("g2", "k", "v")) + require.NoError(t, scopedStore.Set("g1", "k", "v")) + require.NoError(t, scopedStore.Set("g2", "k", "v")) // Delete a group, then create a new one. - require.NoError(t, sc.DeleteGroup("g1")) - require.NoError(t, sc.Set("g3", "k", "v")) + require.NoError(t, scopedStore.DeleteGroup("g1")) + require.NoError(t, scopedStore.Set("g3", "k", "v")) } func TestScope_Quota_Good_MaxGroups_ZeroUnlimited(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxGroups: 0}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxGroups: 0}) for i := range 50 { - require.NoError(t, sc.Set(keyName(i), "k", "v")) + require.NoError(t, scopedStore.Set(keyName(i), "k", "v")) } } func TestScope_Quota_Good_MaxGroups_ExpiredGroupExcluded(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxGroups: 2}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxGroups: 2}) // Create 2 groups, one with only TTL keys. - require.NoError(t, sc.SetWithTTL("g1", "k", "v", 1*time.Millisecond)) - require.NoError(t, sc.Set("g2", "k", "v")) + require.NoError(t, scopedStore.SetWithTTL("g1", "k", "v", 1*time.Millisecond)) + require.NoError(t, scopedStore.Set("g2", "k", "v")) time.Sleep(5 * time.Millisecond) // g1's only key has expired, so group count should be 1 — we can create a new one. - require.NoError(t, sc.Set("g3", "k", "v")) + require.NoError(t, scopedStore.Set("g3", "k", "v")) } func TestScope_Quota_Good_BothLimits(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - sc, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 10, MaxGroups: 2}) + scopedStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 10, MaxGroups: 2}) - require.NoError(t, sc.Set("g1", "a", "1")) - require.NoError(t, sc.Set("g2", "b", "2")) + require.NoError(t, scopedStore.Set("g1", "a", "1")) + require.NoError(t, scopedStore.Set("g2", "b", "2")) // Group limit hit. - err := sc.Set("g3", "c", "3") + err := scopedStore.Set("g3", "c", "3") assert.True(t, core.Is(err, QuotaExceededError)) // But adding to existing groups is fine (within key limit). - require.NoError(t, sc.Set("g1", "d", "4")) + require.NoError(t, scopedStore.Set("g1", "d", "4")) } func TestScope_Quota_Good_DoesNotAffectOtherNamespaces(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - a, _ := NewScopedWithQuota(s, "tenant-a", QuotaConfig{MaxKeys: 2}) - b, _ := NewScopedWithQuota(s, "tenant-b", QuotaConfig{MaxKeys: 2}) + alphaStore, _ := NewScopedWithQuota(storeInstance, "tenant-a", QuotaConfig{MaxKeys: 2}) + betaStore, _ := NewScopedWithQuota(storeInstance, "tenant-b", QuotaConfig{MaxKeys: 2}) - require.NoError(t, a.Set("g", "a1", "v")) - require.NoError(t, a.Set("g", "a2", "v")) - require.NoError(t, b.Set("g", "b1", "v")) - require.NoError(t, b.Set("g", "b2", "v")) + require.NoError(t, alphaStore.Set("g", "a1", "v")) + require.NoError(t, alphaStore.Set("g", "a2", "v")) + require.NoError(t, betaStore.Set("g", "b1", "v")) + require.NoError(t, betaStore.Set("g", "b2", "v")) - // a is at limit — but b's keys don't count against a. - err := a.Set("g", "a3", "v") + // alphaStore is at limit — but betaStore's keys don't count against alphaStore. + err := alphaStore.Set("g", "a3", "v") assert.True(t, core.Is(err, QuotaExceededError)) - // b is also at limit independently. - err = b.Set("g", "b3", "v") + // betaStore is also at limit independently. + err = betaStore.Set("g", "b3", "v") assert.True(t, core.Is(err, QuotaExceededError)) } @@ -480,86 +480,86 @@ func TestScope_Quota_Good_DoesNotAffectOtherNamespaces(t *testing.T) { // --------------------------------------------------------------------------- func TestScope_CountAll_Good_WithPrefix(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("ns-a:g1", "k1", "v")) - require.NoError(t, s.Set("ns-a:g1", "k2", "v")) - require.NoError(t, s.Set("ns-a:g2", "k1", "v")) - require.NoError(t, s.Set("ns-b:g1", "k1", "v")) + require.NoError(t, storeInstance.Set("ns-a:g1", "k1", "v")) + require.NoError(t, storeInstance.Set("ns-a:g1", "k2", "v")) + require.NoError(t, storeInstance.Set("ns-a:g2", "k1", "v")) + require.NoError(t, storeInstance.Set("ns-b:g1", "k1", "v")) - n, err := s.CountAll("ns-a:") + count, err := storeInstance.CountAll("ns-a:") require.NoError(t, err) - assert.Equal(t, 3, n) + assert.Equal(t, 3, count) - n, err = s.CountAll("ns-b:") + count, err = storeInstance.CountAll("ns-b:") require.NoError(t, err) - assert.Equal(t, 1, n) + assert.Equal(t, 1, count) } func TestScope_CountAll_Good_WithPrefix_Wildcards(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Add keys in groups that look like wildcards. - require.NoError(t, s.Set("user_1", "k", "v")) - require.NoError(t, s.Set("user_2", "k", "v")) - require.NoError(t, s.Set("user%test", "k", "v")) - require.NoError(t, s.Set("user_test", "k", "v")) + require.NoError(t, storeInstance.Set("user_1", "k", "v")) + require.NoError(t, storeInstance.Set("user_2", "k", "v")) + require.NoError(t, storeInstance.Set("user%test", "k", "v")) + require.NoError(t, storeInstance.Set("user_test", "k", "v")) // Prefix "user_" should ONLY match groups starting with "user_". // Since we escape "_", it matches literal "_". // Groups: "user_1", "user_2", "user_test" (3 total). // "user%test" is NOT matched because "_" is literal. - n, err := s.CountAll("user_") + count, err := storeInstance.CountAll("user_") require.NoError(t, err) - assert.Equal(t, 3, n) + assert.Equal(t, 3, count) // Prefix "user%" should ONLY match "user%test". - n, err = s.CountAll("user%") + count, err = storeInstance.CountAll("user%") require.NoError(t, err) - assert.Equal(t, 1, n) + assert.Equal(t, 1, count) } func TestScope_CountAll_Good_EmptyPrefix(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g1", "k1", "v")) - require.NoError(t, s.Set("g2", "k2", "v")) + require.NoError(t, storeInstance.Set("g1", "k1", "v")) + require.NoError(t, storeInstance.Set("g2", "k2", "v")) - n, err := s.CountAll("") + count, err := storeInstance.CountAll("") require.NoError(t, err) - assert.Equal(t, 2, n) + assert.Equal(t, 2, count) } func TestScope_CountAll_Good_ExcludesExpired(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("ns:g", "permanent", "v")) - require.NoError(t, s.SetWithTTL("ns:g", "temp", "v", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("ns:g", "permanent", "v")) + require.NoError(t, storeInstance.SetWithTTL("ns:g", "temp", "v", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - n, err := s.CountAll("ns:") + count, err := storeInstance.CountAll("ns:") require.NoError(t, err) - assert.Equal(t, 1, n, "expired keys should not be counted") + assert.Equal(t, 1, count, "expired keys should not be counted") } func TestScope_CountAll_Good_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - n, err := s.CountAll("nonexistent:") + count, err := storeInstance.CountAll("nonexistent:") require.NoError(t, err) - assert.Equal(t, 0, n) + assert.Equal(t, 0, count) } func TestScope_CountAll_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.CountAll("") + _, err := storeInstance.CountAll("") require.Error(t, err) } @@ -568,15 +568,15 @@ func TestScope_CountAll_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestScope_Groups_Good_WithPrefix(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("ns-a:g1", "k", "v")) - require.NoError(t, s.Set("ns-a:g2", "k", "v")) - require.NoError(t, s.Set("ns-a:g2", "k2", "v")) // duplicate group - require.NoError(t, s.Set("ns-b:g1", "k", "v")) + require.NoError(t, storeInstance.Set("ns-a:g1", "k", "v")) + require.NoError(t, storeInstance.Set("ns-a:g2", "k", "v")) + require.NoError(t, storeInstance.Set("ns-a:g2", "k2", "v")) // duplicate group + require.NoError(t, storeInstance.Set("ns-b:g1", "k", "v")) - groups, err := s.Groups("ns-a:") + groups, err := storeInstance.Groups("ns-a:") require.NoError(t, err) assert.Len(t, groups, 2) assert.Contains(t, groups, "ns-a:g1") @@ -584,61 +584,61 @@ func TestScope_Groups_Good_WithPrefix(t *testing.T) { } func TestScope_Groups_Good_EmptyPrefix(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g1", "k", "v")) - require.NoError(t, s.Set("g2", "k", "v")) - require.NoError(t, s.Set("g3", "k", "v")) + require.NoError(t, storeInstance.Set("g1", "k", "v")) + require.NoError(t, storeInstance.Set("g2", "k", "v")) + require.NoError(t, storeInstance.Set("g3", "k", "v")) - groups, err := s.Groups("") + groups, err := storeInstance.Groups("") require.NoError(t, err) assert.Len(t, groups, 3) } func TestScope_Groups_Good_Distinct(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Multiple keys in the same group should produce one entry. - require.NoError(t, s.Set("g1", "a", "v")) - require.NoError(t, s.Set("g1", "b", "v")) - require.NoError(t, s.Set("g1", "c", "v")) + require.NoError(t, storeInstance.Set("g1", "a", "v")) + require.NoError(t, storeInstance.Set("g1", "b", "v")) + require.NoError(t, storeInstance.Set("g1", "c", "v")) - groups, err := s.Groups("") + groups, err := storeInstance.Groups("") require.NoError(t, err) assert.Len(t, groups, 1) assert.Equal(t, "g1", groups[0]) } func TestScope_Groups_Good_ExcludesExpired(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("ns:g1", "permanent", "v")) - require.NoError(t, s.SetWithTTL("ns:g2", "temp", "v", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("ns:g1", "permanent", "v")) + require.NoError(t, storeInstance.SetWithTTL("ns:g2", "temp", "v", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - groups, err := s.Groups("ns:") + groups, err := storeInstance.Groups("ns:") require.NoError(t, err) assert.Len(t, groups, 1, "group with only expired keys should be excluded") assert.Equal(t, "ns:g1", groups[0]) } func TestScope_Groups_Good_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - groups, err := s.Groups("nonexistent:") + groups, err := storeInstance.Groups("nonexistent:") require.NoError(t, err) assert.Empty(t, groups) } func TestScope_Groups_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.Groups("") + _, err := storeInstance.Groups("") require.Error(t, err) } diff --git a/store_test.go b/store_test.go index c85966d..83f4546 100644 --- a/store_test.go +++ b/store_test.go @@ -19,30 +19,30 @@ import ( // --------------------------------------------------------------------------- func TestStore_New_Good_Memory(t *testing.T) { - s, err := New(":memory:") + storeInstance, err := New(":memory:") require.NoError(t, err) - require.NotNil(t, s) - defer s.Close() + require.NotNil(t, storeInstance) + defer storeInstance.Close() } func TestStore_New_Good_FileBacked(t *testing.T) { dbPath := testPath(t, "test.db") - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - require.NotNil(t, s) - defer s.Close() + require.NotNil(t, storeInstance) + defer storeInstance.Close() // Verify data persists: write, close, reopen. - require.NoError(t, s.Set("g", "k", "v")) - require.NoError(t, s.Close()) + require.NoError(t, storeInstance.Set("g", "k", "v")) + require.NoError(t, storeInstance.Close()) - s2, err := New(dbPath) + reopenedStore, err := New(dbPath) require.NoError(t, err) - defer s2.Close() + defer reopenedStore.Close() - val, err := s2.Get("g", "k") + value, err := reopenedStore.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) } func TestStore_New_Bad_InvalidPath(t *testing.T) { @@ -69,9 +69,9 @@ func TestStore_New_Bad_ReadOnlyDir(t *testing.T) { dbPath := core.Path(dir, "readonly.db") // Create a valid database first, then make the directory read-only. - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - require.NoError(t, s.Close()) + require.NoError(t, storeInstance.Close()) // Remove WAL/SHM files and make directory read-only. _ = testFilesystem().Delete(dbPath + "-wal") @@ -88,12 +88,12 @@ func TestStore_New_Bad_ReadOnlyDir(t *testing.T) { func TestStore_New_Good_WALMode(t *testing.T) { dbPath := testPath(t, "wal.db") - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() var mode string - err = s.database.QueryRow("PRAGMA journal_mode").Scan(&mode) + err = storeInstance.database.QueryRow("PRAGMA journal_mode").Scan(&mode) require.NoError(t, err) assert.Equal(t, "wal", mode, "journal_mode should be WAL") } @@ -103,65 +103,65 @@ func TestStore_New_Good_WALMode(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_SetGet_Good(t *testing.T) { - s, err := New(":memory:") + storeInstance, err := New(":memory:") require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() - err = s.Set("config", "theme", "dark") + err = storeInstance.Set("config", "theme", "dark") require.NoError(t, err) - val, err := s.Get("config", "theme") + value, err := storeInstance.Get("config", "theme") require.NoError(t, err) - assert.Equal(t, "dark", val) + assert.Equal(t, "dark", value) } func TestStore_Set_Good_Upsert(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "k", "v1")) - require.NoError(t, s.Set("g", "k", "v2")) + require.NoError(t, storeInstance.Set("g", "k", "v1")) + require.NoError(t, storeInstance.Set("g", "k", "v2")) - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v2", val, "upsert should overwrite the value") + assert.Equal(t, "v2", value, "upsert should overwrite the value") - n, err := s.Count("g") + count, err := storeInstance.Count("g") require.NoError(t, err) - assert.Equal(t, 1, n, "upsert should not duplicate keys") + assert.Equal(t, 1, count, "upsert should not duplicate keys") } func TestStore_Get_Bad_NotFound(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.Get("config", "missing") + _, err := storeInstance.Get("config", "missing") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError), "should wrap NotFoundError") } func TestStore_Get_Bad_NonExistentGroup(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.Get("no-such-group", "key") + _, err := storeInstance.Get("no-such-group", "key") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError)) } func TestStore_Get_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.Get("g", "k") + _, err := storeInstance.Get("g", "k") require.Error(t, err) } func TestStore_Set_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - err := s.Set("g", "k", "v") + err := storeInstance.Set("g", "k", "v") require.Error(t, err) } @@ -170,31 +170,31 @@ func TestStore_Set_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Delete_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("config", "key", "val") - err := s.Delete("config", "key") + _ = storeInstance.Set("config", "key", "val") + err := storeInstance.Delete("config", "key") require.NoError(t, err) - _, err = s.Get("config", "key") + _, err = storeInstance.Get("config", "key") assert.Error(t, err) } func TestStore_Delete_Good_NonExistent(t *testing.T) { // Deleting a key that does not exist should not error. - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - err := s.Delete("g", "nope") + err := storeInstance.Delete("g", "nope") assert.NoError(t, err) } func TestStore_Delete_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - err := s.Delete("g", "k") + err := storeInstance.Delete("g", "k") require.Error(t, err) } @@ -203,45 +203,45 @@ func TestStore_Delete_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Count_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("grp", "a", "1") - _ = s.Set("grp", "b", "2") - _ = s.Set("other", "c", "3") + _ = storeInstance.Set("grp", "a", "1") + _ = storeInstance.Set("grp", "b", "2") + _ = storeInstance.Set("other", "c", "3") - n, err := s.Count("grp") + count, err := storeInstance.Count("grp") require.NoError(t, err) - assert.Equal(t, 2, n) + assert.Equal(t, 2, count) } func TestStore_Count_Good_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - n, err := s.Count("empty") + count, err := storeInstance.Count("empty") require.NoError(t, err) - assert.Equal(t, 0, n) + assert.Equal(t, 0, count) } func TestStore_Count_Good_BulkInsert(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() const total = 500 for i := range total { - require.NoError(t, s.Set("bulk", core.Sprintf("key-%04d", i), "v")) + require.NoError(t, storeInstance.Set("bulk", core.Sprintf("key-%04d", i), "v")) } - n, err := s.Count("bulk") + count, err := storeInstance.Count("bulk") require.NoError(t, err) - assert.Equal(t, total, n) + assert.Equal(t, total, count) } func TestStore_Count_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.Count("g") + _, err := storeInstance.Count("g") require.Error(t, err) } @@ -250,52 +250,52 @@ func TestStore_Count_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_DeleteGroup_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("grp", "a", "1") - _ = s.Set("grp", "b", "2") - err := s.DeleteGroup("grp") + _ = storeInstance.Set("grp", "a", "1") + _ = storeInstance.Set("grp", "b", "2") + err := storeInstance.DeleteGroup("grp") require.NoError(t, err) - n, _ := s.Count("grp") - assert.Equal(t, 0, n) + count, _ := storeInstance.Count("grp") + assert.Equal(t, 0, count) } func TestStore_DeleteGroup_Good_ThenGetAllEmpty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("grp", "a", "1") - _ = s.Set("grp", "b", "2") - require.NoError(t, s.DeleteGroup("grp")) + _ = storeInstance.Set("grp", "a", "1") + _ = storeInstance.Set("grp", "b", "2") + require.NoError(t, storeInstance.DeleteGroup("grp")) - all, err := s.GetAll("grp") + all, err := storeInstance.GetAll("grp") require.NoError(t, err) assert.Empty(t, all) } func TestStore_DeleteGroup_Good_IsolatesOtherGroups(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("a", "k", "1") - _ = s.Set("b", "k", "2") - require.NoError(t, s.DeleteGroup("a")) + _ = storeInstance.Set("a", "k", "1") + _ = storeInstance.Set("b", "k", "2") + require.NoError(t, storeInstance.DeleteGroup("a")) - _, err := s.Get("a", "k") + _, err := storeInstance.Get("a", "k") assert.Error(t, err) - val, err := s.Get("b", "k") + value, err := storeInstance.Get("b", "k") require.NoError(t, err) - assert.Equal(t, "2", val, "other group should be untouched") + assert.Equal(t, "2", value, "other group should be untouched") } func TestStore_DeleteGroup_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - err := s.DeleteGroup("g") + err := storeInstance.DeleteGroup("g") require.Error(t, err) } @@ -304,32 +304,32 @@ func TestStore_DeleteGroup_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_GetAll_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("grp", "a", "1") - _ = s.Set("grp", "b", "2") - _ = s.Set("other", "c", "3") + _ = storeInstance.Set("grp", "a", "1") + _ = storeInstance.Set("grp", "b", "2") + _ = storeInstance.Set("other", "c", "3") - all, err := s.GetAll("grp") + all, err := storeInstance.GetAll("grp") require.NoError(t, err) assert.Equal(t, map[string]string{"a": "1", "b": "2"}, all) } func TestStore_GetAll_Good_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - all, err := s.GetAll("empty") + all, err := storeInstance.GetAll("empty") require.NoError(t, err) assert.Empty(t, all) } func TestStore_GetAll_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.GetAll("g") + _, err := storeInstance.GetAll("g") require.Error(t, err) } @@ -338,13 +338,13 @@ func TestStore_GetAll_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_All_Good_StopsEarly(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "a", "1")) - require.NoError(t, s.Set("g", "b", "2")) + require.NoError(t, storeInstance.Set("g", "a", "1")) + require.NoError(t, storeInstance.Set("g", "b", "2")) - entries := s.All("g") + entries := storeInstance.All("g") var seen []string for entry, err := range entries { require.NoError(t, err) @@ -356,22 +356,22 @@ func TestStore_All_Good_StopsEarly(t *testing.T) { } func TestStore_All_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - for _, err := range s.All("g") { + for _, err := range storeInstance.All("g") { require.Error(t, err) } } func TestStore_GroupsSeq_Good_StopsEarly(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("alpha", "a", "1")) - require.NoError(t, s.Set("beta", "b", "2")) + require.NoError(t, storeInstance.Set("alpha", "a", "1")) + require.NoError(t, storeInstance.Set("beta", "b", "2")) - groups := s.GroupsSeq("") + groups := storeInstance.GroupsSeq("") var seen []string for group, err := range groups { require.NoError(t, err) @@ -383,13 +383,13 @@ func TestStore_GroupsSeq_Good_StopsEarly(t *testing.T) { } func TestStore_GroupsSeq_Good_PrefixStopsEarly(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("alpha", "a", "1")) - require.NoError(t, s.Set("beta", "b", "2")) + require.NoError(t, storeInstance.Set("alpha", "a", "1")) + require.NoError(t, storeInstance.Set("beta", "b", "2")) - groups := s.GroupsSeq("alpha") + groups := storeInstance.GroupsSeq("alpha") var seen []string for group, err := range groups { require.NoError(t, err) @@ -401,10 +401,10 @@ func TestStore_GroupsSeq_Good_PrefixStopsEarly(t *testing.T) { } func TestStore_GroupsSeq_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - for _, err := range s.GroupsSeq("") { + for _, err := range storeInstance.GroupsSeq("") { require.Error(t, err) } } @@ -414,12 +414,12 @@ func TestStore_GroupsSeq_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_GetSplit_Good_SplitsValue(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "comma", "alpha,beta,gamma")) + require.NoError(t, storeInstance.Set("g", "comma", "alpha,beta,gamma")) - parts, err := s.GetSplit("g", "comma", ",") + parts, err := storeInstance.GetSplit("g", "comma", ",") require.NoError(t, err) var values []string @@ -431,12 +431,12 @@ func TestStore_GetSplit_Good_SplitsValue(t *testing.T) { } func TestStore_GetSplit_Good_StopsEarly(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "comma", "alpha,beta,gamma")) + require.NoError(t, storeInstance.Set("g", "comma", "alpha,beta,gamma")) - parts, err := s.GetSplit("g", "comma", ",") + parts, err := storeInstance.GetSplit("g", "comma", ",") require.NoError(t, err) var values []string @@ -449,21 +449,21 @@ func TestStore_GetSplit_Good_StopsEarly(t *testing.T) { } func TestStore_GetSplit_Bad_MissingKey(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.GetSplit("g", "missing", ",") + _, err := storeInstance.GetSplit("g", "missing", ",") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError)) } func TestStore_GetFields_Good_SplitsWhitespace(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "fields", "alpha beta\tgamma\n")) + require.NoError(t, storeInstance.Set("g", "fields", "alpha beta\tgamma\n")) - fields, err := s.GetFields("g", "fields") + fields, err := storeInstance.GetFields("g", "fields") require.NoError(t, err) var values []string @@ -475,12 +475,12 @@ func TestStore_GetFields_Good_SplitsWhitespace(t *testing.T) { } func TestStore_GetFields_Good_StopsEarly(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "fields", "alpha beta\tgamma\n")) + require.NoError(t, storeInstance.Set("g", "fields", "alpha beta\tgamma\n")) - fields, err := s.GetFields("g", "fields") + fields, err := storeInstance.GetFields("g", "fields") require.NoError(t, err) var values []string @@ -493,10 +493,10 @@ func TestStore_GetFields_Good_StopsEarly(t *testing.T) { } func TestStore_GetFields_Bad_MissingKey(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.GetFields("g", "missing") + _, err := storeInstance.GetFields("g", "missing") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError)) } @@ -506,66 +506,66 @@ func TestStore_GetFields_Bad_MissingKey(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Render_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("user", "pool", "pool.lthn.io:3333") - _ = s.Set("user", "wallet", "iz...") + _ = storeInstance.Set("user", "pool", "pool.lthn.io:3333") + _ = storeInstance.Set("user", "wallet", "iz...") templateSource := `{"pool":"{{ .pool }}","wallet":"{{ .wallet }}"}` - out, err := s.Render(templateSource, "user") + renderedTemplate, err := storeInstance.Render(templateSource, "user") require.NoError(t, err) - assert.Contains(t, out, "pool.lthn.io:3333") - assert.Contains(t, out, "iz...") + assert.Contains(t, renderedTemplate, "pool.lthn.io:3333") + assert.Contains(t, renderedTemplate, "iz...") } func TestStore_Render_Good_EmptyGroup(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Template that does not reference any variables. - out, err := s.Render("static content", "empty") + renderedTemplate, err := storeInstance.Render("static content", "empty") require.NoError(t, err) - assert.Equal(t, "static content", out) + assert.Equal(t, "static content", renderedTemplate) } func TestStore_Render_Bad_InvalidTemplateSyntax(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.Render("{{ .unclosed", "g") + _, err := storeInstance.Render("{{ .unclosed", "g") require.Error(t, err) assert.Contains(t, err.Error(), "store.Render: parse") } func TestStore_Render_Bad_MissingTemplateVar(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // text/template with a missing key on a map returns , not an error, // unless Option("missingkey=error") is set. The default behaviour is no error. - out, err := s.Render("hello {{ .missing }}", "g") + renderedTemplate, err := storeInstance.Render("hello {{ .missing }}", "g") require.NoError(t, err) - assert.Contains(t, out, "hello") + assert.Contains(t, renderedTemplate, "hello") } func TestStore_Render_Bad_ExecError(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _ = s.Set("g", "name", "hello") + _ = storeInstance.Set("g", "name", "hello") // Calling a string as a function triggers a template execution error. - _, err := s.Render(`{{ call .name }}`, "g") + _, err := storeInstance.Render(`{{ call .name }}`, "g") require.Error(t, err) assert.Contains(t, err.Error(), "store.Render: exec") } func TestStore_Render_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.Render("{{ .x }}", "g") + _, err := storeInstance.Render("{{ .x }}", "g") require.Error(t, err) } @@ -574,46 +574,46 @@ func TestStore_Render_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Close_Good(t *testing.T) { - s, _ := New(":memory:") - err := s.Close() + storeInstance, _ := New(":memory:") + err := storeInstance.Close() require.NoError(t, err) } func TestStore_Close_Good_OperationsFailAfterClose(t *testing.T) { - s, _ := New(":memory:") - require.NoError(t, s.Close()) + storeInstance, _ := New(":memory:") + require.NoError(t, storeInstance.Close()) // All operations on a closed store should fail. - _, err := s.Get("g", "k") + _, err := storeInstance.Get("g", "k") assert.Error(t, err, "Get on closed store should fail") - err = s.Set("g", "k", "v") + err = storeInstance.Set("g", "k", "v") assert.Error(t, err, "Set on closed store should fail") - err = s.Delete("g", "k") + err = storeInstance.Delete("g", "k") assert.Error(t, err, "Delete on closed store should fail") - _, err = s.Count("g") + _, err = storeInstance.Count("g") assert.Error(t, err, "Count on closed store should fail") - err = s.DeleteGroup("g") + err = storeInstance.DeleteGroup("g") assert.Error(t, err, "DeleteGroup on closed store should fail") - _, err = s.GetAll("g") + _, err = storeInstance.GetAll("g") assert.Error(t, err, "GetAll on closed store should fail") - _, err = s.Render("{{ .x }}", "g") + _, err = storeInstance.Render("{{ .x }}", "g") assert.Error(t, err, "Render on closed store should fail") } func TestStore_Close_Bad_DriverCloseError(t *testing.T) { - db := testCloseErrorDatabase(t) - s := &Store{ - database: db, + database := testCloseErrorDatabase(t) + storeInstance := &Store{ + database: database, cancelPurge: func() {}, } - err := s.Close() + err := storeInstance.Close() require.Error(t, err) assert.Contains(t, err.Error(), "store.Close") } @@ -714,8 +714,8 @@ func (testRowsAffectedErrorResult) RowsAffected() (int64, error) { // --------------------------------------------------------------------------- func TestStore_SetGet_Good_EdgeCases(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() tests := []struct { name string @@ -743,14 +743,14 @@ func TestStore_SetGet_Good_EdgeCases(t *testing.T) { {"long group", repeatString("g", 10000), "k", "val"}, } - for _, tc := range tests { - t.Run(tc.name, func(t *testing.T) { - err := s.Set(tc.group, tc.key, tc.value) + for _, testCase := range tests { + t.Run(testCase.name, func(t *testing.T) { + err := storeInstance.Set(testCase.group, testCase.key, testCase.value) require.NoError(t, err, "Set should succeed") - got, err := s.Get(tc.group, tc.key) + got, err := storeInstance.Get(testCase.group, testCase.key) require.NoError(t, err, "Get should succeed") - assert.Equal(t, tc.value, got, "round-trip should preserve value") + assert.Equal(t, testCase.value, got, "round-trip should preserve value") }) } } @@ -760,28 +760,28 @@ func TestStore_SetGet_Good_EdgeCases(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_GroupIsolation_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("alpha", "k", "a-val")) - require.NoError(t, s.Set("beta", "k", "b-val")) + require.NoError(t, storeInstance.Set("alpha", "k", "a-val")) + require.NoError(t, storeInstance.Set("beta", "k", "b-val")) - a, err := s.Get("alpha", "k") + alphaValue, err := storeInstance.Get("alpha", "k") require.NoError(t, err) - assert.Equal(t, "a-val", a) + assert.Equal(t, "a-val", alphaValue) - b, err := s.Get("beta", "k") + betaValue, err := storeInstance.Get("beta", "k") require.NoError(t, err) - assert.Equal(t, "b-val", b) + assert.Equal(t, "b-val", betaValue) // Delete from alpha should not affect beta. - require.NoError(t, s.Delete("alpha", "k")) - _, err = s.Get("alpha", "k") + require.NoError(t, storeInstance.Delete("alpha", "k")) + _, err = storeInstance.Get("alpha", "k") assert.Error(t, err) - b2, err := s.Get("beta", "k") + betaValueAfterDelete, err := storeInstance.Get("beta", "k") require.NoError(t, err) - assert.Equal(t, "b-val", b2) + assert.Equal(t, "b-val", betaValueAfterDelete) } // --------------------------------------------------------------------------- @@ -790,27 +790,27 @@ func TestStore_GroupIsolation_Good(t *testing.T) { func TestStore_Concurrent_Good_ReadWrite(t *testing.T) { dbPath := testPath(t, "concurrent.db") - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() const goroutines = 10 const opsPerGoroutine = 100 - var wg sync.WaitGroup - errs := make(chan error, goroutines*opsPerGoroutine*2) + var waitGroup sync.WaitGroup + recordedErrors := make(chan error, goroutines*opsPerGoroutine*2) // Writers. for g := range goroutines { - wg.Add(1) + waitGroup.Add(1) go func(id int) { - defer wg.Done() + defer waitGroup.Done() group := core.Sprintf("grp-%d", id) for i := range opsPerGoroutine { key := core.Sprintf("key-%d", i) - val := core.Sprintf("val-%d-%d", id, i) - if err := s.Set(group, key, val); err != nil { - errs <- core.E("TestStore_Concurrent_Good_ReadWrite", core.Sprintf("writer %d", id), err) + value := core.Sprintf("val-%d-%d", id, i) + if err := storeInstance.Set(group, key, value); err != nil { + recordedErrors <- core.E("TestStore_Concurrent_Good_ReadWrite", core.Sprintf("writer %d", id), err) } } }(g) @@ -818,51 +818,51 @@ func TestStore_Concurrent_Good_ReadWrite(t *testing.T) { // Readers — start immediately alongside writers. for g := range goroutines { - wg.Add(1) + waitGroup.Add(1) go func(id int) { - defer wg.Done() + defer waitGroup.Done() group := core.Sprintf("grp-%d", id) for i := range opsPerGoroutine { key := core.Sprintf("key-%d", i) - _, err := s.Get(group, key) + _, err := storeInstance.Get(group, key) // NotFoundError is acceptable — the writer may not have written yet. if err != nil && !core.Is(err, NotFoundError) { - errs <- core.E("TestStore_Concurrent_Good_ReadWrite", core.Sprintf("reader %d", id), err) + recordedErrors <- core.E("TestStore_Concurrent_Good_ReadWrite", core.Sprintf("reader %d", id), err) } } }(g) } - wg.Wait() - close(errs) + waitGroup.Wait() + close(recordedErrors) - for e := range errs { - t.Error(e) + for recordedError := range recordedErrors { + t.Error(recordedError) } // After all writers finish, every key should be present. for g := range goroutines { group := core.Sprintf("grp-%d", g) - n, err := s.Count(group) + count, err := storeInstance.Count(group) require.NoError(t, err) - assert.Equal(t, opsPerGoroutine, n, "group %s should have all keys", group) + assert.Equal(t, opsPerGoroutine, count, "group %s should have all keys", group) } } func TestStore_Concurrent_Good_GetAll(t *testing.T) { - s, err := New(testPath(t, "getall.db")) + storeInstance, err := New(testPath(t, "getall.db")) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() // Seed data. for i := range 50 { - require.NoError(t, s.Set("shared", core.Sprintf("k%d", i), core.Sprintf("v%d", i))) + require.NoError(t, storeInstance.Set("shared", core.Sprintf("k%d", i), core.Sprintf("v%d", i))) } - var wg sync.WaitGroup + var waitGroup sync.WaitGroup for range 10 { - wg.Go(func() { - all, err := s.GetAll("shared") + waitGroup.Go(func() { + all, err := storeInstance.GetAll("shared") if err != nil { t.Errorf("GetAll failed: %v", err) return @@ -872,27 +872,27 @@ func TestStore_Concurrent_Good_GetAll(t *testing.T) { } }) } - wg.Wait() + waitGroup.Wait() } func TestStore_Concurrent_Good_DeleteGroup(t *testing.T) { - s, err := New(testPath(t, "delgrp.db")) + storeInstance, err := New(testPath(t, "delgrp.db")) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() - var wg sync.WaitGroup + var waitGroup sync.WaitGroup for g := range 10 { - wg.Add(1) + waitGroup.Add(1) go func(id int) { - defer wg.Done() - grp := core.Sprintf("g%d", id) + defer waitGroup.Done() + groupName := core.Sprintf("g%d", id) for i := range 20 { - _ = s.Set(grp, core.Sprintf("k%d", i), "v") + _ = storeInstance.Set(groupName, core.Sprintf("k%d", i), "v") } - _ = s.DeleteGroup(grp) + _ = storeInstance.DeleteGroup(groupName) }(g) } - wg.Wait() + waitGroup.Wait() } // --------------------------------------------------------------------------- @@ -900,10 +900,10 @@ func TestStore_Concurrent_Good_DeleteGroup(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_NotFoundError_Good_Is(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - _, err := s.Get("g", "k") + _, err := storeInstance.Get("g", "k") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError), "error should be NotFoundError via core.Is") assert.Contains(t, err.Error(), "g/k", "error message should include group/key") @@ -913,55 +913,55 @@ func TestStore_NotFoundError_Good_Is(t *testing.T) { // Benchmarks // --------------------------------------------------------------------------- -func BenchmarkSet(b *testing.B) { - s, _ := New(":memory:") - defer s.Close() +func BenchmarkSet(benchmark *testing.B) { + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - b.ResetTimer() - for i := range b.N { - _ = s.Set("bench", core.Sprintf("key-%d", i), "value") + benchmark.ResetTimer() + for i := range benchmark.N { + _ = storeInstance.Set("bench", core.Sprintf("key-%d", i), "value") } } -func BenchmarkGet(b *testing.B) { - s, _ := New(":memory:") - defer s.Close() +func BenchmarkGet(benchmark *testing.B) { + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Pre-populate. const keys = 10000 for i := range keys { - _ = s.Set("bench", core.Sprintf("key-%d", i), "value") + _ = storeInstance.Set("bench", core.Sprintf("key-%d", i), "value") } - b.ResetTimer() - for i := range b.N { - _, _ = s.Get("bench", core.Sprintf("key-%d", i%keys)) + benchmark.ResetTimer() + for i := range benchmark.N { + _, _ = storeInstance.Get("bench", core.Sprintf("key-%d", i%keys)) } } -func BenchmarkGetAll(b *testing.B) { - s, _ := New(":memory:") - defer s.Close() +func BenchmarkGetAll(benchmark *testing.B) { + storeInstance, _ := New(":memory:") + defer storeInstance.Close() const keys = 10000 for i := range keys { - _ = s.Set("bench", core.Sprintf("key-%d", i), "value") + _ = storeInstance.Set("bench", core.Sprintf("key-%d", i), "value") } - b.ResetTimer() - for range b.N { - _, _ = s.GetAll("bench") + benchmark.ResetTimer() + for range benchmark.N { + _, _ = storeInstance.GetAll("bench") } } -func BenchmarkSet_FileBacked(b *testing.B) { - dbPath := testPath(b, "bench.db") - s, _ := New(dbPath) - defer s.Close() +func BenchmarkSet_FileBacked(benchmark *testing.B) { + dbPath := testPath(benchmark, "bench.db") + storeInstance, _ := New(dbPath) + defer storeInstance.Close() - b.ResetTimer() - for i := range b.N { - _ = s.Set("bench", core.Sprintf("key-%d", i), "value") + benchmark.ResetTimer() + for i := range benchmark.N { + _ = storeInstance.Set("bench", core.Sprintf("key-%d", i), "value") } } @@ -970,121 +970,121 @@ func BenchmarkSet_FileBacked(b *testing.B) { // --------------------------------------------------------------------------- func TestStore_SetWithTTL_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - err := s.SetWithTTL("g", "k", "v", 5*time.Second) + err := storeInstance.SetWithTTL("g", "k", "v", 5*time.Second) require.NoError(t, err) - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) } func TestStore_SetWithTTL_Good_Upsert(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.SetWithTTL("g", "k", "v1", time.Hour)) - require.NoError(t, s.SetWithTTL("g", "k", "v2", time.Hour)) + require.NoError(t, storeInstance.SetWithTTL("g", "k", "v1", time.Hour)) + require.NoError(t, storeInstance.SetWithTTL("g", "k", "v2", time.Hour)) - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v2", val, "upsert should overwrite the value") + assert.Equal(t, "v2", value, "upsert should overwrite the value") - n, err := s.Count("g") + count, err := storeInstance.Count("g") require.NoError(t, err) - assert.Equal(t, 1, n, "upsert should not duplicate keys") + assert.Equal(t, 1, count, "upsert should not duplicate keys") } func TestStore_SetWithTTL_Good_ExpiresOnGet(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Set a key with a very short TTL. - require.NoError(t, s.SetWithTTL("g", "ephemeral", "gone-soon", 1*time.Millisecond)) + require.NoError(t, storeInstance.SetWithTTL("g", "ephemeral", "gone-soon", 1*time.Millisecond)) // Wait for it to expire. time.Sleep(5 * time.Millisecond) - _, err := s.Get("g", "ephemeral") + _, err := storeInstance.Get("g", "ephemeral") require.Error(t, err) assert.True(t, core.Is(err, NotFoundError), "expired key should be NotFoundError") } func TestStore_SetWithTTL_Good_ExcludedFromCount(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "permanent", "stays")) - require.NoError(t, s.SetWithTTL("g", "temp", "goes", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "permanent", "stays")) + require.NoError(t, storeInstance.SetWithTTL("g", "temp", "goes", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - n, err := s.Count("g") + count, err := storeInstance.Count("g") require.NoError(t, err) - assert.Equal(t, 1, n, "expired key should not be counted") + assert.Equal(t, 1, count, "expired key should not be counted") } func TestStore_SetWithTTL_Good_ExcludedFromGetAll(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "a", "1")) - require.NoError(t, s.SetWithTTL("g", "b", "2", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "a", "1")) + require.NoError(t, storeInstance.SetWithTTL("g", "b", "2", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - all, err := s.GetAll("g") + all, err := storeInstance.GetAll("g") require.NoError(t, err) assert.Equal(t, map[string]string{"a": "1"}, all, "expired key should be excluded") } func TestStore_SetWithTTL_Good_ExcludedFromRender(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "name", "Alice")) - require.NoError(t, s.SetWithTTL("g", "temp", "gone", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "name", "Alice")) + require.NoError(t, storeInstance.SetWithTTL("g", "temp", "gone", 1*time.Millisecond)) time.Sleep(5 * time.Millisecond) - out, err := s.Render("Hello {{ .name }}", "g") + renderedTemplate, err := storeInstance.Render("Hello {{ .name }}", "g") require.NoError(t, err) - assert.Equal(t, "Hello Alice", out) + assert.Equal(t, "Hello Alice", renderedTemplate) } func TestStore_SetWithTTL_Good_SetClearsTTL(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() // Set with TTL, then overwrite with plain Set — TTL should be cleared. - require.NoError(t, s.SetWithTTL("g", "k", "temp", 1*time.Millisecond)) - require.NoError(t, s.Set("g", "k", "permanent")) + require.NoError(t, storeInstance.SetWithTTL("g", "k", "temp", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "k", "permanent")) time.Sleep(5 * time.Millisecond) - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "permanent", val, "plain Set should clear TTL") + assert.Equal(t, "permanent", value, "plain Set should clear TTL") } func TestStore_SetWithTTL_Good_FutureTTLAccessible(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.SetWithTTL("g", "k", "v", 1*time.Hour)) + require.NoError(t, storeInstance.SetWithTTL("g", "k", "v", 1*time.Hour)) - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val, "far-future TTL should be accessible") + assert.Equal(t, "v", value, "far-future TTL should be accessible") - n, err := s.Count("g") + count, err := storeInstance.Count("g") require.NoError(t, err) - assert.Equal(t, 1, n) + assert.Equal(t, 1, count) } func TestStore_SetWithTTL_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - err := s.SetWithTTL("g", "k", "v", time.Hour) + err := storeInstance.SetWithTTL("g", "k", "v", time.Hour) require.Error(t, err) } @@ -1093,77 +1093,77 @@ func TestStore_SetWithTTL_Bad_ClosedStore(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_PurgeExpired_Good(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.SetWithTTL("g", "a", "1", 1*time.Millisecond)) - require.NoError(t, s.SetWithTTL("g", "b", "2", 1*time.Millisecond)) - require.NoError(t, s.Set("g", "c", "3")) + require.NoError(t, storeInstance.SetWithTTL("g", "a", "1", 1*time.Millisecond)) + require.NoError(t, storeInstance.SetWithTTL("g", "b", "2", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "c", "3")) time.Sleep(5 * time.Millisecond) - removed, err := s.PurgeExpired() + removed, err := storeInstance.PurgeExpired() require.NoError(t, err) assert.Equal(t, int64(2), removed, "should purge 2 expired keys") - n, err := s.Count("g") + count, err := storeInstance.Count("g") require.NoError(t, err) - assert.Equal(t, 1, n, "only non-expiring key should remain") + assert.Equal(t, 1, count, "only non-expiring key should remain") } func TestStore_PurgeExpired_Good_NoneExpired(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - require.NoError(t, s.Set("g", "a", "1")) - require.NoError(t, s.SetWithTTL("g", "b", "2", time.Hour)) + require.NoError(t, storeInstance.Set("g", "a", "1")) + require.NoError(t, storeInstance.SetWithTTL("g", "b", "2", time.Hour)) - removed, err := s.PurgeExpired() + removed, err := storeInstance.PurgeExpired() require.NoError(t, err) assert.Equal(t, int64(0), removed) } func TestStore_PurgeExpired_Good_Empty(t *testing.T) { - s, _ := New(":memory:") - defer s.Close() + storeInstance, _ := New(":memory:") + defer storeInstance.Close() - removed, err := s.PurgeExpired() + removed, err := storeInstance.PurgeExpired() require.NoError(t, err) assert.Equal(t, int64(0), removed) } func TestStore_PurgeExpired_Bad_ClosedStore(t *testing.T) { - s, _ := New(":memory:") - s.Close() + storeInstance, _ := New(":memory:") + storeInstance.Close() - _, err := s.PurgeExpired() + _, err := storeInstance.PurgeExpired() require.Error(t, err) } func TestStore_PurgeExpired_Bad_RowsAffectedError(t *testing.T) { - db := testRowsAffectedErrorDatabase(t) - s := &Store{ - database: db, + database := testRowsAffectedErrorDatabase(t) + storeInstance := &Store{ + database: database, cancelPurge: func() {}, } - _, err := s.PurgeExpired() + _, err := storeInstance.PurgeExpired() require.Error(t, err) assert.Contains(t, err.Error(), "store.PurgeExpired") } func TestStore_PurgeExpired_Good_BackgroundPurge(t *testing.T) { - s, _ := New(":memory:") + storeInstance, _ := New(":memory:") // Override purge interval for testing: restart the goroutine with a short interval. - s.cancelPurge() - s.purgeWaitGroup.Wait() - s.purgeInterval = 20 * time.Millisecond + storeInstance.cancelPurge() + storeInstance.purgeWaitGroup.Wait() + storeInstance.purgeInterval = 20 * time.Millisecond ctx, cancel := context.WithCancel(context.Background()) - s.cancelPurge = cancel - s.startBackgroundPurge(ctx) - defer s.Close() + storeInstance.cancelPurge = cancel + storeInstance.startBackgroundPurge(ctx) + defer storeInstance.Close() - require.NoError(t, s.SetWithTTL("g", "ephemeral", "v", 1*time.Millisecond)) - require.NoError(t, s.Set("g", "permanent", "stays")) + require.NoError(t, storeInstance.SetWithTTL("g", "ephemeral", "v", 1*time.Millisecond)) + require.NoError(t, storeInstance.Set("g", "permanent", "stays")) // Wait for the background purge to fire. time.Sleep(60 * time.Millisecond) @@ -1171,7 +1171,7 @@ func TestStore_PurgeExpired_Good_BackgroundPurge(t *testing.T) { // The expired key should have been removed by the background goroutine. // Use a raw query to check the row is actually gone (not just filtered by Get). var count int - err := s.database.QueryRow("SELECT COUNT(*) FROM entries WHERE group_name = ?", "g").Scan(&count) + err := storeInstance.database.QueryRow("SELECT COUNT(*) FROM entries WHERE group_name = ?", "g").Scan(&count) require.NoError(t, err) assert.Equal(t, 1, count, "background purge should have deleted the expired row") } @@ -1184,67 +1184,67 @@ func TestStore_SchemaUpgrade_Good_ExistingDB(t *testing.T) { dbPath := testPath(t, "upgrade.db") // Open, write, close. - s1, err := New(dbPath) + initialStore, err := New(dbPath) require.NoError(t, err) - require.NoError(t, s1.Set("g", "k", "v")) - require.NoError(t, s1.Close()) + require.NoError(t, initialStore.Set("g", "k", "v")) + require.NoError(t, initialStore.Close()) // Reopen — the ALTER TABLE ADD COLUMN should be a no-op. - s2, err := New(dbPath) + reopenedStore, err := New(dbPath) require.NoError(t, err) - defer s2.Close() + defer reopenedStore.Close() - val, err := s2.Get("g", "k") + value, err := reopenedStore.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) // TTL features should work on the reopened store. - require.NoError(t, s2.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) - val2, err := s2.Get("g", "ttl-key") + require.NoError(t, reopenedStore.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) + secondValue, err := reopenedStore.Get("g", "ttl-key") require.NoError(t, err) - assert.Equal(t, "ttl-val", val2) + assert.Equal(t, "ttl-val", secondValue) } func TestStore_SchemaUpgrade_Good_EntriesWithoutExpiryColumn(t *testing.T) { dbPath := testPath(t, "entries-no-expiry.db") - db, err := sql.Open("sqlite", dbPath) + database, err := sql.Open("sqlite", dbPath) require.NoError(t, err) - db.SetMaxOpenConns(1) - _, err = db.Exec("PRAGMA journal_mode=WAL") + database.SetMaxOpenConns(1) + _, err = database.Exec("PRAGMA journal_mode=WAL") require.NoError(t, err) - _, err = db.Exec(`CREATE TABLE entries ( + _, err = database.Exec(`CREATE TABLE entries ( group_name TEXT NOT NULL, entry_key TEXT NOT NULL, entry_value TEXT NOT NULL, PRIMARY KEY (group_name, entry_key) )`) require.NoError(t, err) - _, err = db.Exec("INSERT INTO entries (group_name, entry_key, entry_value) VALUES ('g', 'k', 'v')") + _, err = database.Exec("INSERT INTO entries (group_name, entry_key, entry_value) VALUES ('g', 'k', 'v')") require.NoError(t, err) - require.NoError(t, db.Close()) + require.NoError(t, database.Close()) - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) - require.NoError(t, s.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) - val2, err := s.Get("g", "ttl-key") + require.NoError(t, storeInstance.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) + secondValue, err := storeInstance.Get("g", "ttl-key") require.NoError(t, err) - assert.Equal(t, "ttl-val", val2) + assert.Equal(t, "ttl-val", secondValue) } func TestStore_SchemaUpgrade_Good_LegacyAndCurrentTables(t *testing.T) { dbPath := testPath(t, "entries-and-legacy.db") - db, err := sql.Open("sqlite", dbPath) + database, err := sql.Open("sqlite", dbPath) require.NoError(t, err) - db.SetMaxOpenConns(1) - _, err = db.Exec("PRAGMA journal_mode=WAL") + database.SetMaxOpenConns(1) + _, err = database.Exec("PRAGMA journal_mode=WAL") require.NoError(t, err) - _, err = db.Exec(`CREATE TABLE entries ( + _, err = database.Exec(`CREATE TABLE entries ( group_name TEXT NOT NULL, entry_key TEXT NOT NULL, entry_value TEXT NOT NULL, @@ -1252,28 +1252,28 @@ func TestStore_SchemaUpgrade_Good_LegacyAndCurrentTables(t *testing.T) { PRIMARY KEY (group_name, entry_key) )`) require.NoError(t, err) - _, err = db.Exec("INSERT INTO entries (group_name, entry_key, entry_value) VALUES ('existing', 'k', 'v')") + _, err = database.Exec("INSERT INTO entries (group_name, entry_key, entry_value) VALUES ('existing', 'k', 'v')") require.NoError(t, err) - _, err = db.Exec(`CREATE TABLE kv ( + _, err = database.Exec(`CREATE TABLE kv ( grp TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (grp, key) )`) require.NoError(t, err) - _, err = db.Exec("INSERT INTO kv (grp, key, value) VALUES ('legacy', 'k', 'legacy-v')") + _, err = database.Exec("INSERT INTO kv (grp, key, value) VALUES ('legacy', 'k', 'legacy-v')") require.NoError(t, err) - require.NoError(t, db.Close()) + require.NoError(t, database.Close()) - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() - val, err := s.Get("existing", "k") + value, err := storeInstance.Get("existing", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) - legacyVal, err := s.Get("legacy", "k") + legacyVal, err := storeInstance.Get("legacy", "k") require.NoError(t, err) assert.Equal(t, "legacy-v", legacyVal) } @@ -1282,37 +1282,37 @@ 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") - db, err := sql.Open("sqlite", dbPath) + database, err := sql.Open("sqlite", dbPath) require.NoError(t, err) - db.SetMaxOpenConns(1) - _, err = db.Exec("PRAGMA journal_mode=WAL") + database.SetMaxOpenConns(1) + _, err = database.Exec("PRAGMA journal_mode=WAL") require.NoError(t, err) - _, err = db.Exec(`CREATE TABLE kv ( + _, err = database.Exec(`CREATE TABLE kv ( grp TEXT NOT NULL, key TEXT NOT NULL, value TEXT NOT NULL, PRIMARY KEY (grp, key) )`) require.NoError(t, err) - _, err = db.Exec("INSERT INTO kv (grp, key, value) VALUES ('g', 'k', 'v')") + _, err = database.Exec("INSERT INTO kv (grp, key, value) VALUES ('g', 'k', 'v')") require.NoError(t, err) - require.NoError(t, db.Close()) + require.NoError(t, database.Close()) // Open with New — should migrate the legacy table into the descriptive schema. - s, err := New(dbPath) + storeInstance, err := New(dbPath) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() // Existing data should be readable. - val, err := s.Get("g", "k") + value, err := storeInstance.Get("g", "k") require.NoError(t, err) - assert.Equal(t, "v", val) + assert.Equal(t, "v", value) // TTL features should work after migration. - require.NoError(t, s.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) - val2, err := s.Get("g", "ttl-key") + require.NoError(t, storeInstance.SetWithTTL("g", "ttl-key", "ttl-val", time.Hour)) + secondValue, err := storeInstance.Get("g", "ttl-key") require.NoError(t, err) - assert.Equal(t, "ttl-val", val2) + assert.Equal(t, "ttl-val", secondValue) } // --------------------------------------------------------------------------- @@ -1320,38 +1320,38 @@ func TestStore_SchemaUpgrade_Good_PreTTLDatabase(t *testing.T) { // --------------------------------------------------------------------------- func TestStore_Concurrent_Good_TTL(t *testing.T) { - s, err := New(testPath(t, "concurrent-ttl.db")) + storeInstance, err := New(testPath(t, "concurrent-ttl.db")) require.NoError(t, err) - defer s.Close() + defer storeInstance.Close() const goroutines = 10 const ops = 50 - var wg sync.WaitGroup + var waitGroup sync.WaitGroup for g := range goroutines { - wg.Add(1) + waitGroup.Add(1) go func(id int) { - defer wg.Done() - grp := core.Sprintf("ttl-%d", id) + defer waitGroup.Done() + groupName := core.Sprintf("ttl-%d", id) for i := range ops { key := core.Sprintf("k%d", i) if i%2 == 0 { - _ = s.SetWithTTL(grp, key, "v", 50*time.Millisecond) + _ = storeInstance.SetWithTTL(groupName, key, "v", 50*time.Millisecond) } else { - _ = s.Set(grp, key, "v") + _ = storeInstance.Set(groupName, key, "v") } } }(g) } - wg.Wait() + waitGroup.Wait() // Give expired keys time to lapse. time.Sleep(60 * time.Millisecond) for g := range goroutines { - grp := core.Sprintf("ttl-%d", g) - n, err := s.Count(grp) + groupName := core.Sprintf("ttl-%d", g) + count, err := storeInstance.Count(groupName) require.NoError(t, err) - assert.Equal(t, ops/2, n, "only non-TTL keys should remain in %s", grp) + assert.Equal(t, ops/2, count, "only non-TTL keys should remain in %s", groupName) } }