package ezdb import ( "fmt" "math/rand" "slices" "sort" ) // CollectionTester is a convenience type to help build out collection testing with less boilerplate. type CollectionTester[T any] struct { C Collection[T] Cmp func(a, b T) error // Comparison function to assert equivalence between two documents. Data map[string]T // Test data. } func (t *CollectionTester[T]) DeleteAll() error { if err := t.C.DeleteAll(); err != nil { return fmt.Errorf("failed to delete all documents: %v", err) } return nil } // DeleteOne tests that the collection safely deletes a single document. // The document is not reinserted after deletion. func (t *CollectionTester[T]) DeleteOne() error { // Select a random key. // This selects from the collection rather than test data to allow the function to be reused, so long as the collection has one document keys := t.C.GetAllKeys() if len(keys) == 0 { return fmt.Errorf("no documents") } n := rand.Intn(len(keys) - 1) key := keys[n] if err := t.C.Delete(key); err != nil { return fmt.Errorf("failed to delete document '%s': %v", key, err) } // Confirm document has been deleted has, err := t.C.Has(key) if err != nil { return fmt.Errorf("failed to test whether collection has deleted document '%s': %v", key, err) } else if has { return fmt.Errorf("expected collection not to have deleted document '%s'", key) } return nil } // Get tests that the collection retrieves all documents. // The collection must be initialised. func (t *CollectionTester[T]) Get() error { errs := Errors{} for key, data := range t.Data { actual, err := t.C.Get(key) if err != nil { errs = append(errs, fmt.Errorf("failed to get document '%s': %v", key, err)) } else if err := t.Cmp(data, actual); err != nil { errs = append(errs, fmt.Errorf("comparison failed for document '%s': %v", key, err)) } } return errs.Resolve() } func (t *CollectionTester[T]) GetAll() error { all, err := t.C.GetAll() if err != nil { return fmt.Errorf("failed to get all documents: %v", err) } errs := Errors{} for key, actual := range all { expected := t.Data[key] if err := t.Cmp(expected, actual); err != nil { errs = append(errs, fmt.Errorf("comparison failed for document '%s': %v", key, err)) } } return errs.Resolve() } // Has tests that the collection has all documents. // The collection must be initialised. func (t *CollectionTester[T]) Has() error { errs := Errors{} for key := range t.Data { has, err := t.C.Has(key) if err != nil { return fmt.Errorf("failed to test whether collection has document '%s': %v", key, err) } else if !has { errs = append(errs, fmt.Errorf("expected collection to have document '%s'", key)) } } return errs.Resolve() } // Init tests that the collection is correctly initialised. // This opens the collection, deletes any existing data, and reinserts the test data. func (t *CollectionTester[T]) Init() error { fs := []func() error{t.Open, t.DeleteAll, t.Put} for _, f := range fs { if err := f(); err != nil { return err } } return nil } // IterCount tests that the iterator counts documents correctly. // The collection must be initialised. func (t *CollectionTester[T]) IterCount() error { iter := t.C.Iter() actual := iter.Count() iter.Release() expected := len(t.Data) if expected != actual { return fmt.Errorf("incorrect document count (expected %d, got %d)", expected, actual) } return nil } // IterSortKeys tests that the iterator sorts correctly by key. // The collection must be initialised. func (t *CollectionTester[T]) IterSortKeys() error { ks := &keySort{ a: []string{}, f: func(a, b string) bool { return a < b }, } for key := range t.Data { ks.a = append(ks.a, key) } sort.Stable(ks) sortedKeys := ks.Result() iter := t.C.Iter().SortKeys(ks.f) defer iter.Release() errs := Errors{} actual := 0 for iter.Next() { key := iter.Key() if key != sortedKeys[actual] { expected := slices.Index(sortedKeys, key) errs = append(errs, fmt.Errorf("incorrect sort position of key '%s' (expected %d, got %d)", key, expected, actual)) } actual++ } return errs.Resolve() } // Open tests that the collection is opened. func (t *CollectionTester[T]) Open() error { if err := t.C.Open(); err != nil { return fmt.Errorf("failed to open collection: %v", err) } return nil } // Put tests that the collection can store all documents. func (t *CollectionTester[T]) Put() error { errs := Errors{} for key, data := range t.Data { if err := t.C.Put(key, data); err != nil { errs = append(errs, fmt.Errorf("failed to put document '%s': %v", key, err)) } } return errs.Resolve() } func (t *CollectionTester[T]) Close() error { if err := t.C.Close(); err != nil { return fmt.Errorf("failed to close collection: %v", err) } return nil }