|
1 | 1 | package release |
2 | 2 |
|
3 | 3 | import ( |
| 4 | + "errors" |
4 | 5 | "fmt" |
| 6 | + "io" |
5 | 7 | "net/http" |
6 | 8 | "net/http/httptest" |
| 9 | + "strings" |
7 | 10 | "sync" |
8 | 11 | "testing" |
9 | 12 | "time" |
@@ -191,3 +194,118 @@ type roundTripFunc func(*http.Request) (*http.Response, error) |
191 | 194 | func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) { |
192 | 195 | return f(req) |
193 | 196 | } |
| 197 | + |
| 198 | +// TestCheckReleaseTriggerUsesDefaultAPIBase verifies that an |
| 199 | +// empty APIBaseURL falls back to api.github.com (line covered: |
| 200 | +// the apiBase == "" branch). |
| 201 | +func TestCheckReleaseTriggerUsesDefaultAPIBase(t *testing.T) { |
| 202 | + var gotURL string |
| 203 | + client := &http.Client{Transport: roundTripFunc(func(r *http.Request) (*http.Response, error) { |
| 204 | + gotURL = r.URL.String() |
| 205 | + return &http.Response{ |
| 206 | + StatusCode: http.StatusOK, |
| 207 | + Body: io.NopCloser(strings.NewReader(`{"draft":true}`)), |
| 208 | + Header: make(http.Header), |
| 209 | + }, nil |
| 210 | + })} |
| 211 | + res, err := CheckReleaseTrigger(TriggerGuardOptions{ |
| 212 | + EventName: "create", |
| 213 | + Repository: "jeduden/mdsmith", |
| 214 | + RefType: "tag", |
| 215 | + RefName: "v1.2.3", |
| 216 | + Token: "t", |
| 217 | + Client: client, |
| 218 | + }) |
| 219 | + require.NoError(t, err) |
| 220 | + assert.True(t, res.ShouldRun) |
| 221 | + assert.Equal(t, "https://api.github.com/repos/jeduden/mdsmith/releases/tags/v1.2.3", gotURL) |
| 222 | +} |
| 223 | + |
| 224 | +// TestCheckReleaseTriggerNewRequestError trips http.NewRequest by |
| 225 | +// passing an APIBaseURL containing a control character so the URL |
| 226 | +// parser rejects it. |
| 227 | +func TestCheckReleaseTriggerNewRequestError(t *testing.T) { |
| 228 | + _, err := CheckReleaseTrigger(TriggerGuardOptions{ |
| 229 | + EventName: "create", |
| 230 | + Repository: "jeduden/mdsmith", |
| 231 | + RefType: "tag", |
| 232 | + RefName: "v1.2.3", |
| 233 | + Token: "t", |
| 234 | + APIBaseURL: "http://example.com\x7f", |
| 235 | + }) |
| 236 | + require.Error(t, err) |
| 237 | +} |
| 238 | + |
| 239 | +// TestCheckReleaseTriggerClientDoError propagates a transport |
| 240 | +// error from client.Do. |
| 241 | +func TestCheckReleaseTriggerClientDoError(t *testing.T) { |
| 242 | + sentinel := errors.New("transport boom") |
| 243 | + client := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { |
| 244 | + return nil, sentinel |
| 245 | + })} |
| 246 | + _, err := CheckReleaseTrigger(TriggerGuardOptions{ |
| 247 | + EventName: "create", |
| 248 | + Repository: "jeduden/mdsmith", |
| 249 | + RefType: "tag", |
| 250 | + RefName: "v1.2.3", |
| 251 | + Token: "t", |
| 252 | + APIBaseURL: "https://api.example.com", |
| 253 | + Client: client, |
| 254 | + }) |
| 255 | + require.ErrorIs(t, err, sentinel) |
| 256 | +} |
| 257 | + |
| 258 | +type errReadCloser struct{ err error } |
| 259 | + |
| 260 | +func (e errReadCloser) Read([]byte) (int, error) { return 0, e.err } |
| 261 | +func (e errReadCloser) Close() error { return nil } |
| 262 | + |
| 263 | +// TestCheckReleaseTriggerReadBodyError covers the io.ReadAll |
| 264 | +// error branch of lookupReleaseDraft. |
| 265 | +func TestCheckReleaseTriggerReadBodyError(t *testing.T) { |
| 266 | + sentinel := errors.New("read boom") |
| 267 | + client := &http.Client{Transport: roundTripFunc(func(*http.Request) (*http.Response, error) { |
| 268 | + return &http.Response{ |
| 269 | + StatusCode: http.StatusOK, |
| 270 | + Body: errReadCloser{err: sentinel}, |
| 271 | + Header: make(http.Header), |
| 272 | + }, nil |
| 273 | + })} |
| 274 | + _, err := CheckReleaseTrigger(TriggerGuardOptions{ |
| 275 | + EventName: "create", |
| 276 | + Repository: "jeduden/mdsmith", |
| 277 | + RefType: "tag", |
| 278 | + RefName: "v1.2.3", |
| 279 | + Token: "t", |
| 280 | + APIBaseURL: "https://api.example.com", |
| 281 | + Client: client, |
| 282 | + }) |
| 283 | + require.ErrorIs(t, err, sentinel) |
| 284 | +} |
| 285 | + |
| 286 | +// TestCheckReleaseTriggerInvalidJSONErrors covers the |
| 287 | +// json.Unmarshal error branch of lookupReleaseDraft. |
| 288 | +func TestCheckReleaseTriggerInvalidJSONErrors(t *testing.T) { |
| 289 | + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { |
| 290 | + _, _ = fmt.Fprint(w, `{not json`) |
| 291 | + })) |
| 292 | + t.Cleanup(srv.Close) |
| 293 | + |
| 294 | + _, err := CheckReleaseTrigger(TriggerGuardOptions{ |
| 295 | + EventName: "create", |
| 296 | + Repository: "jeduden/mdsmith", |
| 297 | + RefType: "tag", |
| 298 | + RefName: "v1.2.3", |
| 299 | + Token: "t", |
| 300 | + APIBaseURL: srv.URL, |
| 301 | + }) |
| 302 | + require.Error(t, err) |
| 303 | + assert.Contains(t, err.Error(), "parse ") |
| 304 | +} |
| 305 | + |
| 306 | +// TestReleaseLookupErrorEmptyBody pins the empty-body formatting |
| 307 | +// branch of releaseLookupError.Error. |
| 308 | +func TestReleaseLookupErrorEmptyBody(t *testing.T) { |
| 309 | + e := &releaseLookupError{URL: "https://x/y", StatusCode: 503} |
| 310 | + assert.Equal(t, "lookup https://x/y: unexpected GitHub API status 503", e.Error()) |
| 311 | +} |
0 commit comments