Skip to content

Commit b03f1b3

Browse files
test(coverage): audit List/CSV DB-error; vault PutSecret bad-AES; resource_family Family/ListFamilies DB-error arms
- audit.go: List + ListCSV db_failed via faultdb. - vault.go: PutSecret encryptPlaintext aes-key-invalid 500. - resource.go (Family/ListFamilies): fetch_failed + invalid_id + list DB-error. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 809e491 commit b03f1b3

3 files changed

Lines changed: 188 additions & 0 deletions

File tree

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package handlers_test
2+
3+
// audit_final_test.go — FINAL coverage pass for audit.go's List / ListCSV
4+
// DB-error arms via a faultdb-backed handler.
5+
6+
import (
7+
"database/sql"
8+
"net/http"
9+
"net/http/httptest"
10+
"testing"
11+
12+
"github.com/gofiber/fiber/v2"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
16+
"instant.dev/internal/config"
17+
"instant.dev/internal/handlers"
18+
"instant.dev/internal/middleware"
19+
"instant.dev/internal/testhelpers"
20+
)
21+
22+
func auditFaultApp(t *testing.T, db *sql.DB) *fiber.App {
23+
t.Helper()
24+
cfg := &config.Config{JWTSecret: testhelpers.TestJWTSecret, AESKey: testhelpers.TestAESKeyHex, Environment: "test"}
25+
app := fiber.New(fiber.Config{
26+
ErrorHandler: func(c *fiber.Ctx, e error) error {
27+
if e == handlers.ErrResponseWritten {
28+
return nil
29+
}
30+
code := fiber.StatusInternalServerError
31+
if fe, ok := e.(*fiber.Error); ok {
32+
code = fe.Code
33+
}
34+
return c.Status(code).JSON(fiber.Map{"ok": false, "error": e.Error()})
35+
},
36+
})
37+
app.Use(middleware.RequestID())
38+
h := handlers.NewAuditHandler(db)
39+
api := app.Group("/api/v1", middleware.RequireAuth(cfg))
40+
api.Get("/audit", h.List)
41+
api.Get("/audit.csv", h.ListCSV)
42+
return app
43+
}
44+
45+
func auditJWT(t *testing.T, db *sql.DB) string {
46+
t.Helper()
47+
teamID := testhelpers.MustCreateTeamDB(t, db, "pro")
48+
email := testhelpers.UniqueEmail(t)
49+
var userID string
50+
require.NoError(t, db.QueryRow(
51+
`INSERT INTO users (team_id, email) VALUES ($1::uuid, $2) RETURNING id::text`, teamID, email).Scan(&userID))
52+
return testhelpers.MustSignSessionJWT(t, userID, teamID, email)
53+
}
54+
55+
// List: ListAuditEventsByTeam errors → db_failed (audit.go:246). failAfter=0.
56+
func TestAuditFinal_List_DBError_503(t *testing.T) {
57+
seedDB, clean := testhelpers.SetupTestDB(t)
58+
defer clean()
59+
jwt := auditJWT(t, seedDB)
60+
61+
app := auditFaultApp(t, openFaultDB(t, 0))
62+
req := httptest.NewRequest(http.MethodGet, "/api/v1/audit", nil)
63+
req.Header.Set("Authorization", "Bearer "+jwt)
64+
resp, err := app.Test(req, 10000)
65+
require.NoError(t, err)
66+
defer resp.Body.Close()
67+
require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
68+
}
69+
70+
// ListCSV: ListAuditEventsByTeam errors → db_failed (audit.go:313). failAfter=0.
71+
func TestAuditFinal_ListCSV_DBError_503(t *testing.T) {
72+
seedDB, clean := testhelpers.SetupTestDB(t)
73+
defer clean()
74+
jwt := auditJWT(t, seedDB)
75+
76+
app := auditFaultApp(t, openFaultDB(t, 0))
77+
req := httptest.NewRequest(http.MethodGet, "/api/v1/audit.csv", nil)
78+
req.Header.Set("Authorization", "Bearer "+jwt)
79+
resp, err := app.Test(req, 10000)
80+
require.NoError(t, err)
81+
defer resp.Body.Close()
82+
assert.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
83+
}

internal/handlers/resource_final_test.go

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,8 @@ func resourceFaultApp(t *testing.T, db *sql.DB, teamID string) *fiber.App {
5454
app.Post("/r/:id/pause", h.Pause)
5555
app.Post("/r/:id/resume", h.Resume)
5656
app.Post("/r/:id/rotate", h.RotateCredentials)
57+
app.Get("/r/:id/family", h.Family)
58+
app.Get("/r/families", h.ListFamilies)
5759
return app
5860
}
5961

@@ -84,6 +86,13 @@ func rfPost(t *testing.T, app *fiber.App, path string) *http.Response {
8486
return resp
8587
}
8688

