Add an append-only ledger of entitlement set rule changes with per-pool effect rows, a preview-and-commit rule change flow, and an automatic drain that settles deferred recomputations. Rules gain a tier reduction policy, resource keys declare over-limit behavior, and the materializer now lowers limits when a rule stops applying. Add entitlement set rule change ledger and preview flow Add an append-only ledger of entitlement set rule changes with a preview-and-commit operator flow. Rule writes now go through an enclosed `core.commit_rule_change` function that files an act row and one obligation per carrying pool, with a drain workflow settling deferred recomputations. The preview dry-runs the materializer with a rule overlay and renders per-pool buckets, reduction-policy disclosures, and provider over-limit consequences. Materializing transactions take a shared advisory rendezvous that rule changes hold exclusively, enforced by a possession assertion. Add History and Entitlement changes surfaces, a rule-less warning on five product-selection surfaces, and a `tier_reduction_policy` column that gates FedWiki parking.
79 lines
2.3 KiB
Go
79 lines
2.3 KiB
Go
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Commercial
|
|
// SPDX-FileCopyrightText: 2025-2026 Christian Galo
|
|
|
|
package server
|
|
|
|
// DB-backed handler-wiring test for ux-ia-naming 2.2: loadPlanLaddersPageData
|
|
// must hydrate PlanLaddersData.Topology by reusing loadPlanTopologyData, so
|
|
// GET /operator/plan-ladders loads both data sets from one page load. The
|
|
// render-only assembly (template include, heading order, health-strip
|
|
// states) is covered by operator_plan_ladders_mapfirst_render_test.go; this
|
|
// test locks the Go-side wiring that feeds it.
|
|
|
|
import (
|
|
"context"
|
|
"io"
|
|
"log/slog"
|
|
"net/http/httptest"
|
|
"testing"
|
|
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/billing"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/entitlements"
|
|
"git.coopcloud.tech/wiki-cafe/member-console/internal/organization"
|
|
"github.com/google/uuid"
|
|
_ "github.com/jackc/pgx/v5/stdlib"
|
|
)
|
|
|
|
func TestLoadPlanLaddersPageDataIncludesTopology(t *testing.T) {
|
|
database := topoTestDB(t)
|
|
ctx := context.Background()
|
|
|
|
tx, err := entitlements.BeginMaterializing(ctx, database, nil)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
defer tx.Rollback()
|
|
|
|
bq := billing.New(tx)
|
|
eq := entitlements.New(tx)
|
|
oq := organization.New(tx)
|
|
|
|
ladder, err := bq.CreatePlanLadder(ctx, billing.CreatePlanLadderParams{
|
|
Name: "Map First Ladder " + uuid.New().String()[:8],
|
|
IsActive: true,
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("create ladder: %v", err)
|
|
}
|
|
|
|
h := &OperatorPartialsHandler{
|
|
BillingQ: bq,
|
|
EntitlementsQ: eq,
|
|
OrgQ: oq,
|
|
Database: database,
|
|
Logger: slog.New(slog.NewTextHandler(io.Discard, nil)),
|
|
}
|
|
req := httptest.NewRequest("GET", "/operator/plan-ladders", nil).WithContext(ctx)
|
|
data := h.loadPlanLaddersPageData(req, "", "")
|
|
|
|
foundInList := false
|
|
for _, l := range data.Ladders {
|
|
if l.PlanLadderID == ladder.PlanLadderID {
|
|
foundInList = true
|
|
}
|
|
}
|
|
if !foundInList {
|
|
t.Errorf("expected the created ladder in the management list (data.Ladders)")
|
|
}
|
|
|
|
foundInTopology := false
|
|
for _, l := range data.Topology.Ladders {
|
|
if l.PlanLadderID == ladder.PlanLadderID {
|
|
foundInTopology = true
|
|
}
|
|
}
|
|
if !foundInTopology {
|
|
t.Errorf("expected the created ladder in the topology overview (data.Topology.Ladders) -- loadPlanLaddersPageData must hydrate Topology via loadPlanTopologyData")
|
|
}
|
|
}
|