Skip to content

Commit a484dc3

Browse files
test(handlers): cover stack promote in-place update branch
Add a real-DB test that pre-seeds a target stack in the same family so Promote takes the updated_existing path — covering both the existing-service image_ref update and the missing-service create branches. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 5edb18c commit a484dc3

1 file changed

Lines changed: 53 additions & 0 deletions

File tree

internal/handlers/deploy_stack_gap_coverage_test.go

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -396,6 +396,59 @@ func TestStackPromote_CreatesNewTarget(t *testing.T) {
396396
assert.Equal(t, http.StatusAccepted, resp.StatusCode)
397397
}
398398

399+
// TestStackPromote_InPlaceUpdate — a pre-existing target stack in the same
400+
// family is re-used (action="updated_existing"), exercising the in-place
401+
// branch: updating an existing target service's image_ref AND creating a
402+
// target service the source has but the target lacks.
403+
func TestStackPromote_InPlaceUpdate(t *testing.T) {
404+
gapCovNeedsDB(t)
405+
db, cleanDB := testhelpers.SetupTestDB(t)
406+
defer cleanDB()
407+
ensureStackTables(t, db)
408+
409+
teamID := testhelpers.MustCreateTeamDB(t, db, "pro")
410+
jwt := testhelpers.MustSignSessionJWT(t, uuid.NewString(), teamID, "inplace@example.com")
411+
412+
// Source (staging) is the family root. Two services with image_refs.
413+
srcSlug, srcID := seedPromoteSourceStackNoImageRef(t, db, teamID, "staging", "ip-src")
414+
for _, svc := range []string{"api", "worker"} {
415+
_, err := db.ExecContext(context.Background(), `
416+
INSERT INTO stack_services (stack_id, name, expose, port, image_ref, status)
417+
VALUES ($1::uuid, $2, true, 8080, $3, 'healthy')
418+
`, srcID, svc, "registry.local/"+svc+":v2")
419+
require.NoError(t, err)
420+
}
421+
422+
// Pre-existing target (development) in the SAME family (parent = source).
423+
tgtSlug := "stk-iptgt-" + randHex(t, 4)
424+
var tgtID string
425+
require.NoError(t, db.QueryRowContext(context.Background(), `
426+
INSERT INTO stacks (team_id, name, slug, namespace, status, tier, env, parent_stack_id)
427+
VALUES ($1, 'ip-tgt', $2, $3, 'healthy', 'pro', 'development', $4::uuid)
428+
RETURNING id::text
429+
`, teamID, tgtSlug, "instant-stack-"+tgtSlug, srcID).Scan(&tgtID))
430+
// Target has only "api" (old image_ref) — "worker" is missing so the
431+
// create-new-service branch fires.
432+
_, err := db.ExecContext(context.Background(), `
433+
INSERT INTO stack_services (stack_id, name, expose, port, image_ref, status)
434+
VALUES ($1::uuid, 'api', true, 8080, 'registry.local/api:v1', 'healthy')
435+
`, tgtID)
436+
require.NoError(t, err)
437+
438+
app := newStackTestApp(t, db)
439+
resp := postPromote(t, app, jwt, srcSlug, map[string]any{"from": "staging", "to": "development"})
440+
defer resp.Body.Close()
441+
// Existing target -> 200 updated_existing.
442+
assert.Equal(t, http.StatusOK, resp.StatusCode)
443+
444+
// The target's "worker" service must have been created with the source image.
445+
var n int
446+
require.NoError(t, db.QueryRowContext(context.Background(),
447+
`SELECT count(*) FROM stack_services WHERE stack_id = $1::uuid AND name = 'worker'`,
448+
tgtID).Scan(&n))
449+
assert.Equal(t, 1, n, "missing target service must be created during in-place promote")
450+
}
451+
399452
// ── copyVaultRefsForPromote — direct unit coverage ───────────────────────────
400453

401454
// TestCopyVaultRefsForPromote_NoSourceKeys returns nil,nil (the no-op branch).

0 commit comments

Comments
 (0)