Skip to content

Commit b2e6f1a

Browse files
authored
Merge pull request #10 from tstromberg/main
Improve support for advanced features (entity encoding, embedded structs, namespaces)
2 parents 4697c79 + e57d5a7 commit b2e6f1a

16 files changed

+1461
-563
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ Just switch the import path from `cloud.google.com/go/datastore` to `github.com/
5050

5151
These features are unsupported just because we haven't found a use for the feature yet. PRs welcome:
5252

53-
* Embedded structs, nested slices, map types, some advanced query features (streaming aggregations, OR filters).
53+
* Nested slices, map types, some advanced query features (streaming aggregations, OR filters).
5454

5555
## Testing
5656

pkg/datastore/batch_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
package datastore_test
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"testing"
7+
8+
"github.com/codeGROOVE-dev/ds9/pkg/datastore"
9+
)
10+
11+
func TestBatchOperations(t *testing.T) {
12+
client, cleanup := datastore.NewMockClient(t)
13+
defer cleanup()
14+
15+
ctx := context.Background()
16+
17+
type Item struct {
18+
ID int
19+
}
20+
21+
// Number of items > 1000 to test batching limits
22+
// Put limit: 500, Get limit: 1000
23+
const count = 1200
24+
keys := make([]*datastore.Key, count)
25+
items := make([]Item, count)
26+
27+
for i := range count {
28+
keys[i] = datastore.NameKey("Item", fmt.Sprintf("item-%d", i), nil)
29+
items[i] = Item{ID: i}
30+
}
31+
32+
// Test PutMulti
33+
if _, err := client.PutMulti(ctx, keys, items); err != nil {
34+
t.Fatalf("PutMulti failed: %v", err)
35+
}
36+
37+
// Test GetMulti
38+
results := make([]Item, count)
39+
if err := client.GetMulti(ctx, keys, &results); err != nil {
40+
t.Fatalf("GetMulti failed: %v", err)
41+
}
42+
43+
for i := range count {
44+
if results[i].ID != i {
45+
t.Errorf("Item %d mismatch: got %d, want %d", i, results[i].ID, i)
46+
}
47+
}
48+
49+
// Test DeleteMulti
50+
if err := client.DeleteMulti(ctx, keys); err != nil {
51+
t.Fatalf("DeleteMulti failed: %v", err)
52+
}
53+
54+
// Verify deletion
55+
err := client.GetMulti(ctx, keys, &results)
56+
// Should return MultiError with all ErrNoSuchEntity
57+
if err == nil {
58+
t.Fatal("Expected error after deletion, got nil")
59+
}
60+
}
61+
62+
func TestAllocateIDsBatch(t *testing.T) {
63+
client, cleanup := datastore.NewMockClient(t)
64+
defer cleanup()
65+
66+
ctx := context.Background()
67+
68+
// Test AllocateIDs > 500
69+
const count = 600
70+
keys := make([]*datastore.Key, count)
71+
for i := range count {
72+
keys[i] = datastore.IncompleteKey("Item", nil)
73+
}
74+
75+
allocated, err := client.AllocateIDs(ctx, keys)
76+
if err != nil {
77+
t.Fatalf("AllocateIDs failed: %v", err)
78+
}
79+
80+
if len(allocated) != count {
81+
t.Fatalf("Expected %d keys, got %d", count, len(allocated))
82+
}
83+
84+
seen := make(map[int64]bool)
85+
for i, k := range allocated {
86+
if k.Incomplete() {
87+
t.Errorf("Key %d is incomplete", i)
88+
}
89+
if seen[k.ID] {
90+
t.Errorf("Duplicate ID %d at index %d", k.ID, i)
91+
}
92+
seen[k.ID] = true
93+
}
94+
}

pkg/datastore/encode_coverage_test.go

Lines changed: 8 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,9 @@ func TestEncodeValue_ReflectionSlices(t *testing.T) {
3434

3535
for _, tt := range tests {
3636
t.Run(tt.name, func(t *testing.T) {
37-
result, err := encodeValue(tt.value)
37+
result, err := encodeAny(tt.value)
3838
if err != nil {
39-
t.Errorf("encodeValue(%v) failed: %v", tt.value, err)
39+
t.Errorf("encodeAny(%v) failed: %v", tt.value, err)
4040
}
4141
if result == nil {
4242
t.Error("Expected non-nil result")
@@ -63,17 +63,14 @@ func TestEncodeValue_Errors(t *testing.T) {
6363
"channel type",
6464
make(chan int),
6565
},
66-
{
67-
"struct type",
68-
struct{ Name string }{Name: "test"},
69-
},
66+
// Note: struct types are now supported as nested entities
7067
}
7168

7269
for _, tt := range tests {
7370
t.Run(tt.name, func(t *testing.T) {
74-
_, err := encodeValue(tt.value)
71+
_, err := encodeAny(tt.value)
7572
if err == nil {
76-
t.Errorf("encodeValue(%T) should have returned an error", tt.value)
73+
t.Errorf("encodeAny(%T) should have returned an error", tt.value)
7774
}
7875
})
7976
}
@@ -86,7 +83,7 @@ func TestEncodeValue_TimeSlice(t *testing.T) {
8683

8784
timeSlice := []time.Time{now, later}
8885

89-
result, err := encodeValue(timeSlice)
86+
result, err := encodeAny(timeSlice)
9087
if err != nil {
9188
t.Fatalf("encodeValue failed for time slice: %v", err)
9289
}
@@ -122,7 +119,7 @@ func TestEncodeValue_EmptySlices(t *testing.T) {
122119

123120
for _, tt := range tests {
124121
t.Run(tt.name, func(t *testing.T) {
125-
result, err := encodeValue(tt.value)
122+
result, err := encodeAny(tt.value)
126123
if err != nil {
127124
t.Errorf("encodeValue failed: %v", err)
128125
}
@@ -148,7 +145,7 @@ func TestEncodeValue_SingleElementSlices(t *testing.T) {
148145

149146
for _, tt := range tests {
150147
t.Run(tt.name, func(t *testing.T) {
151-
result, err := encodeValue(tt.value)
148+
result, err := encodeAny(tt.value)
152149
if err != nil {
153150
t.Errorf("encodeValue failed: %v", err)
154151
}

0 commit comments

Comments
 (0)