89+
func rfGet(t *testing.T, app *fiber.App, path string) *http.Response {
90+
t.Helper()
91+
resp, err := app.Test(httptest.NewRequest(http.MethodGet, path, nil), 10000)
92+
require.NoError(t, err)
93+
return resp
94+
}
95+
8796
func rfErr(t *testing.T, resp *http.Response) string {
8897
t.Helper()
8998
var m map[string]any
@@ -179,6 +188,42 @@ func TestResourceFinal_Resume_DBFlipError_503(t *testing.T) {
179188
require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
180189
}
181190

191+
// Family: GetResourceByToken errors → fetch_failed (resource_family.go:89).
192+
// failAfter=0.
193+
func TestResourceFinal_Family_LookupError_503(t *testing.T) {
194+
seedDB, clean := testhelpers.SetupTestDB(t)
195+
defer clean()
196+
teamID := testhelpers.MustCreateTeamDB(t, seedDB, "pro")
197+
token := rfSeedActivePG(t, seedDB, teamID)
198+
199+
app := resourceFaultApp(t, openFaultDB(t, 0), teamID)
200+
resp := rfGet(t, app, "/r/"+token+"/family")
201+
defer resp.Body.Close()
202+
require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
203+
}
204+
205+
// Family: bad :id → invalid_id (resource_family.go:60).
206+
func TestResourceFinal_Family_BadID_400(t *testing.T) {
207+
db, clean := testhelpers.SetupTestDB(t)
208+
defer clean()
209+
teamID := testhelpers.MustCreateTeamDB(t, db, "pro")
210+
app := resourceFaultApp(t, db, teamID)
211+
resp := rfGet(t, app, "/r/not-a-uuid/family")
212+
defer resp.Body.Close()
213+
assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
214+
}
215+
216+
// ListFamilies: DB error → 503 (resource_family.go:144-153). failAfter=0.
217+
func TestResourceFinal_ListFamilies_DBError_503(t *testing.T) {
218+
seedDB, clean := testhelpers.SetupTestDB(t)
219+
defer clean()
220+
teamID := testhelpers.MustCreateTeamDB(t, seedDB, "pro")
221+
app := resourceFaultApp(t, openFaultDB(t, 0), teamID)
222+
resp := rfGet(t, app, "/r/families")
223+
defer resp.Body.Close()
224+
require.Equal(t, http.StatusServiceUnavailable, resp.StatusCode)
225+
}
226+
182227
// RotateCredentials: GetResourceByToken errors → fetch_failed. failAfter=0.
183228
func TestResourceFinal_Rotate_LookupError_503(t *testing.T) {
184229
seedDB, clean := testhelpers.SetupTestDB(t)
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package handlers_test
2+
3+
// vault_final_test.go — FINAL coverage pass for vault.go's encryptPlaintext
4+
// aes-key-invalid arm (vault.go:163) via a handler configured with a bad AES
5+
// key, driven through PutSecret.
6+
7+
import (
8+
"database/sql"
9+
"net/http"
10+
"testing"
11+
12+
"github.com/gofiber/fiber/v2"
13+
"github.com/stretchr/testify/assert"
14+
"github.com/stretchr/testify/require"
15+
16+
"instant.dev/internal/config"
17+
"instant.dev/internal/handlers"
18+
"instant.dev/internal/middleware"
19+
"instant.dev/internal/plans"
20+
"instant.dev/internal/testhelpers"
21+
)
22+
23+
func vaultBadAESApp(t *testing.T, db *sql.DB) *fiber.App {
24+
t.Helper()
25+
cfg := &config.Config{
26+
JWTSecret: testhelpers.TestJWTSecret,
27+
AESKey: "not-a-valid-hex-aes-key", // ParseAESKey fails → encryptPlaintext errors
28+
}
29+
app := fiber.New(fiber.Config{
30+
ErrorHandler: func(c *fiber.Ctx, e error) error {
31+
if e == handlers.ErrResponseWritten {
32+
return nil
33+
}
34+
code := fiber.StatusInternalServerError
35+
if fe, ok := e.(*fiber.Error); ok {
36+
code = fe.Code
37+
}
38+
return c.Status(code).JSON(fiber.Map{"ok": false, "error": "internal_error", "message": e.Error()})
39+
},
40+
})
41+
app.Use(middleware.RequestID())
42+
h := handlers.NewVaultHandler(db, cfg, plans.Default())
43+
api := app.Group("/api/v1", middleware.RequireAuth(cfg))
44+
api.Put("/vault/:env/:key", h.PutSecret)
45+
return app
46+
}
47+
48+
// PutSecret with a bad AES key → encryptPlaintext fails → 500 (vault.go:163).
49+
func TestVaultFinal_PutSecret_BadAESKey_500(t *testing.T) {
50+
db, clean := testhelpers.SetupTestDB(t)
51+
defer clean()
52+
_, _, jwt := makeTeamUser(t, db)
53+
54+
app := vaultBadAESApp(t, db)
55+
req := jsonReq(t, http.MethodPut, "/api/v1/vault/production/MY_KEY", jwt, map[string]string{"value": "s3cr3t"})
56+
resp, err := app.Test(req, 5000)
57+
require.NoError(t, err)
58+
defer resp.Body.Close()
59+
assert.Equal(t, http.StatusInternalServerError, resp.StatusCode)
60+
}

0 commit comments

Comments
 (0)