Search Apps Documentation Source Content File Folder Download Copy Actions Download State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

rotree_test.gno

5.43 Kb · 221 lines
  1package rotree
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/bptree/v0"
  7)
  8
  9func TestExample(t *testing.T) {
 10	// User represents our internal data structure
 11	type User struct {
 12		ID       string
 13		Name     string
 14		Balance  int
 15		Internal string // sensitive internal data
 16	}
 17
 18	// Create and populate the original tree with user pointers
 19	tree := bptree.NewBPTree32()
 20	tree.Set("alice", &User{
 21		ID:       "1",
 22		Name:     "Alice",
 23		Balance:  100,
 24		Internal: "sensitive_data_1",
 25	})
 26	tree.Set("bob", &User{
 27		ID:       "2",
 28		Name:     "Bob",
 29		Balance:  200,
 30		Internal: "sensitive_data_2",
 31	})
 32
 33	// Define a makeEntrySafeFn that:
 34	// 1. Creates a defensive copy of the User struct
 35	// 2. Omits sensitive internal data
 36	makeEntrySafeFn := func(v any) any {
 37		originalUser := v.(*User)
 38		return &User{
 39			ID:       originalUser.ID,
 40			Name:     originalUser.Name,
 41			Balance:  originalUser.Balance,
 42			Internal: "", // Omit sensitive data
 43		}
 44	}
 45
 46	// Create a read-only view of the tree
 47	roTree := Wrap(tree, makeEntrySafeFn)
 48
 49	// Test retrieving and verifying a user
 50	t.Run("Get User", func(t *testing.T) {
 51		// Get user from read-only tree
 52		value := roTree.Get("alice")
 53		if value == nil {
 54			t.Fatal("User 'alice' not found")
 55		}
 56
 57		user := value.(*User)
 58
 59		// Verify user data is correct
 60		if user.Name != "Alice" || user.Balance != 100 {
 61			t.Errorf("Unexpected user data: got name=%s balance=%d", user.Name, user.Balance)
 62		}
 63
 64		// Verify sensitive data is not exposed
 65		if user.Internal != "" {
 66			t.Error("Sensitive data should not be exposed")
 67		}
 68
 69		// Verify it's a different instance than the original
 70		originalValue := tree.Get("alice")
 71		originalUser := originalValue.(*User)
 72		if user == originalUser {
 73			t.Error("Read-only tree should return a copy, not the original pointer")
 74		}
 75	})
 76
 77	// Test iterating over users
 78	t.Run("Iterate Users", func(t *testing.T) {
 79		count := 0
 80		roTree.Iterate("", "", func(key string, value any) bool {
 81			user := value.(*User)
 82			// Verify each user has empty Internal field
 83			if user.Internal != "" {
 84				t.Error("Sensitive data exposed during iteration")
 85			}
 86			count++
 87			return false
 88		})
 89
 90		if count != 2 {
 91			t.Errorf("Expected 2 users, got %d", count)
 92		}
 93	})
 94
 95	// Verify that modifications to the returned user don't affect the original
 96	t.Run("Modification Safety", func(t *testing.T) {
 97		value := roTree.Get("alice")
 98		user := value.(*User)
 99
100		// Try to modify the returned user
101		user.Balance = 999
102		user.Internal = "hacked"
103
104		// Verify original is unchanged
105		originalValue := tree.Get("alice")
106		originalUser := originalValue.(*User)
107		if originalUser.Balance != 100 || originalUser.Internal != "sensitive_data_1" {
108			t.Error("Original user data was modified")
109		}
110	})
111}
112
113func TestReadOnlyTree(t *testing.T) {
114	// Example of a makeEntrySafeFn that appends "_readonly" to demonstrate transformation
115	makeEntrySafeFn := func(value any) any {
116		return value.(string) + "_readonly"
117	}
118
119	tree := bptree.NewBPTree32()
120	tree.Set("key1", "value1")
121	tree.Set("key2", "value2")
122	tree.Set("key3", "value3")
123
124	roTree := Wrap(tree, makeEntrySafeFn)
125
126	tests := []struct {
127		name     string
128		key      string
129		expected any
130	}{
131		{"ExistingKey1", "key1", "value1_readonly"},
132		{"ExistingKey2", "key2", "value2_readonly"},
133		{"NonExistingKey", "key4", nil},
134	}
135
136	for _, tt := range tests {
137		t.Run(tt.name, func(t *testing.T) {
138			value := roTree.Get(tt.key)
139			if value != tt.expected {
140				t.Errorf("For key %s, expected %v, got %v", tt.key, tt.expected, value)
141			}
142		})
143	}
144}
145
146// Add example tests showing different makeEntrySafeFn implementations
147func TestMakeEntrySafeFnVariants(t *testing.T) {
148	tree := bptree.NewBPTree32()
149	tree.Set("slice", []int{1, 2, 3})
150	tree.Set("map", map[string]int{"a": 1})
151
152	tests := []struct {
153		name            string
154		makeEntrySafeFn func(any) any
155		key             string
156		validate        func(t *testing.T, value any)
157	}{
158		{
159			name: "Defensive Copy Slice",
160			makeEntrySafeFn: func(v any) any {
161				original := v.([]int)
162				return append([]int{}, original...)
163			},
164			key: "slice",
165			validate: func(t *testing.T, value any) {
166				slice := value.([]int)
167				// Modify the returned slice
168				slice[0] = 999
169				// Verify original is unchanged
170				originalValue := tree.Get("slice")
171				original := originalValue.([]int)
172				if original[0] != 1 {
173					t.Error("Original slice was modified")
174				}
175			},
176		},
177		// Add more test cases for different makeEntrySafeFn implementations
178	}
179
180	for _, tt := range tests {
181		t.Run(tt.name, func(t *testing.T) {
182			roTree := Wrap(tree, tt.makeEntrySafeFn)
183			value := roTree.Get(tt.key)
184			if value == nil {
185				t.Fatal("Key not found")
186			}
187			tt.validate(t, value)
188		})
189	}
190}
191
192func TestNilMakeEntrySafeFn(t *testing.T) {
193	// Create a tree with some test data
194	tree := bptree.NewBPTree32()
195	originalValue := []int{1, 2, 3}
196	tree.Set("test", originalValue)
197
198	// Create a ReadOnlyTree with nil makeEntrySafeFn
199	roTree := Wrap(tree, nil)
200
201	// Test that we get back the original value
202	value := roTree.Get("test")
203	if value == nil {
204		t.Fatal("Key not found")
205	}
206
207	// Verify it's the exact same slice (not a copy)
208	retrievedSlice := value.([]int)
209	if &retrievedSlice[0] != &originalValue[0] {
210		t.Error("Expected to get back the original slice reference")
211	}
212
213	// Test through iteration as well
214	roTree.Iterate("", "", func(key string, value any) bool {
215		retrievedSlice := value.([]int)
216		if &retrievedSlice[0] != &originalValue[0] {
217			t.Error("Expected to get back the original slice reference in iteration")
218		}
219		return false
220	})
221}