Skip to content Search Apps Documentation Source Content File Folder Download Copy Actions State String Boolean Number Struct Map Slice Pointer Function Closure Reference Nil Package Type Interface Unknown

Community realm. Read the code first. Community realm, deployed by its author. Read the code before you interact or send coins.

views_test.gno

5.32 Kb · 140 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"testing"
  6
  7	"gno.land/p/nt/avl/v0"
  8	"gno.land/p/nt/seqid/v0"
  9	"gno.land/p/nt/testutils/v0"
 10)
 11
 12// testCampaign builds a campaign in memory, stored nowhere: views and the deadline
 13// helper read only what they are given.
 14func testCampaign(id seqid.ID, mode string, deadline int64) *campaign {
 15	return &campaign{
 16		id: id, category: "water", mode: mode, deadline: deadline, createdAt: now(),
 17		organizer: testutils.TestAddress("organizer"), beneficiary: testutils.TestAddress("beneficiary"),
 18		params:        Params{1_000_000, 90 * secondsPerDay, 60 * secondsPerDay, 30 * secondsPerDay},
 19		text:          &campaignText{title: "Clean water"},
 20		state:         &campaignState{status: statusOpen, listed: true},
 21		contributions: avl.NewTree(), receipts: avl.NewTree(), withdrawals: avl.NewTree(),
 22		refunds: avl.NewTree(), updates: avl.NewTree(),
 23	}
 24}
 25
 26const heightsPerDay = 17280 // five seconds a height (spike S10)
 27
 28func TestRecordDeadline(cur realm, t *testing.T) {
 29	c := testCampaign(9001, modeEscrow, now()+secondsPerDay)
 30	key := c.id.Binary()
 31	active.Set(key, true)
 32	activeByCategory.Set("water/"+key, true)
 33	defer dropActive(c)
 34
 35	recordDeadline(cur, c)
 36	if c.state.status != statusOpen || !active.Has(key) {
 37		t.Fatalf("closed before its deadline")
 38	}
 39	testing.SkipHeights(2 * heightsPerDay)
 40	recordDeadline(cur, c)
 41	s := c.state
 42	if s.status != statusClosed || s.closedAt != c.deadline || s.closeReason != reasonDeadline {
 43		t.Errorf("after the deadline: %s at %d (%s), want closed at the deadline %d", s.status, s.closedAt, s.closeReason, c.deadline)
 44	}
 45	if active.Has(key) || activeByCategory.Has("water/"+key) {
 46		t.Errorf("a closed campaign is still in the active indexes")
 47	}
 48	// A second call changes nothing.
 49	s.closeReason = reasonOrganizer
 50	recordDeadline(cur, c)
 51	if s.closeReason != reasonOrganizer {
 52		t.Errorf("recordDeadline rewrote a closed campaign")
 53	}
 54}
 55
 56func TestCampaignView(t *testing.T) {
 57	// Live Params unlike every test campaign's snapshot: a view reading them would
 58	// show it (INV-5).
 59	defer func(p Params) { params = p }(params)
 60	params = Params{CreationDeposit: 7, DormancyPeriod: 1, ExitDelay: 2, BondHold: 3}
 61
 62	// Open, no deadline: only dormancy has a time.
 63	c := testCampaign(9002, modeEscrow, 0)
 64	c.state.lastDonationAt = now() + 5
 65	v := campaignView(c)
 66	if v.Status != statusOpen || v.ClosableByAnyoneAt != now()+5+c.params.DormancyPeriod ||
 67		v.FinalizableAt != 0 || v.ExitOpensAt != 0 || v.BondReleasableAt != 0 || v.ExitOpen {
 68		t.Errorf("open, no deadline: %v", v)
 69	}
 70
 71	// Open with a deadline: finalizable at the deadline while nothing is tracked,
 72	// once the exit opens when something is.
 73	d := testCampaign(9003, modeEscrow, now()+10*secondsPerDay)
 74	if v := campaignView(d); v.FinalizableAt != d.deadline {
 75		t.Errorf("open, nothing tracked: FinalizableAt = %d, want %d", v.FinalizableAt, d.deadline)
 76	}
 77	d.state.raised = 500
 78	if v := campaignView(d); v.Balance != 500 || v.NetRaised != 500 || v.FinalizableAt != d.deadline+d.params.ExitDelay {
 79		t.Errorf("open, 500 tracked: %v", v)
 80	}
 81
 82	// Past the deadline: closed at it, without a write.
 83	testing.SkipHeights(11 * heightsPerDay)
 84	v = campaignView(d)
 85	if v.Status != statusClosed || v.CloseReason != reasonDeadline || v.ClosedAt != d.deadline {
 86		t.Errorf("past the deadline: %s, %s, %d", v.Status, v.CloseReason, v.ClosedAt)
 87	}
 88	if d.state.status != statusOpen {
 89		t.Errorf("a view wrote the close")
 90	}
 91	if v.ClosableByAnyoneAt != 0 || v.BondReleasableAt != d.deadline+d.params.BondHold ||
 92		v.ExitOpensAt != d.deadline+d.params.ExitDelay || v.FinalizableAt != v.ExitOpensAt || v.ExitOpen {
 93		t.Errorf("closed, never withdrawn: %v", v)
 94	}
 95
 96	// The exit window opens only after a withdrawal, once its delay has passed.
 97	d.state.everWithdrawn = true
 98	if campaignView(d).ExitOpen {
 99		t.Errorf("exit open before its delay")
100	}
101	testing.SkipHeights(61 * heightsPerDay)
102	if !campaignView(d).ExitOpen {
103		t.Errorf("exit not open after its delay")
104	}
105	d.state.everWithdrawn = false
106	if campaignView(d).ExitOpen {
107		t.Errorf("exit open after its delay without any withdrawal")
108	}
109	// Nor once everything tracked was withdrawn: there is nothing to reclaim.
110	d.state.everWithdrawn, d.state.withdrawn = true, d.state.raised
111	if campaignView(d).ExitOpen {
112		t.Errorf("exit open on an emptied escrow")
113	}
114
115	// Direct mode: nothing tracked, no exit, finalizable at the close.
116	e := testCampaign(9004, modeDirect, now()+secondsPerDay)
117	e.state.raised = 700
118	testing.SkipHeights(2 * heightsPerDay)
119	if v := campaignView(e); v.Balance != 0 || v.NetRaised != 700 || v.ExitOpensAt != 0 || v.FinalizableAt != e.deadline {
120		t.Errorf("direct, closed: %v", v)
121	}
122
123	// Settled: nothing left to finalize; the bond's release time stays.
124	e.state.status, e.state.closedAt, e.state.closeReason = statusSettled, e.deadline, reasonDeadline
125	if v := campaignView(e); v.FinalizableAt != 0 || v.BondReleasableAt != e.deadline+e.params.BondHold {
126		t.Errorf("settled: %v", v)
127	}
128
129	// Untracked: what the escrow holds beyond the books, never below 0.
130	f := testCampaign(9005, modeEscrow, 0)
131	f.state.raised = 300
132	testing.IssueCoins(escrowAddr(f.id), chain.NewCoins(chain.NewCoin("ugnot", 1000)))
133	if v := campaignView(f); v.Untracked != 700 {
134		t.Errorf("Untracked = %d, want 700", v.Untracked)
135	}
136	f.state.raised = 5000
137	if v := campaignView(f); v.Untracked != 0 {
138		t.Errorf("Untracked = %d, want 0 when the books exceed the bank", v.Untracked)
139	}
140}