- Add deployment-name branding to titles, mastheads, and OG tags - Share one grant delivery-state query with lineage across grants surfaces - Show pool status/usage, org owners, and config readiness - Make billing views projection-aware with recency and sync vocabulary - Guard FedWiki creation without domains and render route-aware 404s
147 lines
6.4 KiB
Go
147 lines
6.4 KiB
Go
package server_test
|
|
|
|
// DB-backed integration coverage for the shared ListGrantsWithDelivery query
|
|
// (ux-honest-surfaces design decision 1) end to end: real IssueGrant / Extend
|
|
// / Revoke flows against a real Postgres, read back through both the
|
|
// org-detail composite and the grants index, asserting the two surfaces
|
|
// agree on delivery-state wording and that supersession lineage is visible.
|
|
//
|
|
// Also locks in the derivation's precise "superseded" boundary: a grant
|
|
// whose provision simply ended with no recorded successor (a plain revoke)
|
|
// renders "Inactive", not "Superseded" -- "Superseded" is reserved for the
|
|
// case a replacing grant is actually on record (extends_grant_id), because
|
|
// the UI must be able to name what replaced a superseded row.
|
|
|
|
import (
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// postOrg posts to a handler with only {orgID} set (Issue Grant's form has
|
|
// no pool-scoped path parameter). Success/error copy fires as an HX-Trigger
|
|
// toast (fireSuccessToast/fireErrorToast), not inline body text -- callers
|
|
// assert against the returned trigger header, mirroring postOrgPool.
|
|
func (h *otcHarness) postOrg(handler http.HandlerFunc, orgID string, form url.Values) (int, string, string) {
|
|
h.t.Helper()
|
|
req := httptest.NewRequestWithContext(h.ctx, http.MethodPost, "/", strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.SetPathValue("orgID", orgID)
|
|
rec := httptest.NewRecorder()
|
|
handler(rec, req)
|
|
return rec.Code, rec.Body.String(), rec.Header().Get("HX-Trigger")
|
|
}
|
|
|
|
// postGrant posts to a handler with {grantID} set (Revoke).
|
|
func (h *otcHarness) postGrant(handler http.HandlerFunc, grantID string, form url.Values) (int, string, string) {
|
|
h.t.Helper()
|
|
req := httptest.NewRequestWithContext(h.ctx, http.MethodPost, "/", strings.NewReader(form.Encode()))
|
|
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
req.SetPathValue("grantID", grantID)
|
|
rec := httptest.NewRecorder()
|
|
handler(rec, req)
|
|
return rec.Code, rec.Body.String(), rec.Header().Get("HX-Trigger")
|
|
}
|
|
|
|
// getOrg issues a GET against a handler with {orgID} set.
|
|
func (h *otcHarness) getOrg(handler http.HandlerFunc, orgID string) (int, string) {
|
|
h.t.Helper()
|
|
req := httptest.NewRequestWithContext(h.ctx, http.MethodGet, "/", nil)
|
|
req.SetPathValue("orgID", orgID)
|
|
rec := httptest.NewRecorder()
|
|
handler(rec, req)
|
|
return rec.Code, rec.Body.String()
|
|
}
|
|
|
|
func TestGrantDeliveryState_EndToEnd(t *testing.T) {
|
|
database := testDB(t)
|
|
f := newOtcFixture(t, database)
|
|
h := newOtcHarness(t, database, f.operatorID)
|
|
|
|
org, pool := otcOrg(t, database, f, "Delivery State Org", true)
|
|
|
|
// Issue on ladder A's product: creates a live grant with no lineage.
|
|
code, body, trigger := h.postOrg(h.handler.IssueGrant, org, url.Values{
|
|
"product_id": {f.prodA}, "reason": {"manual"}, "quantity": {"1"},
|
|
})
|
|
if code != http.StatusOK || !strings.Contains(trigger, "issued") {
|
|
t.Fatalf("issue grant A: status=%d trigger=%q body=%s", code, trigger, body)
|
|
}
|
|
|
|
// Issue on ladder B's product too: this one gets revoked with no
|
|
// successor, so it must land on "Inactive", not "Superseded".
|
|
code, body, trigger = h.postOrg(h.handler.IssueGrant, org, url.Values{
|
|
"product_id": {f.prodB}, "reason": {"manual"}, "quantity": {"1"},
|
|
})
|
|
if code != http.StatusOK || !strings.Contains(trigger, "issued") {
|
|
t.Fatalf("issue grant B: status=%d trigger=%q body=%s", code, trigger, body)
|
|
}
|
|
|
|
grantA := otcScalar(t, database, `SELECT count(*) FROM core.grants WHERE granted_to_org_id = $1 AND product_id = $2`, org, f.prodA)
|
|
if grantA != 1 {
|
|
t.Fatalf("expected exactly one grant for product A, found %d", grantA)
|
|
}
|
|
var grantBID string
|
|
if err := database.QueryRow(`SELECT grant_id FROM core.grants WHERE granted_to_org_id = $1 AND product_id = $2`, org, f.prodB).Scan(&grantBID); err != nil {
|
|
t.Fatalf("resolve grant B id: %v", err)
|
|
}
|
|
|
|
// Extend A's delivery: the handler resolves the pool's active
|
|
// grant-backed provision (A) and issues+confers a replacement, which
|
|
// must supersede A's position and mark A "Superseded" / the new grant
|
|
// "Live", with lineage naming each other.
|
|
code, body, trigger = h.postOrgPool(h.handler.ExtendGrant, org, pool, url.Values{"reason": {"extend test"}})
|
|
if code != http.StatusOK || !strings.Contains(trigger, "extended") {
|
|
t.Fatalf("extend grant A: status=%d trigger=%q body=%s", code, trigger, body)
|
|
}
|
|
|
|
// Revoke B with no successor.
|
|
code, body, trigger = h.postGrant(h.handler.RevokeGrantAndTransition, grantBID, url.Values{"org_id": {org}})
|
|
if code != http.StatusOK || !strings.Contains(trigger, "revoked") {
|
|
t.Fatalf("revoke grant B: status=%d trigger=%q body=%s", code, trigger, body)
|
|
}
|
|
|
|
// Org-detail composite: A is Superseded with a named replacement, the
|
|
// new grant is Live and names A as what it extends, and B is Inactive
|
|
// with no "replaced by" (it was revoked outright, no successor).
|
|
code, orgBody := h.getOrg(h.handler.GetOrgEnrollment, org)
|
|
if code != http.StatusOK {
|
|
t.Fatalf("get org enrollment: status=%d body=%s", code, orgBody)
|
|
}
|
|
assertDeliveryRendering(t, "org-detail", orgBody)
|
|
|
|
// Grants index: same derivation, same wording -- design decision 1
|
|
// ("one query, two consumers") means these two reads must agree.
|
|
req := httptest.NewRequestWithContext(h.ctx, http.MethodGet, "/", nil)
|
|
rec := httptest.NewRecorder()
|
|
h.handler.GetGrantsPage(rec, req)
|
|
if rec.Code != http.StatusOK {
|
|
t.Fatalf("get grants page: status=%d body=%s", rec.Code, rec.Body.String())
|
|
}
|
|
assertDeliveryRendering(t, "grants index", rec.Body.String())
|
|
}
|
|
|
|
// assertDeliveryRendering checks the delivery-state vocabulary and
|
|
// supersession lineage shared between the org-detail composite and the
|
|
// grants index (ux-honest-surfaces UX-3).
|
|
func assertDeliveryRendering(t *testing.T, surface, body string) {
|
|
t.Helper()
|
|
if !strings.Contains(body, ">Live<") {
|
|
t.Errorf("%s: expected a Live delivery badge, got:\n%s", surface, body)
|
|
}
|
|
if !strings.Contains(body, ">Superseded<") {
|
|
t.Errorf("%s: expected a Superseded delivery badge, got:\n%s", surface, body)
|
|
}
|
|
if !strings.Contains(body, ">Inactive<") {
|
|
t.Errorf("%s: expected an Inactive delivery badge (the plain revoke with no successor), got:\n%s", surface, body)
|
|
}
|
|
if !strings.Contains(body, "replaced by") {
|
|
t.Errorf("%s: expected the superseded grant to name its replacement, got:\n%s", surface, body)
|
|
}
|
|
if !strings.Contains(body, "extends") {
|
|
t.Errorf("%s: expected the replacing grant to name what it extends, got:\n%s", surface, body)
|
|
}
|
|
}
|