Openstatus
www.openstatus.dev
1package handlers_test
2
3import (
4 "encoding/json"
5 "fmt"
6 "io"
7 "net/http"
8 "net/http/httptest"
9 "strings"
10 "testing"
11
12 "github.com/gin-gonic/gin"
13 "github.com/openstatushq/openstatus/apps/checker/checker"
14 "github.com/openstatushq/openstatus/apps/checker/handlers"
15
16 "github.com/openstatushq/openstatus/apps/checker/pkg/tinybird"
17 "github.com/openstatushq/openstatus/apps/checker/request"
18 "github.com/stretchr/testify/assert"
19)
20
21func TestHandler_HTTPCheckerHandler(t *testing.T) {
22 hclient := &http.Client{Transport: RoundTripFunc(func(req *http.Request) *http.Response {
23 return &http.Response{
24 StatusCode: http.StatusAccepted,
25 Body: io.NopCloser(strings.NewReader(`Status Accepted`)),
26 }
27 })}
28 client := tinybird.NewClient(hclient, "apiKey")
29
30 t.Run("it should return 401 if there's no auth", func(t *testing.T) {
31
32 region := "local"
33 h := handlers.Handler{
34 TbClient: client,
35 Secret: "",
36 CloudProvider: "fly",
37 Region: region,
38 }
39 router := gin.New()
40 router.POST("/checker/:region", h.HTTPCheckerHandler)
41
42 w := httptest.NewRecorder()
43
44 data := request.HttpCheckerRequest{
45 URL: "https://www.openstatus.dev",
46 }
47 dataJson, _ := json.Marshal(data)
48 req, _ := http.NewRequest(http.MethodPost, "/checker/"+region, strings.NewReader(string(dataJson)))
49 router.ServeHTTP(w, req)
50
51 assert.Equal(t, 401, w.Code)
52 })
53
54 t.Run("it should return 400 if the payload is not ok", func(t *testing.T) {
55 region := "local"
56
57 h := handlers.Handler{
58 TbClient: client,
59 Secret: "test",
60 CloudProvider: "fly",
61 Region: region,
62 }
63 router := gin.New()
64 router.POST("/checker/:region", h.HTTPCheckerHandler)
65
66 w := httptest.NewRecorder()
67
68 data := request.PingRequest{
69 URL: "https://www.openstatus.dev",
70 }
71 dataJson, _ := json.Marshal(data)
72 req, _ := http.NewRequest(http.MethodPost, "/checker/"+region, strings.NewReader(string(dataJson)))
73 req.Header.Set("Authorization", "Basic test")
74 router.ServeHTTP(w, req)
75
76 assert.Equal(t, 400, w.Code)
77 assert.Contains(t, w.Body.String(), "{\"error\":\"invalid request\"}")
78 })
79
80 t.Run("it should return 200 if the payload is not ok", func(t *testing.T) {
81 region := "local"
82
83 httptest.NewRequest(http.MethodGet, "http://www.openstatus.dev", nil)
84 httptest.NewRecorder()
85
86 h := handlers.Handler{
87 TbClient: client,
88 Secret: "test",
89 CloudProvider: "fly",
90 Region: region,
91 }
92 router := gin.New()
93 router.POST("/checker/:region", h.HTTPCheckerHandler)
94
95 w := httptest.NewRecorder()
96
97 data := request.HttpCheckerRequest{
98 URL: "https://www.openstatus.dev",
99 Method: "GET",
100 Body: "",
101 }
102 dataJson, _ := json.Marshal(data)
103 req, _ := http.NewRequest(http.MethodPost, "/checker/"+region, strings.NewReader(string(dataJson)))
104 req.Header.Set("Authorization", "Basic test")
105 router.ServeHTTP(w, req)
106
107 assert.Equal(t, 200, w.Code)
108 fmt.Println(w.Body.String())
109 })
110}
111
112func TestEvaluateAssertions_raw(t *testing.T) {
113 // Helper to marshal assertion
114 marshal := func(a any) json.RawMessage {
115 b, _ := json.Marshal(a)
116 return b
117 }
118
119 // Success if no assertions and status code is 200
120 t.Run("no assertions, status code 200", func(t *testing.T) {
121 raw := []json.RawMessage{}
122 data := handlers.PingData{}
123 res := checker.Response{Status: 200}
124 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
125 assert.True(t, ok)
126 assert.NoError(t, err)
127 })
128
129 // Header assertion success
130 t.Run("header assertion success", func(t *testing.T) {
131 assertion := request.Assertion{AssertionType: request.AssertionHeader}
132 target := struct {
133 request.Assertion
134 Comparator request.StringComparator `json:"compare"`
135 Key string `json:"key"`
136 Target string `json:"target"`
137 }{
138 assertion,
139 request.StringContains,
140 "X-Test",
141 "ok",
142 }
143 rawMsg := marshal(target)
144 raw := []json.RawMessage{rawMsg}
145 data := handlers.PingData{Headers: `{"X-Test":"ok-value"}`}
146 res := checker.Response{Status: 200}
147
148 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
149 assert.True(t, ok)
150 assert.NoError(t, err)
151 })
152
153 t.Run("header assertion failed", func(t *testing.T) {
154 assertion := request.Assertion{AssertionType: request.AssertionHeader}
155 target := struct {
156 request.Assertion
157 Comparator request.StringComparator `json:"compare"`
158 Key string `json:"key"`
159 Target string `json:"target"`
160 }{
161 assertion,
162 request.StringContains,
163 "X-Test",
164 "not-ok",
165 }
166 rawMsg := marshal(target)
167 raw := []json.RawMessage{rawMsg}
168 data := handlers.PingData{Headers: `{"X-Test":"ok-value"}`}
169 res := checker.Response{Status: 200}
170
171 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
172 assert.False(t, ok)
173 assert.NoError(t, err)
174 })
175
176 // Text body assertion failure
177 t.Run("text body assertion failure", func(t *testing.T) {
178 assertion := request.Assertion{AssertionType: request.AssertionTextBody}
179 target := struct {
180 request.Assertion
181 Comparator request.StringComparator `json:"compare"`
182 Target string `json:"target"`
183 }{
184 assertion,
185 request.StringEquals,
186 "fail",
187 }
188 rawMsg := marshal(target)
189 raw := []json.RawMessage{rawMsg}
190 data := handlers.PingData{Body: "ok"}
191 res := checker.Response{Status: 200}
192
193 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
194 assert.False(t, ok)
195 assert.NoError(t, err)
196 })
197
198 // Text body assertion failure
199 t.Run("text body assertion success", func(t *testing.T) {
200 assertion := request.Assertion{AssertionType: request.AssertionTextBody}
201 target := struct {
202 request.Assertion
203 Comparator request.StringComparator `json:"compare"`
204 Target string `json:"target"`
205 }{
206 assertion,
207 request.StringEquals,
208 "success",
209 }
210 rawMsg := marshal(target)
211 raw := []json.RawMessage{rawMsg}
212 data := handlers.PingData{Body: "success"}
213 res := checker.Response{Status: 200}
214
215 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
216 assert.True(t, ok)
217 assert.NoError(t, err)
218 })
219 // Status assertion success
220 t.Run("status assertion success", func(t *testing.T) {
221 assertion := request.Assertion{AssertionType: request.AssertionStatus}
222 target := struct {
223 request.Assertion
224 Comparator request.NumberComparator `json:"compare"`
225 Target int64 `json:"target"`
226 }{
227 assertion,
228 request.NumberEquals,
229 200,
230 }
231 rawMsg := marshal(target)
232 raw := []json.RawMessage{rawMsg}
233 data := handlers.PingData{}
234 res := checker.Response{Status: 200}
235
236 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
237 assert.True(t, ok)
238 assert.NoError(t, err)
239 })
240
241 // Malformed assertion
242 t.Run("malformed assertion", func(t *testing.T) {
243 raw := []json.RawMessage{[]byte(`{not valid json}`)}
244 data := handlers.PingData{}
245 res := checker.Response{Status: 200}
246 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
247 assert.False(t, ok)
248 assert.Error(t, err)
249 })
250
251 // Unknown assertion type
252 t.Run("unknown assertion type", func(t *testing.T) {
253 assertion := request.Assertion{AssertionType: "unknown"}
254 rawMsg := marshal(assertion)
255 raw := []json.RawMessage{rawMsg}
256 data := handlers.PingData{}
257 res := checker.Response{Status: 200}
258 ok, err := handlers.EvaluateHTTPAssertions(raw, data, res)
259 assert.True(t, ok) // Should not fail, just skip
260 assert.NoError(t, err)
261 })
262}