Skip to content

Commit d2331f0

Browse files
committed
test(fuzz): add fuzz tests for query object methods
1 parent 23d3890 commit d2331f0

File tree

1 file changed

+294
-0
lines changed

1 file changed

+294
-0
lines changed
Lines changed: 294 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,294 @@
1+
package slap
2+
3+
import (
4+
"encoding/json"
5+
"testing"
6+
7+
"github.com/bsv-blockchain/go-overlay-discovery-services/pkg/types"
8+
)
9+
10+
// FuzzParseQueryObjectJSON tests the parseQueryObject method with random JSON inputs
11+
// to ensure it handles malformed and edge-case JSON gracefully.
12+
func FuzzParseQueryObjectJSON(f *testing.F) {
13+
// Seed corpus with valid query JSON examples
14+
f.Add(`{"findAll": true}`)
15+
f.Add(`{"domain": "example.com"}`)
16+
f.Add(`{"service": "ls_identity"}`)
17+
f.Add(`{"identityKey": "abc123"}`)
18+
f.Add(`{"limit": 10, "skip": 5}`)
19+
f.Add(`{"sortOrder": "asc"}`)
20+
f.Add(`{"sortOrder": "desc"}`)
21+
f.Add(`{"domain": "example.com", "service": "ls_payments", "limit": 10}`)
22+
23+
// Seed corpus with invalid/edge-case JSON
24+
f.Add(`{}`)
25+
f.Add(`null`)
26+
f.Add(`"findAll"`)
27+
f.Add(`{"domain": 123}`)
28+
f.Add(`{"service": 456}`)
29+
f.Add(`{"limit": -1}`)
30+
f.Add(`{"skip": -1}`)
31+
f.Add(`{"sortOrder": "invalid"}`)
32+
f.Add(`{"unknown_field": "value"}`)
33+
34+
// Seed corpus with edge cases
35+
f.Add(`{"limit": 0}`)
36+
f.Add(`{"skip": 0}`)
37+
f.Add(`{"domain": ""}`)
38+
f.Add(`{"service": ""}`)
39+
f.Add(`{"findAll": false}`)
40+
f.Add(`[1, 2, 3]`)
41+
f.Add(`true`)
42+
f.Add(`123`)
43+
44+
// Create a service instance with a mock storage
45+
service := &LookupService{
46+
storage: nil, // We don't need actual storage for this test
47+
}
48+
49+
f.Fuzz(func(t *testing.T, jsonStr string) {
50+
// First, try to unmarshal to ensure it's valid JSON
51+
var queryInterface interface{}
52+
err := json.Unmarshal([]byte(jsonStr), &queryInterface)
53+
if err != nil {
54+
// Invalid JSON should be rejected, but shouldn't panic
55+
return
56+
}
57+
58+
// Function should not panic on any input
59+
_, err = service.parseQueryObject(queryInterface)
60+
61+
// We don't validate the result or error, just ensure no panic occurs
62+
// Errors are expected for invalid query structures
63+
_ = err
64+
})
65+
}
66+
67+
// FuzzValidateQuerySLAP tests the validateQuery method with random query parameters.
68+
func FuzzValidateQuerySLAP(f *testing.F) {
69+
// Helper to create string pointer
70+
strPtr := func(s string) *string { return &s }
71+
intPtr := func(i int) *int { return &i }
72+
boolPtr := func(b bool) *bool { return &b }
73+
sortOrderPtr := func(s types.SortOrder) *types.SortOrder { return &s }
74+
75+
// Seed corpus with valid queries
76+
f.Add(true, "example.com", "ls_payments", "key123", 10, 0, "asc")
77+
f.Add(false, "", "", "", 0, 0, "desc")
78+
f.Add(false, "test.com", "ls_identity", "", 100, 50, "asc")
79+
80+
// Seed corpus with invalid queries
81+
f.Add(false, "", "", "", -1, 0, "asc") // negative limit
82+
f.Add(false, "", "", "", 0, -1, "asc") // negative skip
83+
f.Add(false, "", "", "", 0, 0, "invalid") // invalid sort order
84+
85+
// Create a service instance
86+
service := &LookupService{
87+
storage: nil,
88+
}
89+
90+
f.Fuzz(func(t *testing.T, findAll bool, domain, serviceName, identityKey string, limit, skip int, sortOrder string) {
91+
// Build a query object
92+
query := &types.SLAPQuery{}
93+
94+
if findAll {
95+
query.FindAll = boolPtr(findAll)
96+
}
97+
98+
if domain != "" {
99+
query.Domain = strPtr(domain)
100+
}
101+
102+
if serviceName != "" {
103+
query.Service = strPtr(serviceName)
104+
}
105+
106+
if identityKey != "" {
107+
query.IdentityKey = strPtr(identityKey)
108+
}
109+
110+
if limit != 0 {
111+
query.Limit = intPtr(limit)
112+
}
113+
114+
if skip != 0 {
115+
query.Skip = intPtr(skip)
116+
}
117+
118+
if sortOrder != "" {
119+
so := types.SortOrder(sortOrder)
120+
query.SortOrder = sortOrderPtr(so)
121+
}
122+
123+
// Function should not panic on any input
124+
err := service.validateQuery(query)
125+
126+
// We don't validate the error, just ensure no panic occurs
127+
_ = err
128+
})
129+
}
130+
131+
// FuzzQueryObjectRoundTrip tests JSON marshaling/unmarshaling of query objects.
132+
func FuzzQueryObjectRoundTrip(f *testing.F) {
133+
// Seed corpus with various JSON structures
134+
f.Add(`{"findAll": true}`)
135+
f.Add(`{"domain": "example.com", "service": "ls_payments"}`)
136+
f.Add(`{"limit": 10, "skip": 5, "sortOrder": "asc"}`)
137+
138+
f.Fuzz(func(t *testing.T, jsonStr string) {
139+
// Try to unmarshal into interface
140+
var queryInterface interface{}
141+
err := json.Unmarshal([]byte(jsonStr), &queryInterface)
142+
if err != nil {
143+
return
144+
}
145+
146+
// Try to marshal back to JSON
147+
jsonBytes, err := json.Marshal(queryInterface)
148+
if err != nil {
149+
t.Errorf("Failed to marshal query interface: %v", err)
150+
return
151+
}
152+
153+
// Try to unmarshal into SLAPQuery
154+
var slapQuery types.SLAPQuery
155+
err = json.Unmarshal(jsonBytes, &slapQuery)
156+
157+
// Function should not panic, errors are acceptable
158+
_ = err
159+
})
160+
}
161+
162+
// FuzzDomainString tests domain string validation edge cases.
163+
func FuzzDomainString(f *testing.F) {
164+
// Seed corpus with various domain formats
165+
f.Add("example.com")
166+
f.Add("sub.example.com")
167+
f.Add("example.com:8080")
168+
f.Add("192.168.1.1")
169+
f.Add("localhost")
170+
f.Add("[::1]")
171+
f.Add("")
172+
f.Add(".")
173+
f.Add(".example.com")
174+
f.Add("example.com.")
175+
f.Add("ex ample.com")
176+
f.Add("example..com")
177+
f.Add("example$.com")
178+
// Test very long domain name
179+
longDomain := ""
180+
for i := 0; i < 255; i++ {
181+
longDomain += "a"
182+
}
183+
f.Add(longDomain)
184+
185+
f.Fuzz(func(t *testing.T, domain string) {
186+
// Create a query with the fuzzed domain
187+
domainPtr := &domain
188+
query := &types.SLAPQuery{
189+
Domain: domainPtr,
190+
}
191+
192+
service := &LookupService{storage: nil}
193+
194+
// Function should not panic on any input
195+
err := service.validateQuery(query)
196+
197+
// We don't validate the error, just ensure no panic occurs
198+
_ = err
199+
})
200+
}
201+
202+
// FuzzServiceNameString tests service name string validation.
203+
func FuzzServiceNameString(f *testing.F) {
204+
// Seed corpus with various service name formats
205+
f.Add("ls_payments")
206+
f.Add("ls_identity")
207+
f.Add("tm_invalid") // topic manager prefix instead of lookup service
208+
f.Add("")
209+
f.Add("invalid")
210+
f.Add("ls_")
211+
f.Add("ls_UPPER")
212+
f.Add("ls_with_numbers123")
213+
f.Add("ls_special-chars")
214+
// Test very long service name
215+
longService := "ls_"
216+
for i := 0; i < 100; i++ {
217+
longService += "a"
218+
}
219+
f.Add(longService)
220+
221+
f.Fuzz(func(t *testing.T, serviceName string) {
222+
// Create a query with the fuzzed service name
223+
servicePtr := &serviceName
224+
query := &types.SLAPQuery{
225+
Service: servicePtr,
226+
}
227+
228+
service := &LookupService{storage: nil}
229+
230+
// Function should not panic on any input
231+
err := service.validateQuery(query)
232+
233+
// We don't validate the error, just ensure no panic occurs
234+
_ = err
235+
})
236+
}
237+
238+
// FuzzIdentityKeyString tests identity key string validation.
239+
func FuzzIdentityKeyString(f *testing.F) {
240+
// Seed corpus with various identity key formats
241+
f.Add("0123456789abcdef")
242+
f.Add("deadbeef")
243+
f.Add("")
244+
f.Add("not_hex")
245+
f.Add("0x1234")
246+
f.Add(string(make([]byte, 1000)))
247+
248+
f.Fuzz(func(t *testing.T, identityKey string) {
249+
// Create a query with the fuzzed identity key
250+
identityKeyPtr := &identityKey
251+
query := &types.SLAPQuery{
252+
IdentityKey: identityKeyPtr,
253+
}
254+
255+
service := &LookupService{storage: nil}
256+
257+
// Function should not panic on any input
258+
err := service.validateQuery(query)
259+
260+
// We don't validate the error, just ensure no panic occurs
261+
_ = err
262+
})
263+
}
264+
265+
// FuzzPaginationParameters tests pagination parameter validation.
266+
func FuzzPaginationParameters(f *testing.F) {
267+
// Seed corpus with various pagination values
268+
f.Add(0, 0)
269+
f.Add(10, 5)
270+
f.Add(100, 0)
271+
f.Add(1, 1000000)
272+
f.Add(-1, 0)
273+
f.Add(0, -1)
274+
f.Add(-100, -100)
275+
f.Add(2147483647, 2147483647) // Max int32
276+
277+
f.Fuzz(func(t *testing.T, limit, skip int) {
278+
// Create a query with the fuzzed pagination parameters
279+
limitPtr := &limit
280+
skipPtr := &skip
281+
query := &types.SLAPQuery{
282+
Limit: limitPtr,
283+
Skip: skipPtr,
284+
}
285+
286+
service := &LookupService{storage: nil}
287+
288+
// Function should not panic on any input
289+
err := service.validateQuery(query)
290+
291+
// We expect errors for negative values, but no panics
292+
_ = err
293+
})
294+
}

0 commit comments

Comments
 (0)