package subscriptions
import (
"chain"
"chain/banker"
"chain/runtime"
"strings"
"testing"
"gno.land/p/nt/testutils/v0"
"gno.land/p/nt/uassert/v0"
)
var (
testAdmin = testutils.TestAddress("subadmin")
prov1 = testutils.TestAddress("subprov1")
prov2 = testutils.TestAddress("subprov2")
alice = testutils.TestAddress("subalice")
bob = testutils.TestAddress("subbob")
mallory = testutils.TestAddress("submallory")
)
// Established harness facts, carried from the sibling realms' suites:
// init runs with an empty origin context (the real deploy captures the
// origin caller); the banker resets between tests while realm globals
// persist, so conservation is asserted in DELTA form after re-seeding
// carried liabilities; testing.SetRealm affects only the frame that
// calls it; and testing.SkipHeights WIPES the origin/realm context, so
// every caller is re-armed after a skip.
func init() {
admin = testAdmin
feeRecipient = testAdmin
self = chain.PackageAddress("gno.land/r/g1qp5pt8cdq2f6kfdamn3yuxwg6quqnfpyewzz39/subscriptions")
}
type baseline struct{ held, users, fees int64 }
func begin() baseline {
if l := Liabilities(); l > 0 {
testing.IssueCoins(Address(), ugnot(l))
}
return snap()
}
func snap() baseline {
return baseline{held: Held(), users: UsersTotal(), fees: FeesAccrued()}
}
// conserved asserts ΔH == ΔU + ΔF + extraSurplus — the realm's monetary
// invariant in delta form. There is no escrow pool in this realm.
func (b baseline) conserved(t *testing.T, extraSurplus int64) {
t.Helper()
dH := Held() - b.held
dU := UsersTotal() - b.users
dF := FeesAccrued() - b.fees
if dH != dU+dF+extraSurplus {
t.Fatalf("conservation violated: ΔH=%d ΔU=%d ΔF=%d ΔS=%d", dH, dU, dF, extraSurplus)
}
if Liabilities() != UsersTotal()+FeesAccrued() {
t.Fatalf("Liabilities() = %d, want U+F", Liabilities())
}
}
func coinsOf(addr address) int64 {
return banker.NewReadonlyBanker().GetCoin(addr, Denom)
}
func ugnot(n int64) chain.Coins {
return chain.NewCoins(chain.NewCoin(Denom, n))
}
// NOTE ON THE HARNESS. testing.SetRealm applies to the frame that CALLS
// it, so the caller identity must be armed in the SAME frame as the
// entrypoint invocation — the three lines are inlined at every site,
// matching the sibling suites.
// planAs publishes a plan (non-payable: envelope explicitly cleared).
func planAs(cur realm, from address, title string, price, period int64) int64 {
testing.SetRealm(testing.NewUserRealm(from))
testing.SetOriginCaller(from)
testing.SetOriginSend(nil)
return CreatePlan(cross(cur), title, "d", price, period, MaxFeeBps)
}
// subscribeAs performs a chain-faithful subscribe: envelope staged AND landed.
func subscribeAs(cur realm, from address, planID, pay int64) int64 {
testing.SetRealm(testing.NewUserRealm(from))
testing.SetOriginCaller(from)
testing.SetOriginSend(ugnot(pay))
testing.IssueCoins(Address(), ugnot(pay))
return Subscribe(cross(cur), planID)
}
// renewAs performs a chain-faithful renewal.
func renewAs(cur realm, from address, subID, pay int64) {
testing.SetRealm(testing.NewUserRealm(from))
testing.SetOriginCaller(from)
testing.SetOriginSend(ugnot(pay))
testing.IssueCoins(Address(), ugnot(pay))
Renew(cross(cur), subID)
}
func TestInitialConfig(t *testing.T) {
if Admin() != testAdmin || FeeRecipient() != testAdmin {
t.Fatal("seeded roles mismatch")
}
var zero address
if PendingAdmin() != zero {
t.Fatal("pending admin not zero")
}
if FeeBps() != 0 || NumPlans() != 0 || NumSubs() != 0 {
t.Fatal("initial counters not zero")
}
}
func TestCreatePlanValidation(cur realm, t *testing.T) {
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(ugnot(5))
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
CreatePlan(cross(cur), "x", "d", 1, MinPeriodBlocks, MaxFeeBps)
})
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "title must be", func() {
CreatePlan(cross(cur), "", "d", 1, MinPeriodBlocks, MaxFeeBps)
})
uassert.AbortsContains(t, cur, "title must be", func() {
CreatePlan(cross(cur), strings.Repeat("t", MaxTitleLen+1), "d", 1, MinPeriodBlocks, MaxFeeBps)
})
uassert.AbortsContains(t, cur, "description too long", func() {
CreatePlan(cross(cur), "x", strings.Repeat("d", MaxDescLen+1), 1, MinPeriodBlocks, MaxFeeBps)
})
uassert.AbortsContains(t, cur, "price must be at least", func() {
CreatePlan(cross(cur), "x", "d", 0, MinPeriodBlocks, MaxFeeBps)
})
uassert.AbortsContains(t, cur, "periodBlocks must be in", func() {
CreatePlan(cross(cur), "x", "d", 1, MinPeriodBlocks-1, MaxFeeBps)
})
uassert.AbortsContains(t, cur, "periodBlocks must be in", func() {
CreatePlan(cross(cur), "x", "d", 1, MaxPeriodBlocks+1, MaxFeeBps)
})
}
func TestFeeConsentCeiling(cur realm, t *testing.T) {
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
SetFeeBps(cross(cur), 250)
// A provider who signed for at most 100 bps is refused while the
// fee is 250.
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "exceeds the caller's maximum", func() {
CreatePlan(cross(cur), "capped", "d", 100, MinPeriodBlocks, 100)
})
// A consenting provider snapshots 250 into the plan; a later fee
// change does not touch it.
id := planAs(cur, prov1, "snap", 1000, MinPeriodBlocks)
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
SetFeeBps(cross(cur), 0)
_, _, _, _, planFee, _, _ := PlanInfo(id)
if planFee != 250 {
t.Fatalf("plan feeBps = %d, want the 250 snapshot", planFee)
}
// The subscription charges the SNAPSHOT, not the current fee.
b := begin()
subscribeAs(cur, alice, id, 1000)
if got := FeesAccrued() - b.fees; got != 25 {
t.Fatalf("fee charged = %d, want 25 (250 bps of 1000)", got)
}
if got := BalanceOf(prov1); got == 0 {
t.Fatal("provider not credited")
}
b.conserved(t, 0)
}
func TestPlanQuota(cur realm, t *testing.T) {
for i := 0; i < MaxPlansPerProvider; i++ {
planAs(cur, prov2, "q", 1, MinPeriodBlocks)
}
testing.SetRealm(testing.NewUserRealm(prov2))
testing.SetOriginCaller(prov2)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "per-provider plan limit reached", func() {
CreatePlan(cross(cur), "q", "d", 1, MinPeriodBlocks, MaxFeeBps)
})
// Retiring one frees the slot.
RetirePlan(cross(cur), NumPlans()-1)
planAs(cur, prov2, "q2", 1, MinPeriodBlocks)
}
func TestRetirePlanAuthority(cur realm, t *testing.T) {
id := planAs(cur, prov1, "mine", 5, MinPeriodBlocks)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "only the provider may retire a plan", func() {
RetirePlan(cross(cur), id)
})
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(nil)
RetirePlan(cross(cur), id)
uassert.AbortsWithMessage(t, cur, "plan is not active", func() {
RetirePlan(cross(cur), id)
})
// No new subscriptions on a retired plan.
testing.SetRealm(testing.NewUserRealm(alice))
testing.SetOriginCaller(alice)
testing.SetOriginSend(ugnot(5))
testing.IssueCoins(Address(), ugnot(5))
uassert.AbortsWithMessage(t, cur, "plan is not active", func() {
Subscribe(cross(cur), id)
})
}
func TestSubscribeLifecycle(cur realm, t *testing.T) {
id := planAs(cur, prov1, "basic", 100, 50)
b := begin()
h0 := runtime.ChainHeight()
subID := subscribeAs(cur, alice, id, 100)
planID, subscriber, provider, price, paidThrough, periods, status := SubInfo(subID)
if planID != id || subscriber != alice || provider != prov1 || price != 100 ||
periods != 1 || status != SubActive {
t.Fatal("subscription fields wrong")
}
if paidThrough != h0+50 {
t.Fatalf("paidThrough = %d, want %d", paidThrough, h0+50)
}
if !Entitled(subID) || !EntitledFor(id, alice) {
t.Fatal("fresh subscription not entitled")
}
if got, ok := ActiveSubID(id, alice); !ok || got != subID {
t.Fatal("active index wrong")
}
b.conserved(t, 0)
// Exact envelope: under- and overpayment refused.
for _, bad := range []int64{99, 101} {
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(ugnot(bad))
testing.IssueCoins(Address(), ugnot(bad))
uassert.AbortsContains(t, cur, "send exactly the plan price", func() {
Subscribe(cross(cur), id)
})
}
// A second live subscription to the same plan is refused — the
// identity-level duplicate-payment guard.
testing.SetRealm(testing.NewUserRealm(alice))
testing.SetOriginCaller(alice)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "renew it instead", func() {
Subscribe(cross(cur), id)
})
}
func TestRenewalWindow(cur realm, t *testing.T) {
id := planAs(cur, prov1, "window", 100, 50)
subID := subscribeAs(cur, bob, id, 100)
_, _, _, _, pt0, _, _ := SubInfo(subID)
from, until := RenewableFrom(subID)
if from != pt0-50 || until != pt0+49 {
t.Fatalf("window [%d,%d], want [%d,%d]", from, until, pt0-50, pt0+49)
}
// Straight after Subscribe, remaining entitlement equals exactly
// one period (the running one), so ONE prepay renewal is inside
// the early bound — and extension is FROM paidThrough.
b := begin()
renewAs(cur, bob, subID, 100)
_, _, _, _, pt1, periods, _ := SubInfo(subID)
if pt1 != pt0+50 || periods != 2 {
t.Fatalf("paidThrough = %d periods = %d, want %d and 2", pt1, periods, pt0+50)
}
b.conserved(t, 0)
// A SECOND renewal in the same block is the accidental duplicate,
// and the early bound refuses it: more than one unstarted period
// would be prepaid.
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "too early", func() {
Renew(cross(cur), subID)
})
// Wrong payer: a third party cannot extend someone else's
// subscription even inside the window.
testing.SkipHeights(50)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsWithMessage(t, cur, "only the subscriber may renew", func() {
Renew(cross(cur), subID)
})
// Wrong amount inside the window.
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(ugnot(99))
testing.IssueCoins(Address(), ugnot(99))
uassert.AbortsContains(t, cur, "send exactly the subscription price", func() {
Renew(cross(cur), subID)
})
}
func TestGraceBoundary(cur realm, t *testing.T) {
id := planAs(cur, prov1, "grace", 100, 50)
subID := subscribeAs(cur, alice, id, 100)
_, _, _, _, pt, _, _ := SubInfo(subID)
// Move to the LAST renewable height: h == paidThrough +
// periodBlocks - 1 (the late bound is exclusive — audit Y2).
testing.SkipHeights(pt + 49 - runtime.ChainHeight())
if Entitled(subID) {
t.Fatal("entitlement must have lapsed past paidThrough")
}
// Renewal at the boundary is accepted, extends FROM paidThrough —
// and buys at least one block: the Y2 regression.
renewAs(cur, alice, subID, 100)
_, _, _, _, pt1, _, _ := SubInfo(subID)
if pt1 != pt+50 {
t.Fatalf("paidThrough = %d, want %d (extension from paidThrough)", pt1, pt+50)
}
if !Entitled(subID) {
t.Fatal("a boundary renewal must buy at least one block of entitlement")
}
// At the grace end itself renewal is refused, Expire opens: the
// two sets partition exactly.
testing.SkipHeights(pt1 + 50 - runtime.ChainHeight())
testing.SetRealm(testing.NewUserRealm(alice))
testing.SetOriginCaller(alice)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "grace window over", func() {
Renew(cross(cur), subID)
})
// Expire is permissionless: mallory may.
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
Expire(cross(cur), subID)
_, _, _, _, _, _, status := SubInfo(subID)
if status != SubExpired {
t.Fatal("not expired")
}
if EntitledFor(id, alice) {
t.Fatal("expired subscription still indexed")
}
// Terminal: renewal refused by status.
testing.SetRealm(testing.NewUserRealm(alice))
testing.SetOriginCaller(alice)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "subscription is expired", func() {
Renew(cross(cur), subID)
})
// The slot is free: a fresh Subscribe works.
subscribeAs(cur, alice, id, 100)
}
func TestExpireGuards(cur realm, t *testing.T) {
id := planAs(cur, prov1, "expguard", 100, 50)
subID := subscribeAs(cur, bob, id, 100)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "not expirable", func() {
Expire(cross(cur), subID)
})
// One block before the grace end it is still not expirable...
_, _, _, _, pt, _, _ := SubInfo(subID)
testing.SkipHeights(pt + 49 - runtime.ChainHeight())
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "not expirable", func() {
Expire(cross(cur), subID)
})
// ...and AT the grace end it is: the sets partition exactly.
testing.SkipHeights(1)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
Expire(cross(cur), subID)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "subscription is expired", func() {
Expire(cross(cur), subID)
})
}
func TestSubscribeCollapsesExpiredIncumbent(cur realm, t *testing.T) {
id := planAs(cur, prov1, "collapse", 100, 50)
oldID := subscribeAs(cur, alice, id, 100)
_, _, _, _, pt, _, _ := SubInfo(oldID)
testing.SkipHeights(pt + 50 - runtime.ChainHeight())
// No Expire call happened; Subscribe collapses the incumbent
// itself, at the first expirable height.
newID := subscribeAs(cur, alice, id, 100)
if newID == oldID {
t.Fatal("expected a fresh subscription id")
}
_, _, _, _, _, _, oldStatus := SubInfo(oldID)
if oldStatus != SubExpired {
t.Fatalf("incumbent status = %q, want expired", oldStatus)
}
if got, ok := ActiveSubID(id, alice); !ok || got != newID {
t.Fatal("index does not point at the fresh subscription")
}
}
func TestCancel(cur realm, t *testing.T) {
id := planAs(cur, prov1, "cancel", 100, 50)
subID := subscribeAs(cur, bob, id, 100)
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "only the subscriber may cancel", func() {
Cancel(cross(cur), subID)
})
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(nil)
Cancel(cross(cur), subID)
_, _, _, _, _, _, status := SubInfo(subID)
if status != SubCancelled {
t.Fatal("not cancelled")
}
// Paid entitlement survives cancellation until paidThrough — on
// both surfaces (audit Y1)...
if !Entitled(subID) || !EntitledFor(id, bob) {
t.Fatal("cancelled subscription lost its paid entitlement")
}
// ...but the ACTIVE slot is gone and renewal is refused forever.
if _, ok := ActiveSubID(id, bob); ok {
t.Fatal("cancelled subscription still holds the active slot")
}
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "subscription is cancelled", func() {
Renew(cross(cur), subID)
})
testing.SetRealm(testing.NewUserRealm(bob))
testing.SetOriginCaller(bob)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "subscription is cancelled", func() {
Cancel(cross(cur), subID)
})
// The slot freed: bob may start a fresh subscription at once.
subscribeAs(cur, bob, id, 100)
}
func TestRenewRefusedOnRetiredPlan(cur realm, t *testing.T) {
id := planAs(cur, prov1, "retire-mid", 100, 50)
subID := subscribeAs(cur, alice, id, 100)
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(nil)
RetirePlan(cross(cur), id)
// Entitlement continues...
if !Entitled(subID) {
t.Fatal("entitlement must survive plan retirement")
}
// ...but renewal is refused even inside the window.
_, _, _, _, pt, _, _ := SubInfo(subID)
testing.SkipHeights(pt - runtime.ChainHeight())
testing.SetRealm(testing.NewUserRealm(alice))
testing.SetOriginCaller(alice)
testing.SetOriginSend(ugnot(100))
testing.IssueCoins(Address(), ugnot(100))
uassert.AbortsContains(t, cur, "plan is retired", func() {
Renew(cross(cur), subID)
})
}
func TestSubscriberQuota(cur realm, t *testing.T) {
// Fill carol's quota with one-plan-each subscriptions, then refuse.
// A dedicated provider: prov2's plan quota is already saturated by
// TestPlanQuota, and realm globals persist across tests.
carol := testutils.TestAddress("subcarol")
provQ := testutils.TestAddress("subprovq")
var lastPlan int64
for i := 0; i < MaxSubsPerSubscriber; i++ {
lastPlan = planAs(cur, provQ, "quota", 1, MinPeriodBlocks)
subscribeAs(cur, carol, lastPlan, 1)
// Free the provider quota slot for the next plan.
testing.SetRealm(testing.NewUserRealm(provQ))
testing.SetOriginCaller(provQ)
testing.SetOriginSend(nil)
RetirePlan(cross(cur), lastPlan)
}
extra := planAs(cur, provQ, "quota-x", 1, MinPeriodBlocks)
testing.SetRealm(testing.NewUserRealm(carol))
testing.SetOriginCaller(carol)
testing.SetOriginSend(ugnot(1))
testing.IssueCoins(Address(), ugnot(1))
uassert.AbortsWithMessage(t, cur, "per-subscriber subscription limit reached", func() {
Subscribe(cross(cur), extra)
})
// Cancelling one frees a slot.
sid, ok := ActiveSubID(lastPlan, carol)
if !ok {
t.Fatal("expected an active sub to free")
}
testing.SetRealm(testing.NewUserRealm(carol))
testing.SetOriginCaller(carol)
testing.SetOriginSend(nil)
Cancel(cross(cur), sid)
subscribeAs(cur, carol, extra, 1)
testing.SetRealm(testing.NewUserRealm(provQ))
testing.SetOriginCaller(provQ)
testing.SetOriginSend(nil)
RetirePlan(cross(cur), extra)
}
func TestClaimsAndFees(cur realm, t *testing.T) {
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
SetFeeBps(cross(cur), 500) // 5% into the next plan
id := planAs(cur, prov1, "money", 1000, 50)
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
SetFeeBps(cross(cur), 0)
b := begin()
subscribeAs(cur, alice, id, 1000)
if FeesAccrued()-b.fees != 50 {
t.Fatalf("fee = %d, want 50", FeesAccrued()-b.fees)
}
credited := BalanceOf(prov1)
// Mallory has no balance: claims fail.
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "insufficient balance", func() { Claim(cross(cur), 1) })
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "nothing to claim", func() { ClaimAll(cross(cur)) })
// Provider claims all; coins actually move.
before := coinsOf(prov1)
testing.SetRealm(testing.NewUserRealm(prov1))
testing.SetOriginCaller(prov1)
testing.SetOriginSend(nil)
ClaimAll(cross(cur))
if coinsOf(prov1)-before != credited {
t.Fatalf("provider received %d, want %d", coinsOf(prov1)-before, credited)
}
if BalanceOf(prov1) != 0 {
t.Fatal("balance not drained")
}
// Fees: only the recipient, and only the pot.
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "fee recipient only", func() { WithdrawFees(cross(cur)) })
potBefore := coinsOf(testAdmin)
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
WithdrawFees(cross(cur))
if coinsOf(testAdmin)-potBefore == 0 {
t.Fatal("fee pot did not move")
}
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "no fees accrued", func() { WithdrawFees(cross(cur)) })
b.conserved(t, 0)
}
func TestSweepReserve(cur realm, t *testing.T) {
id := planAs(cur, prov1, "sweep", 500, 50)
begin()
subscribeAs(cur, bob, id, 500)
// Nothing above the reserve: sweep refuses.
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "no surplus to sweep", func() {
SweepDenom(cross(cur), Denom)
})
// Out-of-band surplus lands; only the surplus is sweepable.
testing.IssueCoins(Address(), ugnot(77))
liabBefore := Liabilities()
got := coinsOf(testAdmin)
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
SweepDenom(cross(cur), Denom)
if coinsOf(testAdmin)-got != 77 {
t.Fatalf("swept %d, want 77", coinsOf(testAdmin)-got)
}
if Liabilities() != liabBefore {
t.Fatal("sweep touched the reserve")
}
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "fee recipient only", func() {
SweepDenom(cross(cur), Denom)
})
}
func TestAdminHandover(cur realm, t *testing.T) {
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "admin only", func() {
TransferAdmin(cross(cur), mallory)
})
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
TransferAdmin(cross(cur), prov2)
// Wrong acceptor refused; authority has not moved yet.
testing.SetRealm(testing.NewUserRealm(mallory))
testing.SetOriginCaller(mallory)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "only the staged successor may accept", func() {
AcceptAdmin(cross(cur))
})
if Admin() != testAdmin {
t.Fatal("authority moved before acceptance")
}
testing.SetRealm(testing.NewUserRealm(prov2))
testing.SetOriginCaller(prov2)
testing.SetOriginSend(nil)
AcceptAdmin(cross(cur))
if Admin() != prov2 {
t.Fatal("authority did not move")
}
// The former admin is locked out.
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "admin only", func() {
SetFeeBps(cross(cur), 1)
})
// Hand it back for the remaining tests.
testing.SetRealm(testing.NewUserRealm(prov2))
testing.SetOriginCaller(prov2)
testing.SetOriginSend(nil)
TransferAdmin(cross(cur), testAdmin)
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
AcceptAdmin(cross(cur))
}
func TestFeeBounds(cur realm, t *testing.T) {
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "fee must be in", func() {
SetFeeBps(cross(cur), MaxFeeBps+1)
})
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsContains(t, cur, "fee must be in", func() {
SetFeeBps(cross(cur), -1)
})
var zero address
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "fee recipient must not be the zero address", func() {
SetFeeRecipient(cross(cur), zero)
})
testing.SetRealm(testing.NewUserRealm(testAdmin))
testing.SetOriginCaller(testAdmin)
testing.SetOriginSend(nil)
uassert.AbortsWithMessage(t, cur, "successor must not be the zero address", func() {
TransferAdmin(cross(cur), zero)
})
}
func TestStraySendRejected(cur realm, t *testing.T) {
id := planAs(cur, prov1, "stray", 100, 50)
subID := subscribeAs(cur, alice, id, 100)
arm := func(from address) {
testing.SetRealm(testing.NewUserRealm(from))
testing.SetOriginCaller(from)
testing.SetOriginSend(ugnot(3))
}
arm(prov1)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
RetirePlan(cross(cur), id)
})
arm(alice)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
Cancel(cross(cur), subID)
})
arm(mallory)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
Expire(cross(cur), subID)
})
arm(prov1)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
ClaimAll(cross(cur))
})
arm(testAdmin)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
WithdrawFees(cross(cur))
})
arm(testAdmin)
uassert.AbortsWithMessage(t, cur, "this function does not accept coins", func() {
SetFeeBps(cross(cur), 0)
})
}
func TestRenderSanitizes(cur realm, t *testing.T) {
planAs(cur, prov1, "[evil](https://x) ", 1, MinPeriodBlocks)
out := Render("")
if strings.Contains(out, "