hub v0.61.0 + felhom-tenantsync v1.1.0: Customer RESET (middle lifecycle tier)
One operator action returns a customer to pre-first-install: all operational state dies (offsite repo, PBS namespace+backups, DR recipe, one-time secret, claim state, retained escrow custody); identity + basic config + provenance + events survive. Sits between host delete and customer Delete. - store/customer_reset.go: customer_resets journal, live inventory, ack-gated purge (never touches identity/provenance/events), DeleteClaim. - claim.ResetToUnclaimed: delete claim row -> fresh code next onboarding. - offsite.Deprovision (idempotent) + OffsiteIdentifier + ClearProvisionedDescriptor. - tenantsync.Deprovision + felhom-tenantsync.sh deprovision op (destroys ns + backup groups + token; shared user untouched; idempotent). - web/customer_reset.go: GET reset -> inventory JSON; POST -> orchestration (external teardown FIRST, DB purge LAST; refuse-while-hosts; typed-id + separate escrow ack). Amber RESET card distinct from red Danger-zone Delete. - Red-proofs: ack-gate + partial-failure resumability (both proven red); store ack-gating + journal round-trip; offsite idempotency + descriptor clear; RESET-card render. Green: build + vet + test.
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
package web
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
|
||||
)
|
||||
|
||||
// The RESET control renders on the customer page as its OWN amber card, visually distinct from the
|
||||
// red Danger-zone Delete, with the typed-id confirm + the separate escrow-custody ack row (ruling 1).
|
||||
func TestTemplates_CustomerResetCard(t *testing.T) {
|
||||
s, st := newTestServer(t)
|
||||
if err := st.SaveCustomerConfig(&store.CustomerConfig{
|
||||
CustomerID: "acme", CustomerName: "Acme", Domain: "acme.hu",
|
||||
RetrievalPassword: "pw", APIKey: "k", Status: "active",
|
||||
}); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
html := renderCustomerPage(t, s, "acme")
|
||||
|
||||
// The RESET form posts to the reset endpoint, and the preview endpoint is fetched by its JS.
|
||||
if !strings.Contains(html, `action="/configs/acme/reset"`) {
|
||||
t.Error("reset form action missing")
|
||||
}
|
||||
for _, fn := range []string{"customerResetConfirm", "customerResetSubmit", "/configs/' + encodeURIComponent(cid) + '/reset"} {
|
||||
if !strings.Contains(html, fn) {
|
||||
t.Errorf("reset JS missing %q", fn)
|
||||
}
|
||||
}
|
||||
// Distinct visual: the reset card is amber (--warn); the delete form (red --crit) still exists
|
||||
// separately — the two controls are not merged.
|
||||
if !strings.Contains(html, "border-color: var(--warn);") {
|
||||
t.Error("reset card is not amber-toned (must be distinct from the red Delete)")
|
||||
}
|
||||
if !strings.Contains(html, `action="/configs/acme/delete"`) {
|
||||
t.Error("the Danger-zone Delete must remain a separate control")
|
||||
}
|
||||
// The separate escrow-custody ack (ruling 1) + typed-id confirm.
|
||||
if !strings.Contains(html, `id="cust-reset-escrow-acme"`) {
|
||||
t.Error("escrow-custody ack checkbox missing")
|
||||
}
|
||||
if !strings.Contains(html, `name="confirm_id"`) || !strings.Contains(html, `name="escrow_ack"`) {
|
||||
t.Error("reset confirm inputs (confirm_id / escrow_ack) missing")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user