Skip to content

Commit 32fc1ac

Browse files
committed
test(validation): add fuzz tests for URI validation functions
1 parent d590a5d commit 32fc1ac

File tree

1 file changed

+192
-0
lines changed

1 file changed

+192
-0
lines changed

pkg/utils/validation_fuzz_test.go

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
// Package utils provides utility functions for validating URIs and service names.
2+
package utils
3+
4+
import (
5+
"testing"
6+
)
7+
8+
// FuzzIsAdvertisableURI tests the IsAdvertisableURI function with random inputs
9+
// to ensure it never panics and handles all edge cases gracefully.
10+
func FuzzIsAdvertisableURI(f *testing.F) {
11+
// Seed corpus with valid examples from different schemes
12+
f.Add("https://example.com/")
13+
f.Add("https+bsvauth://example.com/")
14+
f.Add("https+bsvauth+smf://example.com/")
15+
f.Add("https+bsvauth+scrypt-offchain://example.com/")
16+
f.Add("https+rtt://example.com/")
17+
f.Add("wss://example.com")
18+
f.Add("js8c+bsvauth+smf:?lat=40.7128&long=-74.0060&freq=7.078&radius=100")
19+
20+
// Seed corpus with invalid examples
21+
f.Add("")
22+
f.Add(" ")
23+
f.Add("http://example.com")
24+
f.Add("https://localhost/")
25+
f.Add("https://example.com/path")
26+
f.Add("ftp://example.com")
27+
f.Add("js8c+bsvauth+smf:")
28+
f.Add("js8c+bsvauth+smf:?lat=91&long=0&freq=1&radius=1")
29+
30+
// Seed corpus with edge cases
31+
f.Add("https://")
32+
f.Add("://example.com")
33+
f.Add("https://example.com:99999/")
34+
f.Add("wss://")
35+
f.Add("js8c+bsvauth+smf:?")
36+
f.Add("https://[::1]/")
37+
f.Add("https://192.168.1.1/")
38+
39+
f.Fuzz(func(t *testing.T, uri string) {
40+
// Function should not panic on any input
41+
result := IsAdvertisableURI(uri)
42+
43+
// If result is true, verify it's actually a valid URI format
44+
// (we can't validate full correctness here, but we can ensure consistency)
45+
if result {
46+
// Valid URIs should not be empty or whitespace-only
47+
if uri == "" || len(uri) < 4 {
48+
t.Errorf("IsAdvertisableURI returned true for invalid short URI: %q", uri)
49+
}
50+
}
51+
})
52+
}
53+
54+
// FuzzIsValidTopicOrServiceName tests the IsValidTopicOrServiceName function
55+
// with random inputs to ensure it handles all edge cases without panicking.
56+
func FuzzIsValidTopicOrServiceName(f *testing.F) {
57+
// Seed corpus with valid topic names
58+
f.Add("tm_payments")
59+
f.Add("tm_chat_messages")
60+
f.Add("tm_identity_verification_service")
61+
f.Add("tm_a")
62+
f.Add("ls_payments")
63+
f.Add("ls_identity_verification")
64+
65+
// Seed corpus with invalid examples
66+
f.Add("")
67+
f.Add("tm_")
68+
f.Add("ls_")
69+
f.Add("payments")
70+
f.Add("TM_payments")
71+
f.Add("tm_Payments")
72+
f.Add("tm_payments123")
73+
f.Add("tm_payments-special")
74+
f.Add("tm_payments__double")
75+
f.Add("tm_payments_")
76+
f.Add("tm__payments")
77+
78+
// Seed corpus with edge cases
79+
f.Add("t")
80+
f.Add("tm")
81+
f.Add("ls")
82+
f.Add("sv_payments")
83+
// exactly 50 chars
84+
f.Add("tm_" + string(make([]byte, 48)))
85+
// too long
86+
f.Add("tm_" + string(make([]byte, 100)))
87+
88+
f.Fuzz(func(t *testing.T, name string) {
89+
// Function should not panic on any input
90+
result := IsValidTopicOrServiceName(name)
91+
92+
// If result is true, verify basic constraints
93+
if result {
94+
// Valid names must be within length constraints
95+
if len(name) < 1 || len(name) > 50 {
96+
t.Errorf("IsValidTopicOrServiceName returned true for invalid length: %d (name: %q)", len(name), name)
97+
}
98+
99+
// Valid names must start with tm_ or ls_
100+
if len(name) >= 3 {
101+
prefix := name[:3]
102+
if prefix != "tm_" && prefix != "ls_" {
103+
t.Errorf("IsValidTopicOrServiceName returned true for invalid prefix: %q (name: %q)", prefix, name)
104+
}
105+
}
106+
}
107+
})
108+
}
109+
110+
// FuzzValidateCustomHTTPSURI tests the validateCustomHTTPSURI function
111+
// with random inputs to ensure robustness.
112+
func FuzzValidateCustomHTTPSURI(f *testing.F) {
113+
// Seed corpus with valid examples
114+
f.Add("custom://example.com/", "custom://")
115+
f.Add("https://example.com/", "https://")
116+
f.Add("https+bsvauth://example.com/", "https+bsvauth://")
117+
118+
// Seed corpus with invalid examples
119+
f.Add("custom://localhost/", "custom://")
120+
f.Add("custom://example.com/path", "custom://")
121+
f.Add("custom://[invalid", "custom://")
122+
123+
// Seed corpus with edge cases
124+
f.Add("custom://", "custom://")
125+
f.Add("://example.com/", "://")
126+
f.Add("custom://192.168.1.1/", "custom://")
127+
f.Add("custom://[::1]/", "custom://")
128+
129+
f.Fuzz(func(t *testing.T, uri, prefix string) {
130+
// Function should not panic on any input
131+
_ = validateCustomHTTPSURI(uri, prefix)
132+
// We don't validate the result as this is an internal function
133+
// The main goal is to ensure it doesn't panic
134+
})
135+
}
136+
137+
// FuzzValidateWSSURI tests the validateWSSURI function with random inputs.
138+
func FuzzValidateWSSURI(f *testing.F) {
139+
// Seed corpus with valid examples
140+
f.Add("wss://example.com")
141+
f.Add("wss://example.com:443")
142+
f.Add("wss://example.com/path")
143+
144+
// Seed corpus with invalid examples
145+
f.Add("wss://localhost")
146+
f.Add("ws://example.com")
147+
f.Add("wss://[invalid")
148+
f.Add("")
149+
150+
// Seed corpus with edge cases
151+
f.Add("wss://")
152+
f.Add("wss://192.168.1.1")
153+
f.Add("wss://[::1]")
154+
f.Add("wss://example.com:99999")
155+
156+
f.Fuzz(func(t *testing.T, uri string) {
157+
// Function should not panic on any input
158+
_ = validateWSSURI(uri)
159+
// We don't validate the result as this is an internal function
160+
// The main goal is to ensure it doesn't panic
161+
})
162+
}
163+
164+
// FuzzValidateJS8CallURI tests the validateJS8CallURI function with random inputs.
165+
func FuzzValidateJS8CallURI(f *testing.F) {
166+
// Seed corpus with valid examples
167+
f.Add("js8c+bsvauth+smf:?lat=40.7128&long=-74.0060&freq=7.078&radius=100")
168+
f.Add("js8c+bsvauth+smf:?lat=0&long=0&freq=1&radius=1")
169+
f.Add("js8c+bsvauth+smf:?lat=90&long=180&freq=7.078MHz&radius=100km")
170+
f.Add("js8c+bsvauth+smf:?lat=-90&long=-180&freq=7.078&radius=100.5")
171+
172+
// Seed corpus with invalid examples
173+
f.Add("js8c+bsvauth+smf:")
174+
f.Add("js8c+bsvauth+smf:?lat=91&long=0&freq=1&radius=1")
175+
f.Add("js8c+bsvauth+smf:?lat=0&long=181&freq=1&radius=1")
176+
f.Add("js8c+bsvauth+smf:?lat=0&long=0&freq=0&radius=1")
177+
f.Add("js8c+bsvauth+smf:?lat=0&long=0&freq=1&radius=0")
178+
f.Add("js8c+bsvauth+smf:?lat=0&long=0&freq=-1&radius=1")
179+
180+
// Seed corpus with edge cases
181+
f.Add("js8c+bsvauth+smf:?")
182+
f.Add("js8c+bsvauth+smf:?lat=&long=&freq=&radius=")
183+
f.Add("js8c+bsvauth+smf:?lat=invalid&long=invalid&freq=invalid&radius=invalid")
184+
f.Add("js8c+bsvauth+smf:?lat=0&long=0&freq=abc&radius=xyz")
185+
186+
f.Fuzz(func(t *testing.T, uri string) {
187+
// Function should not panic on any input
188+
_ = validateJS8CallURI(uri)
189+
// We don't validate the result as this is an internal function
190+
// The main goal is to ensure it doesn't panic
191+
})
192+
}

0 commit comments

Comments
 (0)