Skip to content

Commit 2be5e70

Browse files
test(router): cover the DELETE /resources/:id env-lookup wiring
The Wave-2 A1 rename of the WithEnvLookup closure target (ResourceEnvByTokenOrIDForMiddleware) made router.go's closure line a changed line under diff-cover, and it had no unit coverage (the route was only exercised by build-tagged E2E). New router-package test drives an authenticated DELETE by ROW ID through the real router — covering the closure, the id-fallback resolution, and the idempotent-retry path. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 9d1cdff commit 2be5e70

1 file changed

Lines changed: 77 additions & 0 deletions

File tree

Lines changed: 77 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
package router_test
2+
3+
// resource_delete_env_lookup_test.go — drives the DELETE /api/v1/resources/:id
4+
// route through the REAL router so the WithEnvLookup closure wired in
5+
// router.go (the `return handlers.ResourceEnvByTokenOrIDForMiddleware(c, db)`
6+
// line) executes under unit coverage, not just E2E. Wave-2 A1 renamed that
7+
// helper (token-or-id resolution); this test pins the wiring:
8+
//
9+
// - an authenticated DELETE addressed by the resource's ROW ID resolves
10+
// through the env-lookup (id fallback) and soft-deletes the row, and
11+
// - a second DELETE on the same (now-deleted) row reports idempotent
12+
// success — proving the closure's fail-open path also routes through.
13+
14+
import (
15+
"context"
16+
"net/http"
17+
"net/http/httptest"
18+
"testing"
19+
20+
"github.com/stretchr/testify/require"
21+
22+
"instant.dev/internal/email"
23+
"instant.dev/internal/plans"
24+
"instant.dev/internal/router"
25+
"instant.dev/internal/testhelpers"
26+
)
27+
28+
func TestRouter_ResourceDelete_ByRowID_EnvLookupWired(t *testing.T) {
29+
db, dbClean := testhelpers.SetupTestDB(t)
30+
defer dbClean()
31+
rdb, rdbClean := testhelpers.SetupTestRedis(t)
32+
defer rdbClean()
33+
34+
cfg := newRouterTestConfig()
35+
app := router.New(cfg, db, rdb, nil, email.NewNoop(), plans.Default(), nil, nil)
36+
37+
// Seed a team + user + an active resource owned by the team.
38+
teamID := testhelpers.MustCreateTeamDB(t, db, "pro")
39+
var userID string
40+
require.NoError(t, db.QueryRowContext(context.Background(),
41+
`INSERT INTO users (team_id, email) VALUES ($1::uuid, $2) RETURNING id::text`,
42+
teamID, testhelpers.UniqueEmail(t)).Scan(&userID))
43+
jwt := testhelpers.MustSignSessionJWT(t, userID, teamID, "router-del@example.com")
44+
45+
var rowID string
46+
require.NoError(t, db.QueryRowContext(context.Background(),
47+
`INSERT INTO resources (team_id, resource_type, tier, env, status)
48+
VALUES ($1::uuid, 'webhook', 'pro', 'staging', 'active')
49+
RETURNING id::text`, teamID).Scan(&rowID))
50+
51+
doDelete := func() *http.Response {
52+
req := httptest.NewRequest(http.MethodDelete, "/api/v1/resources/"+rowID, nil)
53+
req.Header.Set("Authorization", "Bearer "+jwt)
54+
resp, err := app.Test(req, 15000)
55+
require.NoError(t, err)
56+
return resp
57+
}
58+
59+
// DELETE by ROW ID through the real router: env-policy middleware runs
60+
// the token-or-id env lookup, then the handler resolves + deletes.
61+
resp := doDelete()
62+
defer resp.Body.Close()
63+
require.Equal(t, http.StatusOK, resp.StatusCode,
64+
"DELETE by row id through the real router must succeed")
65+
66+
var status string
67+
require.NoError(t, db.QueryRowContext(context.Background(),
68+
`SELECT status FROM resources WHERE id = $1::uuid`, rowID).Scan(&status))
69+
require.Equal(t, "deleted", status)
70+
71+
// Idempotent retry: the row is now 'deleted'; the env lookup still runs
72+
// (resolves the deleted row) and the handler reports already_deleted.
73+
resp2 := doDelete()
74+
defer resp2.Body.Close()
75+
require.Equal(t, http.StatusOK, resp2.StatusCode,
76+
"repeat DELETE must be idempotent success")
77+
}

0 commit comments

Comments
 (0)