fix: resolve all 47 noctx lint findings in tests
All checks were successful
Check / check (pull_request) Successful in 3m10s
All checks were successful
Check / check (pull_request) Successful in 3m10s
Replace every httptest.NewRequest call with httptest.NewRequestWithContext using the test's t.Context(). Thread t *testing.T through the createSetupFormRequest and createLoginFormRequest helpers so they can supply a context. make lint under golangci-lint 2.12.2 drops from 94 findings to 47 (remaining: 23 gosec, 24 goconst, tracked in #176/#177/#178). make test and make fmt-check pass unchanged. Closes #175
This commit is contained in:
28
TODO.md
28
TODO.md
@@ -10,18 +10,23 @@
|
|||||||
|
|
||||||
# Status
|
# Status
|
||||||
|
|
||||||
1.0+. Tagged 1.0.0 on 2026-02-26; 8 commits on main since. Policy
|
1.0+. Tagged 1.0.0 on 2026-02-26. Policy violation: main currently
|
||||||
violation: main currently fails make check (91 lint issues), so the tree
|
fails make check under golangci-lint >= 2.12 (47 lint issues remaining:
|
||||||
is out of compliance until fixed.
|
23 gosec, 24 goconst), so the tree is out of compliance until fixed. CI
|
||||||
|
(Dockerfile lint stage, pinned golangci-lint v2.10.1) is green; the pin
|
||||||
|
bump is tracked in issue #179. The road to release 1.1.0 is tracked in
|
||||||
|
Gitea issues #175-#182 (milestone 1.1.0).
|
||||||
|
|
||||||
# Next Step
|
# Next Step
|
||||||
|
|
||||||
Fix the 47 noctx lint findings (HTTP requests without context) in one
|
Fix the 22 gosec G710 open-redirect findings in
|
||||||
commit and confirm the count drops under make check. This is the largest
|
internal/handlers/app.go (issue #176) by validating app IDs in a
|
||||||
of the three lint classes blocking a green main.
|
shared redirect helper.
|
||||||
|
|
||||||
# Completed Steps
|
# Completed Steps
|
||||||
|
|
||||||
|
- 2026-08-07: Fixed all 47 noctx lint findings: tests now use
|
||||||
|
httptest.NewRequestWithContext with t.Context() (#175).
|
||||||
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
- 2026-07-07 Adopted scripts-to-rule-them-all: `script/` entrypoints,
|
||||||
Makefile shims, README Entrypoints section
|
Makefile shims, README Entrypoints section
|
||||||
- 2026-03-11: Monolithic env var editing with bulk save (#158).
|
- 2026-03-11: Monolithic env var editing with bulk save (#158).
|
||||||
@@ -45,11 +50,14 @@ of the three lint classes blocking a green main.
|
|||||||
# Future Steps
|
# Future Steps
|
||||||
|
|
||||||
- Get main green (compliance, ordered):
|
- Get main green (compliance, ordered):
|
||||||
- Fix 47 noctx findings (Next Step).
|
- Fix 22 gosec G710 findings (Next Step, #176).
|
||||||
- Fix 23 gosec findings.
|
- Fix 1 gosec G703 finding (#177).
|
||||||
- Fix 21 goconst findings.
|
- Fix 24 goconst findings (#178).
|
||||||
|
- Bump Dockerfile golangci-lint pin to v2.12.x (#179).
|
||||||
- Run make check clean on main and keep it green; main must always
|
- Run make check clean on main and keep it green; main must always
|
||||||
pass.
|
pass.
|
||||||
- Confirm .gitea/workflows/check.yml gates merges on make check so main
|
- Confirm .gitea/workflows/check.yml gates merges on make check so main
|
||||||
cannot regress.
|
cannot regress (#180).
|
||||||
|
- Deploy to fsn1app1 and verify end-to-end (#181), then tag 1.1.0
|
||||||
|
(#182).
|
||||||
- Resume feature work only after main is green.
|
- Resume feature work only after main is green.
|
||||||
|
|||||||
@@ -47,7 +47,12 @@ func setupAPITest(t *testing.T) (*testContext, []*http.Cookie) {
|
|||||||
r := apiRouter(tc)
|
r := apiRouter(tc)
|
||||||
|
|
||||||
loginBody := `{"username":"admin","password":"password123"}`
|
loginBody := `{"username":"admin","password":"password123"}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/login", strings.NewReader(loginBody))
|
req := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
|
http.MethodPost,
|
||||||
|
"/api/v1/login",
|
||||||
|
strings.NewReader(loginBody),
|
||||||
|
)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -70,7 +75,7 @@ func apiGet(
|
|||||||
) *httptest.ResponseRecorder {
|
) *httptest.ResponseRecorder {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, path, nil)
|
||||||
|
|
||||||
for _, c := range cookies {
|
for _, c := range cookies {
|
||||||
req.AddCookie(c)
|
req.AddCookie(c)
|
||||||
@@ -95,7 +100,12 @@ func TestAPILoginSuccess(t *testing.T) {
|
|||||||
r := apiRouter(tc)
|
r := apiRouter(tc)
|
||||||
|
|
||||||
body := `{"username":"admin","password":"password123"}`
|
body := `{"username":"admin","password":"password123"}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/login", strings.NewReader(body))
|
req := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
|
http.MethodPost,
|
||||||
|
"/api/v1/login",
|
||||||
|
strings.NewReader(body),
|
||||||
|
)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -122,7 +132,12 @@ func TestAPILoginInvalidCredentials(t *testing.T) {
|
|||||||
r := apiRouter(tc)
|
r := apiRouter(tc)
|
||||||
|
|
||||||
body := `{"username":"admin","password":"wrong"}`
|
body := `{"username":"admin","password":"wrong"}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/login", strings.NewReader(body))
|
req := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
|
http.MethodPost,
|
||||||
|
"/api/v1/login",
|
||||||
|
strings.NewReader(body),
|
||||||
|
)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -139,7 +154,12 @@ func TestAPILoginMissingFields(t *testing.T) {
|
|||||||
r := apiRouter(tc)
|
r := apiRouter(tc)
|
||||||
|
|
||||||
body := `{"username":"","password":""}`
|
body := `{"username":"","password":""}`
|
||||||
req := httptest.NewRequest(http.MethodPost, "/api/v1/login", strings.NewReader(body))
|
req := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
|
http.MethodPost,
|
||||||
|
"/api/v1/login",
|
||||||
|
strings.NewReader(body),
|
||||||
|
)
|
||||||
req.Header.Set("Content-Type", "application/json")
|
req.Header.Set("Content-Type", "application/json")
|
||||||
|
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
@@ -155,7 +175,9 @@ func TestAPIRejectsUnauthenticated(t *testing.T) {
|
|||||||
|
|
||||||
r := apiRouter(tc)
|
r := apiRouter(tc)
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/api/v1/apps", nil)
|
req := httptest.NewRequestWithContext(
|
||||||
|
t.Context(), http.MethodGet, "/api/v1/apps", nil,
|
||||||
|
)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
r.ServeHTTP(rr, req)
|
r.ServeHTTP(rr, req)
|
||||||
|
|
||||||
|
|||||||
@@ -193,7 +193,8 @@ func TestHandleHealthCheck(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodGet,
|
http.MethodGet,
|
||||||
"/.well-known/healthcheck.json",
|
"/.well-known/healthcheck.json",
|
||||||
nil,
|
nil,
|
||||||
@@ -218,7 +219,7 @@ func TestHandleSetupGET(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/setup", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/setup", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupGET()
|
handler := testCtx.handlers.HandleSetupGET()
|
||||||
@@ -230,14 +231,18 @@ func TestHandleSetupGET(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func createSetupFormRequest(
|
func createSetupFormRequest(
|
||||||
|
t *testing.T,
|
||||||
username, password, confirm string,
|
username, password, confirm string,
|
||||||
) *http.Request {
|
) *http.Request {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Set("username", username)
|
form.Set("username", username)
|
||||||
form.Set("password", password)
|
form.Set("password", password)
|
||||||
form.Set("password_confirm", confirm)
|
form.Set("password_confirm", confirm)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/setup",
|
"/setup",
|
||||||
strings.NewReader(form.Encode()),
|
strings.NewReader(form.Encode()),
|
||||||
@@ -252,7 +257,7 @@ func TestHandleSetupPOSTCreatesUserAndRedirects(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := createSetupFormRequest("admin", "password123", "password123")
|
request := createSetupFormRequest(t, "admin", "password123", "password123")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupPOST()
|
handler := testCtx.handlers.HandleSetupPOST()
|
||||||
@@ -267,7 +272,7 @@ func TestHandleSetupPOSTRejectsEmptyUsername(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := createSetupFormRequest("", "password123", "password123")
|
request := createSetupFormRequest(t, "", "password123", "password123")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupPOST()
|
handler := testCtx.handlers.HandleSetupPOST()
|
||||||
@@ -282,7 +287,7 @@ func TestHandleSetupPOSTRejectsShortPassword(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := createSetupFormRequest("admin", "short", "short")
|
request := createSetupFormRequest(t, "admin", "short", "short")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupPOST()
|
handler := testCtx.handlers.HandleSetupPOST()
|
||||||
@@ -297,7 +302,7 @@ func TestHandleSetupPOSTRejectsMismatchedPasswords(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := createSetupFormRequest("admin", "password123", "different123")
|
request := createSetupFormRequest(t, "admin", "password123", "different123")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupPOST()
|
handler := testCtx.handlers.HandleSetupPOST()
|
||||||
@@ -315,7 +320,7 @@ func TestHandleLoginGET(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/login", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/login", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleLoginGET()
|
handler := testCtx.handlers.HandleLoginGET()
|
||||||
@@ -326,12 +331,15 @@ func TestHandleLoginGET(t *testing.T) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func createLoginFormRequest(username, password string) *http.Request {
|
func createLoginFormRequest(t *testing.T, username, password string) *http.Request {
|
||||||
|
t.Helper()
|
||||||
|
|
||||||
form := url.Values{}
|
form := url.Values{}
|
||||||
form.Set("username", username)
|
form.Set("username", username)
|
||||||
form.Set("password", password)
|
form.Set("password", password)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/login",
|
"/login",
|
||||||
strings.NewReader(form.Encode()),
|
strings.NewReader(form.Encode()),
|
||||||
@@ -354,7 +362,7 @@ func TestHandleLoginPOSTAuthenticatesValidCredentials(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, createErr)
|
require.NoError(t, createErr)
|
||||||
|
|
||||||
request := createLoginFormRequest("testuser", "testpass123")
|
request := createLoginFormRequest(t, "testuser", "testpass123")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleLoginPOST()
|
handler := testCtx.handlers.HandleLoginPOST()
|
||||||
@@ -377,7 +385,7 @@ func TestHandleLoginPOSTRejectsInvalidCredentials(t *testing.T) {
|
|||||||
)
|
)
|
||||||
require.NoError(t, createErr)
|
require.NoError(t, createErr)
|
||||||
|
|
||||||
request := createLoginFormRequest("testuser", "wrongpassword")
|
request := createLoginFormRequest(t, "testuser", "wrongpassword")
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleLoginPOST()
|
handler := testCtx.handlers.HandleLoginPOST()
|
||||||
@@ -395,7 +403,7 @@ func TestHandleDashboard(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleDashboard()
|
handler := testCtx.handlers.HandleDashboard()
|
||||||
@@ -413,7 +421,7 @@ func TestHandleDashboard(t *testing.T) {
|
|||||||
// Create an app so the template iterates over AppStats and hits .CSRFField
|
// Create an app so the template iterates over AppStats and hits .CSRFField
|
||||||
createTestApp(t, testCtx, "csrf-test-app")
|
createTestApp(t, testCtx, "csrf-test-app")
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleDashboard()
|
handler := testCtx.handlers.HandleDashboard()
|
||||||
@@ -433,7 +441,9 @@ func TestHandleAppNew(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/apps/new", nil)
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(), http.MethodGet, "/apps/new", nil,
|
||||||
|
)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleAppNew()
|
handler := testCtx.handlers.HandleAppNew()
|
||||||
@@ -501,7 +511,8 @@ func TestHandleWebhookRejectsOversizedBody(t *testing.T) {
|
|||||||
// Create a body larger than 1MB - it should be silently truncated
|
// Create a body larger than 1MB - it should be silently truncated
|
||||||
// and the webhook should still process (or fail gracefully on parse)
|
// and the webhook should still process (or fail gracefully on parse)
|
||||||
largePayload := strings.Repeat("x", 2*1024*1024) // 2MB
|
largePayload := strings.Repeat("x", 2*1024*1024) // 2MB
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/webhook/"+createdApp.WebhookSecret,
|
"/webhook/"+createdApp.WebhookSecret,
|
||||||
strings.NewReader(largePayload),
|
strings.NewReader(largePayload),
|
||||||
@@ -544,7 +555,8 @@ func testOwnershipVerification(t *testing.T, cfg ownedResourceTestConfig) {
|
|||||||
|
|
||||||
resourceID := cfg.createFn(t, testCtx, app1)
|
resourceID := cfg.createFn(t, testCtx, app1)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
cfg.deletePath(app2.ID, resourceID),
|
cfg.deletePath(app2.ID, resourceID),
|
||||||
nil,
|
nil,
|
||||||
@@ -583,7 +595,8 @@ func TestHandleEnvVarSaveBulk(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/env",
|
"/apps/"+createdApp.ID+"/env",
|
||||||
strings.NewReader(body),
|
strings.NewReader(body),
|
||||||
@@ -625,7 +638,8 @@ func TestHandleEnvVarSaveAppNotFound(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/nonexistent-id/env",
|
"/apps/nonexistent-id/env",
|
||||||
strings.NewReader(body),
|
strings.NewReader(body),
|
||||||
@@ -651,7 +665,8 @@ func TestHandleEnvVarSaveEmptyKeyRejected(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/env",
|
"/apps/"+createdApp.ID+"/env",
|
||||||
strings.NewReader(body),
|
strings.NewReader(body),
|
||||||
@@ -678,7 +693,8 @@ func TestHandleEnvVarSaveDuplicateKeyRejected(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/env",
|
"/apps/"+createdApp.ID+"/env",
|
||||||
strings.NewReader(body),
|
strings.NewReader(body),
|
||||||
@@ -716,7 +732,8 @@ func TestHandleEnvVarSaveCrossAppIsolation(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+appA.ID+"/env",
|
"/apps/"+appA.ID+"/env",
|
||||||
strings.NewReader(body),
|
strings.NewReader(body),
|
||||||
@@ -779,7 +796,8 @@ func TestHandleEnvVarSaveBodySizeLimit(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/env",
|
"/apps/"+createdApp.ID+"/env",
|
||||||
strings.NewReader(sb.String()),
|
strings.NewReader(sb.String()),
|
||||||
@@ -848,7 +866,8 @@ func TestDeleteVolumeOwnershipVerification(t *testing.T) {
|
|||||||
require.NoError(t, volume.Save(context.Background()))
|
require.NoError(t, volume.Save(context.Background()))
|
||||||
|
|
||||||
// Try to delete app1's volume using app2's URL path
|
// Try to delete app1's volume using app2's URL path
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+app2.ID+"/volumes/"+strconv.FormatInt(volume.ID, 10)+"/delete",
|
"/apps/"+app2.ID+"/volumes/"+strconv.FormatInt(volume.ID, 10)+"/delete",
|
||||||
nil,
|
nil,
|
||||||
@@ -889,7 +908,8 @@ func TestDeletePortOwnershipVerification(t *testing.T) {
|
|||||||
require.NoError(t, port.Save(context.Background()))
|
require.NoError(t, port.Save(context.Background()))
|
||||||
|
|
||||||
// Try to delete app1's port using app2's URL path
|
// Try to delete app1's port using app2's URL path
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+app2.ID+"/ports/"+strconv.FormatInt(port.ID, 10)+"/delete",
|
"/apps/"+app2.ID+"/ports/"+strconv.FormatInt(port.ID, 10)+"/delete",
|
||||||
nil,
|
nil,
|
||||||
@@ -930,7 +950,8 @@ func TestHandleEnvVarSaveEmptyClears(t *testing.T) {
|
|||||||
r := chi.NewRouter()
|
r := chi.NewRouter()
|
||||||
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
r.Post("/apps/{id}/env", testCtx.handlers.HandleEnvVarSave())
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/env",
|
"/apps/"+createdApp.ID+"/env",
|
||||||
strings.NewReader("[]"),
|
strings.NewReader("[]"),
|
||||||
@@ -979,7 +1000,8 @@ func TestHandleVolumeAddValidatesPaths(t *testing.T) {
|
|||||||
form.Set("host_path", tt.hostPath)
|
form.Set("host_path", tt.hostPath)
|
||||||
form.Set("container_path", tt.containerPath)
|
form.Set("container_path", tt.containerPath)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/volumes",
|
"/apps/"+createdApp.ID+"/volumes",
|
||||||
strings.NewReader(form.Encode()),
|
strings.NewReader(form.Encode()),
|
||||||
@@ -1038,7 +1060,7 @@ func TestSetupRequiredExemptsHealthAndStaticAndAPI(t *testing.T) {
|
|||||||
t.Run(path, func(t *testing.T) {
|
t.Run(path, func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, path, nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, path, nil)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
wrapped.ServeHTTP(rr, req)
|
wrapped.ServeHTTP(rr, req)
|
||||||
|
|
||||||
@@ -1051,7 +1073,7 @@ func TestSetupRequiredExemptsHealthAndStaticAndAPI(t *testing.T) {
|
|||||||
t.Run("non-exempt redirects", func(t *testing.T) {
|
t.Run("non-exempt redirects", func(t *testing.T) {
|
||||||
t.Parallel()
|
t.Parallel()
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
rr := httptest.NewRecorder()
|
rr := httptest.NewRecorder()
|
||||||
wrapped.ServeHTTP(rr, req)
|
wrapped.ServeHTTP(rr, req)
|
||||||
|
|
||||||
@@ -1067,7 +1089,8 @@ func TestHandleCancelDeployRedirects(t *testing.T) {
|
|||||||
|
|
||||||
createdApp := createTestApp(t, testCtx, "cancel-deploy-app")
|
createdApp := createTestApp(t, testCtx, "cancel-deploy-app")
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/"+createdApp.ID+"/deployments/cancel",
|
"/apps/"+createdApp.ID+"/deployments/cancel",
|
||||||
nil,
|
nil,
|
||||||
@@ -1087,7 +1110,8 @@ func TestHandleCancelDeployReturns404ForUnknownApp(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
"/apps/nonexistent/deployments/cancel",
|
"/apps/nonexistent/deployments/cancel",
|
||||||
nil,
|
nil,
|
||||||
@@ -1108,7 +1132,8 @@ func TestHandleWebhookReturns404ForUnknownSecret(t *testing.T) {
|
|||||||
|
|
||||||
webhookURL := "/webhook/unknown-secret"
|
webhookURL := "/webhook/unknown-secret"
|
||||||
payload := `{"ref": "refs/heads/main"}`
|
payload := `{"ref": "refs/heads/main"}`
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
webhookURL,
|
webhookURL,
|
||||||
strings.NewReader(payload),
|
strings.NewReader(payload),
|
||||||
@@ -1143,7 +1168,8 @@ func TestHandleWebhookProcessesValidWebhook(t *testing.T) {
|
|||||||
|
|
||||||
payload := `{"ref": "refs/heads/main", "after": "abc123"}`
|
payload := `{"ref": "refs/heads/main", "after": "abc123"}`
|
||||||
webhookURL := "/webhook/" + createdApp.WebhookSecret
|
webhookURL := "/webhook/" + createdApp.WebhookSecret
|
||||||
request := httptest.NewRequest(
|
request := httptest.NewRequestWithContext(
|
||||||
|
t.Context(),
|
||||||
http.MethodPost,
|
http.MethodPost,
|
||||||
webhookURL,
|
webhookURL,
|
||||||
strings.NewReader(payload),
|
strings.NewReader(payload),
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ func TestRenderTemplateBuffersOutput(t *testing.T) {
|
|||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
// The setup page is simple and has no DB dependencies
|
// The setup page is simple and has no DB dependencies
|
||||||
request := httptest.NewRequest(http.MethodGet, "/setup", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/setup", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleSetupGET()
|
handler := testCtx.handlers.HandleSetupGET()
|
||||||
@@ -39,7 +39,7 @@ func TestDashboardRenderTemplateBuffersOutput(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleDashboard()
|
handler := testCtx.handlers.HandleDashboard()
|
||||||
@@ -59,7 +59,7 @@ func TestLoginRenderTemplateBuffersOutput(t *testing.T) {
|
|||||||
|
|
||||||
testCtx := setupTestHandlers(t)
|
testCtx := setupTestHandlers(t)
|
||||||
|
|
||||||
request := httptest.NewRequest(http.MethodGet, "/login", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/login", nil)
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
|
|
||||||
handler := testCtx.handlers.HandleLoginGET()
|
handler := testCtx.handlers.HandleLoginGET()
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ func TestCORS_NoOriginsConfigured_NoCORSHeaders(t *testing.T) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
req.Header.Set("Origin", "https://evil.com")
|
req.Header.Set("Origin", "https://evil.com")
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
@@ -50,7 +50,7 @@ func TestCORS_OriginsConfigured_AllowsMatchingOrigin(t *testing.T) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
req.Header.Set("Origin", "https://app.example.com")
|
req.Header.Set("Origin", "https://app.example.com")
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
@@ -70,7 +70,7 @@ func TestCORS_OriginsConfigured_RejectsNonMatchingOrigin(t *testing.T) {
|
|||||||
w.WriteHeader(http.StatusOK)
|
w.WriteHeader(http.StatusOK)
|
||||||
}))
|
}))
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
req.Header.Set("Origin", "https://evil.com")
|
req.Header.Set("Origin", "https://evil.com")
|
||||||
|
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
|
|||||||
|
|
||||||
// First 5 requests should succeed (burst)
|
// First 5 requests should succeed (burst)
|
||||||
for i := range 5 {
|
for i := range 5 {
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "192.168.1.1:12345"
|
req.RemoteAddr = "192.168.1.1:12345"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
@@ -44,7 +44,7 @@ func TestLoginRateLimitAllowsUpToBurst(t *testing.T) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 6th request should be rate limited
|
// 6th request should be rate limited
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "192.168.1.1:12345"
|
req.RemoteAddr = "192.168.1.1:12345"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
@@ -63,21 +63,21 @@ func TestLoginRateLimitIsolatesIPs(t *testing.T) {
|
|||||||
|
|
||||||
// Exhaust IP1's budget
|
// Exhaust IP1's budget
|
||||||
for range 5 {
|
for range 5 {
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "10.0.0.1:1234"
|
req.RemoteAddr = "10.0.0.1:1234"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
// IP1 should be blocked
|
// IP1 should be blocked
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "10.0.0.1:1234"
|
req.RemoteAddr = "10.0.0.1:1234"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
assert.Equal(t, http.StatusTooManyRequests, rec.Code)
|
assert.Equal(t, http.StatusTooManyRequests, rec.Code)
|
||||||
|
|
||||||
// IP2 should still work
|
// IP2 should still work
|
||||||
req2 := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req2 := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req2.RemoteAddr = "10.0.0.2:1234"
|
req2.RemoteAddr = "10.0.0.2:1234"
|
||||||
rec2 := httptest.NewRecorder()
|
rec2 := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec2, req2)
|
handler.ServeHTTP(rec2, req2)
|
||||||
@@ -96,13 +96,13 @@ func TestLoginRateLimitReturns429Body(t *testing.T) {
|
|||||||
|
|
||||||
// Exhaust burst
|
// Exhaust burst
|
||||||
for range 5 {
|
for range 5 {
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "172.16.0.1:5555"
|
req.RemoteAddr = "172.16.0.1:5555"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
}
|
}
|
||||||
|
|
||||||
req := httptest.NewRequest(http.MethodPost, "/login", nil)
|
req := httptest.NewRequestWithContext(t.Context(), http.MethodPost, "/login", nil)
|
||||||
req.RemoteAddr = "172.16.0.1:5555"
|
req.RemoteAddr = "172.16.0.1:5555"
|
||||||
rec := httptest.NewRecorder()
|
rec := httptest.NewRecorder()
|
||||||
handler.ServeHTTP(rec, req)
|
handler.ServeHTTP(rec, req)
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ func getSessionCookie(t *testing.T, svc *auth.Service) *http.Cookie {
|
|||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
|
|
||||||
err = svc.CreateSession(recorder, request, user)
|
err = svc.CreateSession(recorder, request, user)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
@@ -380,7 +380,7 @@ func TestDestroySessionMaxAge(testingT *testing.T) {
|
|||||||
defer cleanup()
|
defer cleanup()
|
||||||
|
|
||||||
recorder := httptest.NewRecorder()
|
recorder := httptest.NewRecorder()
|
||||||
request := httptest.NewRequest(http.MethodGet, "/", nil)
|
request := httptest.NewRequestWithContext(t.Context(), http.MethodGet, "/", nil)
|
||||||
|
|
||||||
err := svc.DestroySession(recorder, request)
|
err := svc.DestroySession(recorder, request)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|||||||
Reference in New Issue
Block a user