rolist_test.gno
3.68 Kb · 164 lines
1package rolist
2
3import (
4 "testing"
5
6 "gno.land/p/nt/bptree/v0/list"
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 list
19 l := &list.List{}
20 l.Append(
21 &User{
22 ID: "1",
23 Name: "Alice",
24 Balance: 100,
25 Internal: "sensitive_data_1",
26 },
27 &User{
28 ID: "2",
29 Name: "Bob",
30 Balance: 200,
31 Internal: "sensitive_data_2",
32 },
33 )
34
35 // Define a makeEntrySafeFn that:
36 // 1. Creates a defensive copy of the User struct
37 // 2. Omits sensitive internal data
38 makeEntrySafeFn := func(v any) any {
39 originalUser := v.(*User)
40 return &User{
41 ID: originalUser.ID,
42 Name: originalUser.Name,
43 Balance: originalUser.Balance,
44 Internal: "", // Omit sensitive data
45 }
46 }
47
48 // Create a read-only view of the list
49 roList := Wrap(l, makeEntrySafeFn)
50
51 // Test retrieving and verifying a user
52 t.Run("Get User", func(t *testing.T) {
53 // Get user from read-only list
54 value, ok := roList.Get(0)
55 if !ok {
56 t.Fatal("User at index 0 not found")
57 }
58
59 user := value.(*User)
60
61 // Verify user data is correct
62 if user.Name != "Alice" || user.Balance != 100 {
63 t.Errorf("Unexpected user data: got name=%s balance=%d", user.Name, user.Balance)
64 }
65
66 // Verify sensitive data is not exposed
67 if user.Internal != "" {
68 t.Error("Sensitive data should not be exposed")
69 }
70
71 // Verify it's a different instance than the original
72 origVal, _ := l.Get(0)
73 originalUser := origVal.(*User)
74 if user == originalUser {
75 t.Error("Read-only list should return a copy, not the original pointer")
76 }
77 })
78
79 // Test slice functionality
80 t.Run("Slice Users", func(t *testing.T) {
81 users := roList.Slice(0, 2)
82 if len(users) != 2 {
83 t.Fatalf("Expected 2 users, got %d", len(users))
84 }
85
86 for _, v := range users {
87 user := v.(*User)
88 if user.Internal != "" {
89 t.Error("Sensitive data exposed in slice")
90 }
91 }
92 })
93
94 // Test ForEach functionality
95 t.Run("ForEach Users", func(t *testing.T) {
96 count := 0
97 roList.ForEach(func(index int, value any) bool {
98 user := value.(*User)
99 if user.Internal != "" {
100 t.Error("Sensitive data exposed during iteration")
101 }
102 count++
103 return false
104 })
105
106 if count != 2 {
107 t.Errorf("Expected 2 users, got %d", count)
108 }
109 })
110}
111
112func TestNilMakeEntrySafeFn(t *testing.T) {
113 // Create a list with some test data
114 l := &list.List{}
115 originalValue := []int{1, 2, 3}
116 l.Append(originalValue)
117
118 // Create a ReadOnlyList with nil makeEntrySafeFn
119 roList := Wrap(l, nil)
120
121 // Test that we get back the original value
122 value, ok := roList.Get(0)
123 if !ok {
124 t.Fatal("Value not found")
125 }
126
127 // Verify it's the exact same slice (not a copy)
128 retrievedSlice := value.([]int)
129 if &retrievedSlice[0] != &originalValue[0] {
130 t.Error("Expected to get back the original slice reference")
131 }
132}
133
134func TestReadOnlyList(t *testing.T) {
135 // Example of a makeEntrySafeFn that appends "_readonly" to demonstrate transformation
136 makeEntrySafeFn := func(value any) any {
137 return value.(string) + "_readonly"
138 }
139
140 l := &list.List{}
141 l.Append("value1", "value2", "value3")
142
143 roList := Wrap(l, makeEntrySafeFn)
144
145 tests := []struct {
146 name string
147 index int
148 expected any
149 ok bool
150 }{
151 {"ExistingIndex0", 0, "value1_readonly", true},
152 {"ExistingIndex1", 1, "value2_readonly", true},
153 {"NonExistingIndex", 3, nil, false},
154 }
155
156 for _, tt := range tests {
157 t.Run(tt.name, func(t *testing.T) {
158 value, ok := roList.Get(tt.index)
159 if ok != tt.ok || value != tt.expected {
160 t.Errorf("For index %d, expected (%v, %v), got (%v, %v)", tt.index, tt.expected, tt.ok, value, ok)
161 }
162 })
163 }
164}