refactored Record.data and Record.expand to be concurrent safe
This commit is contained in:
+43
-2
@@ -8,9 +8,36 @@ type Store[T any] struct {
|
||||
data map[string]T
|
||||
}
|
||||
|
||||
// New creates a new Store[T] instance.
|
||||
// New creates a new Store[T] instance with a shallow copy of the provided data (if any).
|
||||
func New[T any](data map[string]T) *Store[T] {
|
||||
return &Store[T]{data: data}
|
||||
s := &Store[T]{}
|
||||
|
||||
s.Reset(data)
|
||||
|
||||
return s
|
||||
}
|
||||
|
||||
// Reset clears the store and replaces the store data with a
|
||||
// shallow copy of the provided newData.
|
||||
func (s *Store[T]) Reset(newData map[string]T) {
|
||||
s.mux.Lock()
|
||||
defer s.mux.Unlock()
|
||||
|
||||
var clone = make(map[string]T, len(newData))
|
||||
|
||||
for k, v := range newData {
|
||||
clone[k] = v
|
||||
}
|
||||
|
||||
s.data = clone
|
||||
}
|
||||
|
||||
// Length returns the current number of elements in the store.
|
||||
func (s *Store[T]) Length() int {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
|
||||
return len(s.data)
|
||||
}
|
||||
|
||||
// RemoveAll removes all the existing store entries.
|
||||
@@ -51,6 +78,20 @@ func (s *Store[T]) Get(key string) T {
|
||||
return s.data[key]
|
||||
}
|
||||
|
||||
// GetAll returns a shallow copy of the current store data.
|
||||
func (s *Store[T]) GetAll() map[string]T {
|
||||
s.mux.RLock()
|
||||
defer s.mux.RUnlock()
|
||||
|
||||
var clone = make(map[string]T, len(s.data))
|
||||
|
||||
for k, v := range s.data {
|
||||
clone[k] = v
|
||||
}
|
||||
|
||||
return clone
|
||||
}
|
||||
|
||||
// Set sets (or overwrite if already exist) a new value for key.
|
||||
func (s *Store[T]) Set(key string, value T) {
|
||||
s.mux.Lock()
|
||||
|
||||
@@ -1,16 +1,80 @@
|
||||
package store_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/pocketbase/pocketbase/tools/store"
|
||||
)
|
||||
|
||||
func TestNew(t *testing.T) {
|
||||
s := store.New(map[string]int{"test": 1})
|
||||
data := map[string]int{"test1": 1, "test2": 2}
|
||||
originalRawData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
if s.Get("test") != 1 {
|
||||
t.Error("Expected the initizialized store map to be loaded")
|
||||
s := store.New(data)
|
||||
s.Set("test3", 3) // add 1 item
|
||||
s.Remove("test1") // remove 1 item
|
||||
|
||||
// check if data was shallow copied
|
||||
rawData, _ := json.Marshal(data)
|
||||
if !bytes.Equal(originalRawData, rawData) {
|
||||
t.Fatalf("Expected data \n%s, \ngot \n%s", originalRawData, rawData)
|
||||
}
|
||||
|
||||
if s.Has("test1") {
|
||||
t.Fatalf("Expected test1 to be deleted, got %v", s.Get("test1"))
|
||||
}
|
||||
|
||||
if v := s.Get("test2"); v != 2 {
|
||||
t.Fatalf("Expected test2 to be %v, got %v", 2, v)
|
||||
}
|
||||
|
||||
if v := s.Get("test3"); v != 3 {
|
||||
t.Fatalf("Expected test3 to be %v, got %v", 3, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestReset(t *testing.T) {
|
||||
s := store.New(map[string]int{"test1": 1})
|
||||
|
||||
data := map[string]int{"test2": 2}
|
||||
originalRawData, err := json.Marshal(data)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
|
||||
s.Reset(data)
|
||||
s.Set("test3", 3)
|
||||
|
||||
// check if data was shallow copied
|
||||
rawData, _ := json.Marshal(data)
|
||||
if !bytes.Equal(originalRawData, rawData) {
|
||||
t.Fatalf("Expected data \n%s, \ngot \n%s", originalRawData, rawData)
|
||||
}
|
||||
|
||||
if s.Has("test1") {
|
||||
t.Fatalf("Expected test1 to be deleted, got %v", s.Get("test1"))
|
||||
}
|
||||
|
||||
if v := s.Get("test2"); v != 2 {
|
||||
t.Fatalf("Expected test2 to be %v, got %v", 2, v)
|
||||
}
|
||||
|
||||
if v := s.Get("test3"); v != 3 {
|
||||
t.Fatalf("Expected test3 to be %v, got %v", 3, v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLength(t *testing.T) {
|
||||
s := store.New(map[string]int{"test1": 1})
|
||||
s.Set("test2", 2)
|
||||
|
||||
if v := s.Length(); v != 2 {
|
||||
t.Fatalf("Expected length %d, got %d", 2, v)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,8 +145,36 @@ func TestGet(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetAll(t *testing.T) {
|
||||
data := map[string]int{
|
||||
"a": 1,
|
||||
"b": 2,
|
||||
}
|
||||
|
||||
s := store.New(data)
|
||||
|
||||
// fetch and delete each key to make sure that it was shallow copied
|
||||
result := s.GetAll()
|
||||
for k := range result {
|
||||
delete(result, k)
|
||||
}
|
||||
|
||||
// refetch again
|
||||
result = s.GetAll()
|
||||
|
||||
if len(result) != len(data) {
|
||||
t.Fatalf("Expected %d, got %d items", len(data), len(result))
|
||||
}
|
||||
|
||||
for k := range result {
|
||||
if result[k] != data[k] {
|
||||
t.Fatalf("Expected %s to be %v, got %v", k, data[k], result[k])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSet(t *testing.T) {
|
||||
s := store.New[int](nil)
|
||||
s := store.Store[int]{}
|
||||
|
||||
data := map[string]int{"test1": 0, "test2": 1, "test3": 3}
|
||||
|
||||
@@ -105,7 +197,7 @@ func TestSet(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSetIfLessThanLimit(t *testing.T) {
|
||||
s := store.New[int](nil)
|
||||
s := store.Store[int]{}
|
||||
|
||||
limit := 2
|
||||
|
||||
|
||||
Reference in New Issue
Block a user