collection_test.gno
20.94 Kb ยท 987 lines
1package collection
2
3import (
4 "errors"
5 "strconv"
6 "strings"
7 "testing"
8
9 "gno.land/p/demo/seqid"
10 "gno.land/p/demo/ufmt"
11)
12
13type Person struct {
14 Name string
15 Age int
16 Email string
17 Username string
18 Tags []string
19}
20
21func (p Person) String() string {
22 return ufmt.Sprintf("name=%s age=%d email=%s username=%s tags=%s",
23 p.Name, p.Age, p.Email, p.Username, strings.Join(p.Tags, ","))
24}
25
26// TestOperation represents a single operation in a test sequence
27type TestOperation struct {
28 op string // "set" or "update"
29 person *Person
30 id uint64 // for updates
31 wantID uint64
32 wantErr bool
33}
34
35// TestCase represents a complete test case with setup and operations
36type TestCase struct {
37 name string
38 setupIndex func(*Collection)
39 operations []TestOperation
40}
41
42func TestBasicOperations(t *testing.T) {
43 c := New()
44
45 // Add indexes
46 c.AddIndex("name", func(v any) string {
47 return v.(*Person).Name
48 }, UniqueIndex)
49 c.AddIndex("age", func(v any) string {
50 return strconv.Itoa(v.(*Person).Age)
51 }, DefaultIndex)
52
53 // Test basic Set and Get
54 p1 := &Person{Name: "Alice", Age: 30, Email: "alice@test.com"}
55 id1 := c.Set(p1)
56 if id1 == 0 {
57 t.Error("Failed to set first object")
58 }
59
60 // Get by ID
61 iter := c.Get(IDIndex, seqid.ID(id1).String())
62 if !iter.Next() {
63 t.Error("Failed to get object by ID")
64 }
65 entry := iter.Value()
66 if entry.Obj.(*Person).Name != "Alice" {
67 t.Error("Got wrong object")
68 }
69}
70
71func TestUniqueConstraints(t *testing.T) {
72 tests := []struct {
73 name string
74 setup func(*Collection) uint64
75 wantID bool
76 }{
77 {
78 name: "First person",
79 setup: func(c *Collection) uint64 {
80 return c.Set(&Person{Name: "Alice"})
81 },
82 wantID: true,
83 },
84 {
85 name: "Duplicate name",
86 setup: func(c *Collection) uint64 {
87 c.Set(&Person{Name: "Alice"})
88 return c.Set(&Person{Name: "Alice"})
89 },
90 wantID: false,
91 },
92 {
93 name: "Same age (non-unique index)",
94 setup: func(c *Collection) uint64 {
95 c.AddIndex("age", func(v any) string {
96 return strconv.Itoa(v.(*Person).Age)
97 }, DefaultIndex)
98 c.Set(&Person{Name: "Alice", Age: 30})
99 return c.Set(&Person{Name: "Bob", Age: 30})
100 },
101 wantID: true,
102 },
103 }
104
105 for _, tt := range tests {
106 t.Run(tt.name, func(t *testing.T) {
107 c := New()
108 c.AddIndex("name", func(v any) string {
109 return v.(*Person).Name
110 }, UniqueIndex)
111
112 id := tt.setup(c)
113 if (id != 0) != tt.wantID {
114 t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID)
115 }
116 })
117 }
118}
119
120func TestUpdates(t *testing.T) {
121 c := New()
122 c.AddIndex("name", func(v any) string {
123 return v.(*Person).Name
124 }, UniqueIndex)
125 c.AddIndex("username", func(v any) string {
126 return strings.ToLower(v.(*Person).Username)
127 }, UniqueIndex|CaseInsensitiveIndex)
128
129 // Initial setup
130 p1 := &Person{Name: "Alice", Username: "alice123"}
131 p2 := &Person{Name: "Bob", Username: "bob456"}
132
133 id1 := c.Set(p1)
134 c.Set(p2)
135
136 tests := []struct {
137 name string
138 id uint64
139 newPerson *Person
140 wantRet bool
141 }{
142 {
143 name: "Update to non-conflicting values",
144 id: id1,
145 newPerson: &Person{Name: "Alice2", Username: "alice1234"},
146 wantRet: true,
147 },
148 {
149 name: "Update to conflicting username",
150 id: id1,
151 newPerson: &Person{Name: "Alice2", Username: "bob456"},
152 wantRet: false,
153 },
154 {
155 name: "Update non-existent ID",
156 id: 99999,
157 newPerson: &Person{Name: "Test", Username: "test"},
158 wantRet: false,
159 },
160 }
161
162 for _, tt := range tests {
163 t.Run(tt.name, func(t *testing.T) {
164 gotID := c.Update(tt.id, tt.newPerson)
165 if gotID != tt.wantRet {
166 t.Errorf("Update() got = %v, want %v", gotID, tt.wantRet)
167 }
168 })
169 }
170}
171
172func TestDelete(t *testing.T) {
173 c := New()
174 c.AddIndex("name", func(v any) string {
175 return v.(*Person).Name
176 }, UniqueIndex)
177
178 p1 := &Person{Name: "Alice"}
179 id1 := c.Set(p1)
180
181 tests := []struct {
182 name string
183 id uint64
184 wantRet bool
185 }{
186 {
187 name: "Delete existing object",
188 id: id1,
189 wantRet: true,
190 },
191 {
192 name: "Delete non-existent object",
193 id: 99999,
194 wantRet: false,
195 },
196 {
197 name: "Delete already deleted object",
198 id: id1,
199 wantRet: false,
200 },
201 }
202
203 for _, tt := range tests {
204 t.Run(tt.name, func(t *testing.T) {
205 gotID := c.Delete(tt.id)
206 if gotID != tt.wantRet {
207 t.Errorf("Delete() got = %v, want %v", gotID, tt.wantRet)
208 }
209 })
210 }
211}
212
213func TestEdgeCases(t *testing.T) {
214 c := New()
215 c.AddIndex("name", func(v any) string {
216 return v.(*Person).Name
217 }, UniqueIndex)
218
219 tests := []struct {
220 name string
221 operation func() bool
222 wantPanic bool
223 }{
224 {
225 name: "Set nil object",
226 operation: func() bool {
227 return c.Set(nil) != 0
228 },
229 wantPanic: false,
230 },
231 {
232 name: "Set wrong type",
233 operation: func() bool {
234 return c.Set("not a person") != 0
235 },
236 wantPanic: true,
237 },
238 {
239 name: "Update with nil",
240 operation: func() bool {
241 id := c.Set(&Person{Name: "Test"})
242 return c.Update(id, nil)
243 },
244 wantPanic: false,
245 },
246 {
247 name: "Get with invalid index name",
248 operation: func() bool {
249 iter := c.Get("invalid_index", "key")
250 if iter.Empty() {
251 return false
252 }
253 entry := iter.Value()
254 if entry == nil {
255 return false
256 }
257 _, err := seqid.FromString(entry.ID)
258 if err != nil {
259 return false
260 }
261 return true
262 },
263 wantPanic: false,
264 },
265 }
266
267 for _, tt := range tests {
268 t.Run(tt.name, func(t *testing.T) {
269 var got bool
270 panicked := false
271
272 func() {
273 defer func() {
274 if r := recover(); r != nil {
275 panicked = true
276 }
277 }()
278 got = tt.operation()
279 }()
280
281 if panicked != tt.wantPanic {
282 t.Errorf("Operation panicked = %v, want panic = %v", panicked, tt.wantPanic)
283 }
284 if !panicked && got != false {
285 t.Errorf("Operation returned %v, want 0", got)
286 }
287 })
288 }
289}
290
291func TestIndexTypes(t *testing.T) {
292 c := New()
293
294 // Test different types of indexes
295 c.AddIndex("composite", func(v any) string {
296 p := v.(*Person)
297 return p.Name + ":" + strconv.Itoa(p.Age)
298 }, UniqueIndex)
299
300 c.AddIndex("case_insensitive", func(v any) string {
301 return strings.ToLower(v.(*Person).Username)
302 }, UniqueIndex|CaseInsensitiveIndex)
303
304 // Test composite index
305 p1 := &Person{Name: "Alice", Age: 30, Username: "Alice123"}
306 id1 := c.Set(p1)
307 if id1 == 0 {
308 t.Error("Failed to set object with composite index")
309 }
310
311 // Test case-insensitive index
312 p2 := &Person{Name: "Bob", Age: 25, Username: "alice123"}
313 id2 := c.Set(p2)
314 if id2 != 0 {
315 t.Error("Case-insensitive index failed to prevent duplicate")
316 }
317}
318
319func TestIndexOptions(t *testing.T) {
320 tests := []struct {
321 name string
322 setup func(*Collection) uint64
323 wantID bool
324 wantErr bool
325 }{
326 {
327 name: "Unique case-sensitive index",
328 setup: func(c *Collection) uint64 {
329 c.AddIndex("username", func(v any) string {
330 return v.(*Person).Username
331 }, UniqueIndex)
332
333 c.Set(&Person{Username: "Alice"})
334 return c.Set(&Person{Username: "Alice"}) // Should fail
335 },
336 wantID: false,
337 },
338 {
339 name: "Unique case-insensitive index",
340 setup: func(c *Collection) uint64 {
341 c.AddIndex("email", func(v any) string {
342 return v.(*Person).Email
343 }, UniqueIndex|CaseInsensitiveIndex)
344
345 c.Set(&Person{Email: "test@example.com"})
346 return c.Set(&Person{Email: "TEST@EXAMPLE.COM"}) // Should fail
347 },
348 wantID: false,
349 },
350 {
351 name: "Default index",
352 setup: func(c *Collection) uint64 {
353 c.AddIndex("age", func(v any) string {
354 return strconv.Itoa(v.(*Person).Age)
355 }, DefaultIndex)
356
357 // First person with age 30
358 id1 := c.Set(&Person{Age: 30})
359 if id1 == 0 {
360 t.Error("Failed to set first person")
361 }
362
363 // Second person with same age should succeed
364 return c.Set(&Person{Age: 30})
365 },
366 wantID: true,
367 },
368 {
369 name: "Multiple options",
370 setup: func(c *Collection) uint64 {
371 c.AddIndex("name", func(v any) string {
372 return v.(*Person).Name
373 }, UniqueIndex|CaseInsensitiveIndex|SparseIndex)
374
375 c.Set(&Person{Name: "Alice"})
376 return c.Set(&Person{Name: "ALICE"}) // Should fail
377 },
378 wantID: false,
379 },
380 }
381
382 for _, tt := range tests {
383 t.Run(tt.name, func(t *testing.T) {
384 c := New() // Create new collection for each test
385 id := tt.setup(c)
386 if (id != 0) != tt.wantID {
387 t.Errorf("got id = %v, want non-zero: %v", id, tt.wantID)
388 }
389 })
390 }
391}
392
393func TestConcurrentOperations(t *testing.T) {
394 c := New()
395 c.AddIndex("name", func(v any) string {
396 return v.(*Person).Name
397 }, UniqueIndex)
398
399 p1 := &Person{Name: "Alice"}
400 id1 := c.Set(p1)
401 iter := c.Get("_id", seqid.ID(id1).String())
402 success := c.Update(id1, &Person{Name: "Alice2"})
403
404 if iter.Empty() || !success {
405 t.Error("Concurrent operations failed")
406 }
407}
408
409func TestSparseIndexBehavior(t *testing.T) {
410 c := New()
411 c.AddIndex("optional_field", func(v any) string {
412 return v.(*Person).Username
413 }, SparseIndex)
414
415 tests := []struct {
416 name string
417 person *Person
418 wantID bool
419 }{
420 {
421 name: "Empty optional field",
422 person: &Person{Name: "Alice", Email: "alice@test.com"},
423 wantID: true,
424 },
425 {
426 name: "Populated optional field",
427 person: &Person{Name: "Bob", Email: "bob@test.com", Username: "bobby"},
428 wantID: true,
429 },
430 {
431 name: "Multiple empty fields",
432 person: &Person{Name: "Charlie"},
433 wantID: true,
434 },
435 }
436
437 for _, tt := range tests {
438 t.Run(tt.name, func(t *testing.T) {
439 id := c.Set(tt.person)
440 if (id != 0) != tt.wantID {
441 t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID)
442 }
443 })
444 }
445}
446
447func TestIndexKeyGeneration(t *testing.T) {
448 c := New()
449 c.AddIndex("composite", func(v any) string {
450 p := v.(*Person)
451 return p.Name + ":" + strconv.Itoa(p.Age)
452 }, UniqueIndex)
453
454 tests := []struct {
455 name string
456 person *Person
457 wantID bool
458 }{
459 {
460 name: "Valid composite key",
461 person: &Person{Name: "Alice", Age: 30},
462 wantID: true,
463 },
464 {
465 name: "Duplicate composite key",
466 person: &Person{Name: "Alice", Age: 30},
467 wantID: false,
468 },
469 {
470 name: "Different composite key",
471 person: &Person{Name: "Alice", Age: 31},
472 wantID: true,
473 },
474 }
475
476 for _, tt := range tests {
477 t.Run(tt.name, func(t *testing.T) {
478 id := c.Set(tt.person)
479 if (id != 0) != tt.wantID {
480 t.Errorf("Set() got id = %v, want non-zero: %v", id, tt.wantID)
481 }
482 })
483 }
484}
485
486func TestGetIndex(t *testing.T) {
487 c := New()
488 c.AddIndex("name", func(v any) string {
489 return v.(*Person).Name
490 }, UniqueIndex)
491
492 tests := []struct {
493 name string
494 indexName string
495 wantNil bool
496 }{
497 {
498 name: "Get existing index",
499 indexName: "name",
500 wantNil: false,
501 },
502 {
503 name: "Get _id index",
504 indexName: IDIndex,
505 wantNil: false,
506 },
507 {
508 name: "Get non-existent index",
509 indexName: "invalid",
510 wantNil: true,
511 },
512 }
513
514 for _, tt := range tests {
515 t.Run(tt.name, func(t *testing.T) {
516 tree := c.GetIndex(tt.indexName)
517 if (tree == nil) != tt.wantNil {
518 t.Errorf("GetIndex() got nil = %v, want nil = %v", tree == nil, tt.wantNil)
519 }
520 })
521 }
522}
523
524func TestAddIndexPanic(t *testing.T) {
525 c := New()
526 defer func() {
527 if r := recover(); r == nil {
528 t.Error("Expected panic when adding _id index")
529 }
530 }()
531
532 c.AddIndex(IDIndex, func(v any) string {
533 return ""
534 }, DefaultIndex)
535}
536
537func TestCaseInsensitiveOperations(t *testing.T) {
538 c := New()
539 c.AddIndex("email", func(v any) string {
540 return v.(*Person).Email
541 }, UniqueIndex|CaseInsensitiveIndex)
542
543 p := &Person{Email: "Test@Example.com"}
544 id := c.Set(p)
545
546 tests := []struct {
547 name string
548 key string
549 wantObj bool
550 operation string // "get" or "getAll"
551 wantCount int
552 }{
553 {"Get exact match", "Test@Example.com", true, "get", 1},
554 {"Get different case", "test@example.COM", true, "get", 1},
555 {"Get non-existent", "other@example.com", false, "get", 0},
556 {"GetAll exact match", "Test@Example.com", true, "getAll", 1},
557 {"GetAll different case", "test@example.COM", true, "getAll", 1},
558 {"GetAll non-existent", "other@example.com", false, "getAll", 0},
559 }
560
561 for _, tt := range tests {
562 t.Run(tt.name, func(t *testing.T) {
563 if tt.operation == "get" {
564 iter := c.Get("email", tt.key)
565 if iter.Empty() {
566 if tt.wantObj {
567 t.Error("Expected iterator to not be empty")
568 }
569 return
570 }
571 hasValue := iter.Next()
572 if hasValue != tt.wantObj {
573 t.Errorf("Get() got object = %v, want object = %v", hasValue, tt.wantObj)
574 }
575 if hasValue {
576 entry := iter.Value()
577 if entry.ID != seqid.ID(id).String() {
578 t.Errorf("Get() got id = %v, want id = %v", entry.ID, seqid.ID(id).String())
579 }
580 }
581 } else {
582 entries := c.GetAll("email", tt.key)
583 if len(entries) != tt.wantCount {
584 t.Errorf("GetAll() returned %d entries, want %d", len(entries), tt.wantCount)
585 }
586 if tt.wantCount > 0 {
587 entry := entries[0]
588 if entry.ID != seqid.ID(id).String() {
589 t.Errorf("GetAll() returned ID %s, want %s", entry.ID, seqid.ID(id).String())
590 }
591 }
592 }
593 })
594 }
595}
596
597func TestGetInvalidID(t *testing.T) {
598 c := New()
599 iter := c.Get(IDIndex, "not-a-valid-id")
600 if !iter.Empty() {
601 t.Errorf("Get() with invalid ID format got an entry, want nil")
602 }
603}
604
605func TestGetAll(t *testing.T) {
606 c := New()
607 c.AddIndex("tags", func(v any) []string {
608 return v.(*Person).Tags
609 }, DefaultIndex)
610 c.AddIndex("age", func(v any) string {
611 return strconv.Itoa(v.(*Person).Age)
612 }, DefaultIndex)
613 c.AddIndex("name", func(v any) string {
614 return v.(*Person).Name
615 }, UniqueIndex)
616
617 // Create test data
618 people := []*Person{
619 {Name: "Alice", Age: 30, Tags: []string{"dev", "go"}},
620 {Name: "Bob", Age: 30, Tags: []string{"dev", "python"}},
621 {Name: "Charlie", Age: 25, Tags: []string{"dev", "rust"}},
622 }
623
624 ids := make([]uint64, len(people))
625 for i, p := range people {
626 ids[i] = c.Set(p)
627 if ids[i] == 0 {
628 t.Fatalf("Failed to set person %s", p.Name)
629 }
630 }
631
632 tests := []struct {
633 name string
634 indexName string
635 key string
636 wantCount int
637 }{
638 {
639 name: "Multi-value index with multiple matches",
640 indexName: "tags",
641 key: "dev",
642 wantCount: 3,
643 },
644 {
645 name: "Multi-value index with single match",
646 indexName: "tags",
647 key: "go",
648 wantCount: 1,
649 },
650 {
651 name: "Multi-value index with no matches",
652 indexName: "tags",
653 key: "java",
654 wantCount: 0,
655 },
656 {
657 name: "Single-value non-unique index with multiple matches",
658 indexName: "age",
659 key: "30",
660 wantCount: 2,
661 },
662 {
663 name: "Single-value unique index",
664 indexName: "name",
665 key: "Alice",
666 wantCount: 1,
667 },
668 {
669 name: "Non-existent index",
670 indexName: "invalid",
671 key: "value",
672 wantCount: 0,
673 },
674 }
675
676 for _, tt := range tests {
677 t.Run(tt.name, func(t *testing.T) {
678 iter := c.Get(tt.indexName, tt.key)
679 count := 0
680 for iter.Next() {
681 entry := iter.Value()
682 if entry.ID == "" {
683 t.Error("Got entry with empty ID")
684 }
685 if entry.Obj == nil {
686 t.Error("Got entry with nil Obj")
687 }
688 count++
689 }
690 if count != tt.wantCount {
691 t.Errorf("Got %d entries, want %d", count, tt.wantCount)
692 }
693 })
694 }
695}
696
697func TestIndexOperations(t *testing.T) {
698 tests := []struct {
699 name string
700 setup func(*Collection) (uint64, error)
701 verify func(*Collection, uint64) error
702 wantErr bool
703 }{
704 {
705 name: "Basic set and get",
706 setup: func(c *Collection) (uint64, error) {
707 c.AddIndex("name", func(v any) string {
708 return v.(*Person).Name
709 }, UniqueIndex)
710 return c.Set(&Person{Name: "Alice", Age: 30}), nil
711 },
712 verify: func(c *Collection, id uint64) error {
713 iter := c.Get(IDIndex, seqid.ID(id).String())
714 if !iter.Next() {
715 return errors.New("failed to get object by ID")
716 }
717 entry := iter.Value()
718 if entry.Obj.(*Person).Name != "Alice" {
719 return errors.New("got wrong object")
720 }
721 return nil
722 },
723 },
724 {
725 name: "Composite index",
726 setup: func(c *Collection) (uint64, error) {
727 c.AddIndex("composite", func(v any) string {
728 p := v.(*Person)
729 return p.Name + ":" + strconv.Itoa(p.Age)
730 }, UniqueIndex)
731 return c.Set(&Person{Name: "Alice", Age: 30}), nil
732 },
733 verify: func(c *Collection, id uint64) error {
734 iter := c.Get("composite", "Alice:30")
735 if !iter.Next() {
736 return errors.New("failed to get object by composite index")
737 }
738 return nil
739 },
740 },
741 // Add more test cases combining unique scenarios from original tests
742 }
743
744 for _, tt := range tests {
745 t.Run(tt.name, func(t *testing.T) {
746 c := New()
747 id, err := tt.setup(c)
748 if (err != nil) != tt.wantErr {
749 t.Errorf("setup error = %v, wantErr %v", err, tt.wantErr)
750 return
751 }
752 if err == nil {
753 if err := tt.verify(c, id); err != nil {
754 t.Errorf("verification failed: %v", err)
755 }
756 }
757 })
758 }
759}
760
761func TestMultiValueIndexes(t *testing.T) {
762 c := New()
763 c.AddIndex("tags", func(v any) []string {
764 return v.(*Person).Tags
765 }, DefaultIndex)
766
767 tests := []struct {
768 name string
769 setup []*Person
770 searchTag string
771 wantCount int
772 }{
773 {
774 name: "Multiple tags, multiple matches",
775 setup: []*Person{
776 {Name: "Alice", Tags: []string{"dev", "go"}},
777 {Name: "Bob", Tags: []string{"dev", "python"}},
778 {Name: "Charlie", Tags: []string{"dev", "rust"}},
779 },
780 searchTag: "dev",
781 wantCount: 3,
782 },
783 {
784 name: "Single tag match",
785 setup: []*Person{
786 {Name: "Alice", Tags: []string{"dev", "go"}},
787 {Name: "Bob", Tags: []string{"dev", "python"}},
788 },
789 searchTag: "go",
790 wantCount: 1,
791 },
792 {
793 name: "No matches",
794 setup: []*Person{
795 {Name: "Alice", Tags: []string{"dev", "go"}},
796 {Name: "Bob", Tags: []string{"dev", "python"}},
797 },
798 searchTag: "java",
799 wantCount: 0,
800 },
801 {
802 name: "Empty tags",
803 setup: []*Person{
804 {Name: "Alice", Tags: []string{}},
805 {Name: "Bob", Tags: nil},
806 },
807 searchTag: "dev",
808 wantCount: 0,
809 },
810 }
811
812 for _, tt := range tests {
813 t.Run(tt.name, func(t *testing.T) {
814 c := New()
815 c.AddIndex("tags", func(v any) []string {
816 return v.(*Person).Tags
817 }, DefaultIndex)
818
819 // Setup test data
820 for _, p := range tt.setup {
821 if id := c.Set(p); id == 0 {
822 t.Fatalf("Failed to set person %s", p.Name)
823 }
824 }
825
826 // Test Get operation
827 iter := c.Get("tags", tt.searchTag)
828 count := 0
829 for iter.Next() {
830 count++
831 }
832 if count != tt.wantCount {
833 t.Errorf("Get() got %d matches, want %d", count, tt.wantCount)
834 }
835 })
836 }
837}
838
839func TestGetOperations(t *testing.T) {
840 c := New()
841 c.AddIndex("name", func(v any) string {
842 return v.(*Person).Name
843 }, UniqueIndex)
844 c.AddIndex("age", func(v any) string {
845 return strconv.Itoa(v.(*Person).Age)
846 }, DefaultIndex)
847
848 // Setup test data
849 testPeople := []*Person{
850 {Name: "Alice", Age: 30},
851 {Name: "Bob", Age: 30},
852 {Name: "Charlie", Age: 25},
853 }
854
855 ids := make([]uint64, len(testPeople))
856 for i, p := range testPeople {
857 ids[i] = c.Set(p)
858 if ids[i] == 0 {
859 t.Fatalf("Failed to set person %s", p.Name)
860 }
861 }
862
863 tests := []struct {
864 name string
865 indexName string
866 key string
867 wantCount int
868 wantErr bool
869 }{
870 {
871 name: "Get by ID",
872 indexName: IDIndex,
873 key: seqid.ID(ids[0]).String(),
874 wantCount: 1,
875 wantErr: false,
876 },
877 {
878 name: "Get by unique index",
879 indexName: "name",
880 key: "Alice",
881 wantCount: 1,
882 wantErr: false,
883 },
884 {
885 name: "Get by non-unique index",
886 indexName: "age",
887 key: "30",
888 wantCount: 2,
889 wantErr: false,
890 },
891 {
892 name: "Get with invalid index",
893 indexName: "invalid_index",
894 key: "value",
895 wantCount: 0,
896 wantErr: true,
897 },
898 {
899 name: "Get with invalid ID format",
900 indexName: IDIndex,
901 key: "not-a-valid-id",
902 wantCount: 0,
903 wantErr: true,
904 },
905 }
906
907 for _, tt := range tests {
908 t.Run(tt.name, func(t *testing.T) {
909 iter := c.Get(tt.indexName, tt.key)
910 if iter.Empty() {
911 if !tt.wantErr {
912 t.Errorf("Get() returned empty iterator, wanted %d results", tt.wantCount)
913 }
914 return
915 }
916
917 count := 0
918 for iter.Next() {
919 entry := iter.Value()
920 if entry.ID == "" {
921 t.Error("Got entry with empty ID")
922 }
923 if entry.Obj == nil {
924 t.Error("Got entry with nil Obj")
925 }
926 count++
927 }
928
929 if count != tt.wantCount {
930 t.Errorf("Get() returned %d results, want %d", count, tt.wantCount)
931 }
932 })
933 }
934}
935
936func TestEntryString(t *testing.T) {
937 tests := []struct {
938 name string
939 entry *Entry
940 expected string
941 }{
942 {
943 name: "Nil entry",
944 entry: nil,
945 expected: "<nil>",
946 },
947 {
948 name: "Person entry",
949 entry: &Entry{
950 ID: "123",
951 Obj: &Person{Name: "Alice", Age: 30},
952 },
953 expected: `Entry{ID: 123, Obj: name=Alice age=30 email= username= tags=}`,
954 },
955 {
956 name: "Entry with nil object",
957 entry: &Entry{
958 ID: "456",
959 Obj: nil,
960 },
961 expected: `Entry{ID: 456, Obj: <nil>}`,
962 },
963 {
964 name: "Entry with complete person",
965 entry: &Entry{
966 ID: "789",
967 Obj: &Person{
968 Name: "Bob",
969 Age: 25,
970 Email: "bob@example.com",
971 Username: "bobby",
972 Tags: []string{"dev", "go"},
973 },
974 },
975 expected: `Entry{ID: 789, Obj: name=Bob age=25 email=bob@example.com username=bobby tags=dev,go}`,
976 },
977 }
978
979 for _, tt := range tests {
980 t.Run(tt.name, func(t *testing.T) {
981 got := tt.entry.String()
982 if got != tt.expected {
983 t.Errorf("Entry.String() = %q, want %q", got, tt.expected)
984 }
985 })
986 }
987}