cli + tui to publish to leaflet (wip) & manage tasks, notes & watch/read lists 馃崈
charm
leaflet
readability
golang
1// shared test utilities & helpers
2package shared
3
4import (
5 "encoding/json"
6 "net"
7 "net/http"
8 "net/http/httptest"
9 "os"
10 "strings"
11 "testing"
12 "time"
13)
14
15func NewHTTPTestServer(t testing.TB, handler http.Handler) *httptest.Server {
16 t.Helper()
17 server, err := startHTTPTestServer(handler)
18 if err != nil {
19 t.Fatalf("failed to start HTTP test server: %v", err)
20 }
21 return server
22}
23
24func startHTTPTestServer(handler http.Handler) (*httptest.Server, error) {
25 ln, err := net.Listen("tcp4", "127.0.0.1:0")
26 if err != nil {
27 return nil, err
28 }
29 server := &httptest.Server{
30 Listener: ln,
31 Config: &http.Server{Handler: handler},
32 }
33 server.Start()
34 return server, nil
35}
36
37func CreateTempDir(p string, t *testing.T) (string, func()) {
38 t.Helper()
39 tempDir, err := os.MkdirTemp("", p)
40 if err != nil {
41 t.Fatalf("Failed to create temp directory: %v", err)
42 }
43 return tempDir, func() { os.RemoveAll(tempDir) }
44}
45
46func AssertNoError(t *testing.T, err error, msg string) {
47 t.Helper()
48 if err != nil {
49 t.Fatalf("%s: %v", msg, err)
50 }
51}
52
53func AssertError(t *testing.T, err error, msg string) {
54 t.Helper()
55 if err == nil {
56 t.Fatalf("%s: expected error but got none", msg)
57 }
58}
59
60// AssertErrorContains checks that an error occurred and optionally contains expected text
61func AssertErrorContains(t *testing.T, err error, expected, msg string) {
62 t.Helper()
63 if err == nil {
64 t.Errorf("%s: expected error but got none", msg)
65 return
66 }
67
68 if !ContainsString(err.Error(), expected) {
69 if msg == "" {
70 t.Errorf("expected error containing %q, got: %v", expected, err)
71 } else if expected != "" {
72 t.Errorf("%s: expected error containing %q, got: %v", msg, expected, err)
73 }
74 }
75}
76
77func AssertTrue(t *testing.T, condition bool, msg string) {
78 t.Helper()
79 if !condition {
80 t.Fatalf("%s: expected true", msg)
81 }
82}
83
84func AssertFalse(t *testing.T, condition bool, msg string) {
85 t.Helper()
86 if condition {
87 t.Fatalf("%s: expected false", msg)
88 }
89}
90
91func AssertContains(t *testing.T, str, substr, msg string) {
92 t.Helper()
93 if !strings.Contains(str, substr) {
94 t.Fatalf("%s: expected string '%s' to contain '%s'", msg, str, substr)
95 }
96}
97
98func AssertEqual[T comparable](t *testing.T, expected, actual T, msg string) {
99 t.Helper()
100 if expected != actual {
101 t.Fatalf("%s: expected %v, got %v", msg, expected, actual)
102 }
103}
104
105func AssertNotEqual[T comparable](t *testing.T, not, actual T, msg string) {
106 t.Helper()
107 if not == actual {
108 t.Fatalf("%s: expected value to not equal %v", msg, not)
109 }
110}
111
112func AssertNil(t *testing.T, value any, msg string) {
113 t.Helper()
114 if value != nil {
115 t.Fatalf("%s: expected nil, got %v", msg, value)
116 }
117}
118
119func AssertNotNil(t *testing.T, value any, msg string) {
120 t.Helper()
121 if value == nil {
122 t.Fatalf("%s: expected non-nil value", msg)
123 }
124}
125
126func AssertGreaterThan[T interface{ int | int64 | float64 }](t *testing.T, actual, threshold T, msg string) {
127 t.Helper()
128 if actual <= threshold {
129 t.Fatalf("%s: expected %v > %v", msg, actual, threshold)
130 }
131}
132
133func AssertLessThan[T interface{ int | int64 | float64 }](t *testing.T, actual, threshold T, msg string) {
134 t.Helper()
135 if actual >= threshold {
136 t.Fatalf("%s: expected %v < %v", msg, actual, threshold)
137 }
138}
139
140// Helper function to check if string contains substring (case-insensitive)
141func ContainsString(haystack, needle string) bool {
142 if needle == "" {
143 return true
144 }
145 return len(haystack) >= len(needle) &&
146 haystack[len(haystack)-len(needle):] == needle ||
147 haystack[:len(needle)] == needle ||
148 (len(haystack) > len(needle) &&
149 func() bool {
150 for i := 1; i <= len(haystack)-len(needle); i++ {
151 if haystack[i:i+len(needle)] == needle {
152 return true
153 }
154 }
155 return false
156 }())
157}
158
159// HTTPMockServer provides utilities for mocking HTTP services in tests
160type HTTPMockServer struct {
161 server *httptest.Server
162 requests []*http.Request
163}
164
165// NewMockServer creates a new mock HTTP server
166func NewMockServer() *HTTPMockServer {
167 mock := &HTTPMockServer{
168 requests: make([]*http.Request, 0),
169 }
170 return mock
171}
172
173// WithHandler sets up the mock server with a custom handler
174func (m *HTTPMockServer) WithHandler(handler http.HandlerFunc) *HTTPMockServer {
175 server, err := startHTTPTestServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
176 m.requests = append(m.requests, r)
177 handler(w, r)
178 }))
179 if err != nil {
180 panic(err)
181 }
182
183 m.server = server
184 return m
185}
186
187// URL returns the mock server URL
188func (m *HTTPMockServer) URL() string {
189 if m.server == nil {
190 panic("mock server not initialized - call WithHandler first")
191 }
192 return m.server.URL
193}
194
195// Close closes the mock server
196func (m *HTTPMockServer) Close() {
197 if m.server != nil {
198 m.server.Close()
199 }
200}
201
202// GetRequests returns all recorded HTTP requests
203func (m *HTTPMockServer) GetRequests() []*http.Request {
204 return m.requests
205}
206
207// GetLastRequest returns the last recorded HTTP request
208func (m *HTTPMockServer) GetLastRequest() *http.Request {
209 if len(m.requests) == 0 {
210 return nil
211 }
212 return m.requests[len(m.requests)-1]
213}
214
215func (m HTTPMockServer) Requests() []*http.Request {
216 return m.requests
217}
218
219// HTTPErrorMockServer creates a mock server that returns HTTP errors
220func HTTPErrorMockServer(statusCode int, message string) *HTTPMockServer {
221 return NewMockServer().WithHandler(func(w http.ResponseWriter, r *http.Request) {
222 http.Error(w, message, statusCode)
223 })
224}
225
226// JSONMockServer creates a mock server that returns JSON responses
227func JSONMockServer(response any) *HTTPMockServer {
228 return NewMockServer().WithHandler(func(w http.ResponseWriter, r *http.Request) {
229 w.Header().Set("Content-Type", "application/json")
230 if err := json.NewEncoder(w).Encode(response); err != nil {
231 http.Error(w, "Failed to encode response", http.StatusInternalServerError)
232 }
233 })
234}
235
236// TimeoutMockServer creates a mock server that simulates timeouts
237func TimeoutMockServer(delay time.Duration) *HTTPMockServer {
238 return NewMockServer().WithHandler(func(w http.ResponseWriter, r *http.Request) {
239 time.Sleep(delay)
240 w.WriteHeader(http.StatusOK)
241 })
242}
243
244// InvalidJSONMockServer creates a mock server that returns malformed JSON
245func InvalidJSONMockServer() *HTTPMockServer {
246 return NewMockServer().WithHandler(func(w http.ResponseWriter, r *http.Request) {
247 w.Header().Set("Content-Type", "application/json")
248 w.Write([]byte(`{"invalid": json`))
249 })
250}
251
252// EmptyResponseMockServer creates a mock server that returns empty responses
253func EmptyResponseMockServer() *HTTPMockServer {
254 return NewMockServer().WithHandler(func(w http.ResponseWriter, r *http.Request) {
255 w.WriteHeader(http.StatusOK)
256 })
257}
258
259// AssertRequestMade verifies that a request was made to the mock server
260func AssertRequestMade(t *testing.T, server *HTTPMockServer, expected string) {
261 t.Helper()
262 if len(server.requests) == 0 {
263 t.Error("Expected HTTP request to be made but none were recorded")
264 return
265 }
266
267 lastReq := server.GetLastRequest()
268 if lastReq.URL.Path != expected {
269 t.Errorf("Expected request to path %s, got %s", expected, lastReq.URL.Path)
270 }
271}