collection_tester.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. package ezdb
  2. import (
  3. "fmt"
  4. "math/rand"
  5. "slices"
  6. "sort"
  7. )
  8. // CollectionTester is a convenience type to help build out collection testing with less boilerplate.
  9. type CollectionTester[T any] struct {
  10. C Collection[T]
  11. Cmp func(a, b T) error // Comparison function to assert equivalence between two documents.
  12. Data map[string]T // Test data.
  13. }
  14. func (t *CollectionTester[T]) DeleteAll() error {
  15. if err := t.C.DeleteAll(); err != nil {
  16. return fmt.Errorf("failed to delete all documents: %v", err)
  17. }
  18. return nil
  19. }
  20. // DeleteOne tests that the collection safely deletes a single document.
  21. // The document is not reinserted after deletion.
  22. func (t *CollectionTester[T]) DeleteOne() error {
  23. // Select a random key.
  24. // This selects from the collection rather than test data to allow the function to be reused, so long as the collection has one document
  25. keys := t.C.GetAllKeys()
  26. if len(keys) == 0 {
  27. return fmt.Errorf("no documents")
  28. }
  29. n := rand.Intn(len(keys) - 1)
  30. key := keys[n]
  31. if err := t.C.Delete(key); err != nil {
  32. return fmt.Errorf("failed to delete document '%s': %v", key, err)
  33. }
  34. // Confirm document has been deleted
  35. has, err := t.C.Has(key)
  36. if err != nil {
  37. return fmt.Errorf("failed to test whether collection has deleted document '%s': %v", key, err)
  38. } else if has {
  39. return fmt.Errorf("expected collection not to have deleted document '%s'", key)
  40. }
  41. return nil
  42. }
  43. // Get tests that the collection retrieves all documents.
  44. // The collection must be initialised.
  45. func (t *CollectionTester[T]) Get() error {
  46. errs := Errors{}
  47. for key, data := range t.Data {
  48. actual, err := t.C.Get(key)
  49. if err != nil {
  50. errs = append(errs, fmt.Errorf("failed to get document '%s': %v", key, err))
  51. } else if err := t.Cmp(data, actual); err != nil {
  52. errs = append(errs, fmt.Errorf("comparison failed for document '%s': %v", key, err))
  53. }
  54. }
  55. return errs.Resolve()
  56. }
  57. func (t *CollectionTester[T]) GetAll() error {
  58. all, err := t.C.GetAll()
  59. if err != nil {
  60. return fmt.Errorf("failed to get all documents: %v", err)
  61. }
  62. errs := Errors{}
  63. for key, actual := range all {
  64. expected := t.Data[key]
  65. if err := t.Cmp(expected, actual); err != nil {
  66. errs = append(errs, fmt.Errorf("comparison failed for document '%s': %v", key, err))
  67. }
  68. }
  69. return errs.Resolve()
  70. }
  71. // Has tests that the collection has all documents.
  72. // The collection must be initialised.
  73. func (t *CollectionTester[T]) Has() error {
  74. errs := Errors{}
  75. for key := range t.Data {
  76. has, err := t.C.Has(key)
  77. if err != nil {
  78. return fmt.Errorf("failed to test whether collection has document '%s': %v", key, err)
  79. } else if !has {
  80. errs = append(errs, fmt.Errorf("expected collection to have document '%s'", key))
  81. }
  82. }
  83. return errs.Resolve()
  84. }
  85. // Init tests that the collection is correctly initialised.
  86. // This opens the collection, deletes any existing data, and reinserts the test data.
  87. func (t *CollectionTester[T]) Init() error {
  88. fs := []func() error{t.Open, t.DeleteAll, t.Put}
  89. for _, f := range fs {
  90. if err := f(); err != nil {
  91. return err
  92. }
  93. }
  94. return nil
  95. }
  96. // IterCount tests that the iterator counts documents correctly.
  97. // The collection must be initialised.
  98. func (t *CollectionTester[T]) IterCount() error {
  99. iter := t.C.Iter()
  100. actual := iter.Count()
  101. iter.Release()
  102. expected := len(t.Data)
  103. if expected != actual {
  104. return fmt.Errorf("incorrect document count (expected %d, got %d)", expected, actual)
  105. }
  106. return nil
  107. }
  108. // IterSortKeys tests that the iterator sorts correctly by key.
  109. // The collection must be initialised.
  110. func (t *CollectionTester[T]) IterSortKeys() error {
  111. ks := &keySort{
  112. a: []string{},
  113. f: func(a, b string) bool {
  114. return a < b
  115. },
  116. }
  117. for key := range t.Data {
  118. ks.a = append(ks.a, key)
  119. }
  120. sort.Stable(ks)
  121. sortedKeys := ks.Result()
  122. iter := t.C.Iter().SortKeys(ks.f)
  123. defer iter.Release()
  124. errs := Errors{}
  125. actual := 0
  126. for iter.Next() {
  127. key := iter.Key()
  128. if key != sortedKeys[actual] {
  129. expected := slices.Index(sortedKeys, key)
  130. errs = append(errs, fmt.Errorf("incorrect sort position of key '%s' (expected %d, got %d)", key, expected, actual))
  131. }
  132. actual++
  133. }
  134. return errs.Resolve()
  135. }
  136. // Open tests that the collection is opened.
  137. func (t *CollectionTester[T]) Open() error {
  138. if err := t.C.Open(); err != nil {
  139. return fmt.Errorf("failed to open collection: %v", err)
  140. }
  141. return nil
  142. }
  143. // Put tests that the collection can store all documents.
  144. func (t *CollectionTester[T]) Put() error {
  145. errs := Errors{}
  146. for key, data := range t.Data {
  147. if err := t.C.Put(key, data); err != nil {
  148. errs = append(errs, fmt.Errorf("failed to put document '%s': %v", key, err))
  149. }
  150. }
  151. return errs.Resolve()
  152. }
  153. func (t *CollectionTester[T]) Close() error {
  154. if err := t.C.Close(); err != nil {
  155. return fmt.Errorf("failed to close collection: %v", err)
  156. }
  157. return nil
  158. }