create_test.gno
7.12 Kb · 189 lines
1package bolo
2
3import (
4 "chain"
5 "chain/banker"
6 "strings"
7 "testing"
8
9 "gno.land/p/nt/avl/v0"
10 "gno.land/p/nt/seqid/v0"
11 "gno.land/p/nt/testutils/v0"
12)
13
14// TestCreateCampaign creates campaigns as a user, straight from the test's body as
15// AssertOriginCall requires (plan §3.3), with the envelope and the realm's coins a
16// real transaction would bring. Its refusals are in txtar (create.txtar): a test
17// can't recover a crossing call's panic.
18func TestCreateCampaign(cur realm, t *testing.T) {
19 defer restoreCampaigns(nextID)
20 defer restoreSettings(admin, pendingAdmin, params, presets, minDonation, escrowCap, tipRecipient, paused, sunset, categoryOrder)
21
22 organizer := testutils.TestAddress("organizer")
23 beneficiary := testutils.TestAddress("beneficiary")
24 deposit := params.CreationDeposit
25 first := nextID + 1
26
27 testing.SetRealm(testing.NewUserRealm(organizer))
28 testing.SetOriginSend(chain.NewCoins(chain.NewCoin("ugnot", deposit)))
29 testing.IssueCoins(realmAddr(), chain.NewCoins(chain.NewCoin("ugnot", deposit)))
30 id := CreateCampaign(cross(cur), " Clean water for Ndiaganiao ", "A pump.", "water", "", "", beneficiary, 1_500_000_000, modeEscrow, 0)
31 if id != idString(first) {
32 t.Fatalf("CreateCampaign returned %q, want %q", id, idString(first))
33 }
34
35 // The deposit went to the bond address; the realm keeps nothing (INV-3).
36 rb := banker.NewReadonlyBanker()
37 if got := rb.GetCoin(bondAddr(first), "ugnot"); got != deposit {
38 t.Errorf("bond address holds %d, want the deposit %d", got, deposit)
39 }
40 if got := rb.GetCoin(realmAddr(), "ugnot"); got != 0 {
41 t.Errorf("the realm kept %d", got)
42 }
43
44 v := GetCampaign(id)
45 want := CampaignView{
46 ID: id, Title: "Clean water for Ndiaganiao", Description: "A pump.", Category: "water",
47 Organizer: organizer, Beneficiary: beneficiary, Mode: modeEscrow, Status: statusOpen, Listed: true,
48 Goal: 1_500_000_000, Deposit: deposit, CreatedAt: now(),
49 ClosableByAnyoneAt: now() + params.DormancyPeriod,
50 EscrowAddress: escrowAddr(first), BondAddress: bondAddr(first), Params: params,
51 }
52 if v != want {
53 t.Errorf("GetCampaign =\n%v\nwant\n%v", v, want)
54 }
55 key := first.Binary()
56 for name, tree := range map[string]*avl.Tree{
57 "campaigns": &campaigns,
58 "active": &active,
59 "activeByCategory": &activeByCategory,
60 "byOrganizer": &byOrganizer,
61 "byBeneficiary": &byBeneficiary,
62 } {
63 k := key
64 switch name {
65 case "activeByCategory":
66 k = "water/" + key
67 case "byOrganizer":
68 k = organizer.String() + "/" + key
69 case "byBeneficiary":
70 k = beneficiary.String() + "/" + key
71 }
72 if !tree.Has(k) {
73 t.Errorf("campaign %s is missing from %s", id, name)
74 }
75 }
76 if !derived.Has(escrowAddr(first).String()) || !derived.Has(bondAddr(first).String()) {
77 t.Errorf("campaign %s's escrow and bond addresses aren't recorded as derived", id)
78 }
79
80 // A change of Params applies to campaigns created afterwards only (INV-5).
81 testing.SetRealm(testing.NewUserRealm(adminAddr))
82 SetParams(cross(cur), 3_000_000, 30, 14, 7)
83 testing.SetRealm(testing.NewUserRealm(organizer))
84 testing.SetOriginSend(chain.NewCoins(chain.NewCoin("ugnot", 3_000_000)))
85 testing.IssueCoins(realmAddr(), chain.NewCoins(chain.NewCoin("ugnot", 3_000_000)))
86 second := CreateCampaign(cross(cur), "A second campaign", "", "food", "", "", beneficiary, 0, modeDirect, 0)
87 if v := GetCampaign(id); v.Params != want.Params || v.Deposit != deposit {
88 t.Errorf("SetParams changed campaign %s: %v", id, v.Params)
89 }
90 if v := GetCampaign(second); v.Params != GetParams() || v.Deposit != 3_000_000 {
91 t.Errorf("campaign %s didn't get the new Params: %v", second, v.Params)
92 }
93}
94
95func TestCheckCampaignTerms(t *testing.T) {
96 checkCampaignTerms("water", modeDirect, 0, 0)
97 checkCampaignTerms("water", modeEscrow, 1, now()+1)
98 checkCampaignTerms("water", modeEscrow, 1, now()+maxDeadlineDays*secondsPerDay)
99 for name, c := range map[string]struct {
100 category, mode string
101 goal, deadline int64
102 err Error
103 }{
104 "unknown category": {"rockets", modeDirect, 0, 0, ErrCategory},
105 "no mode": {"water", "", 0, 0, ErrMode},
106 "unknown mode": {"water", "both", 0, 0, ErrMode},
107 "negative goal": {"water", modeDirect, -1, 0, ErrGoal},
108 "deadline now": {"water", modeDirect, 0, now(), ErrDeadline},
109 "deadline past": {"water", modeDirect, 0, now() - 1, ErrDeadline},
110 "deadline too far": {"water", modeDirect, 0, now() + maxDeadlineDays*secondsPerDay + 1, ErrDeadline},
111 "deadline negative": {"water", modeDirect, 0, -1, ErrDeadline},
112 } {
113 if !panicsWith(func() { checkCampaignTerms(c.category, c.mode, c.goal, c.deadline) }, c.err) {
114 t.Errorf("%s: not refused with %s", name, c.err)
115 }
116 }
117}
118
119func TestCheckCampaignText(t *testing.T) {
120 text := checkCampaignText(" Clean water ", " A pump. ", "", "")
121 if text.title != "Clean water" || text.description != "A pump." {
122 t.Errorf("checkCampaignText = %v", text)
123 }
124 for name, c := range map[string]struct {
125 title, description, cid, alt string
126 err Error
127 }{
128 "short title": {"abc", "", "", "", ErrTitle},
129 "image in story": {"Clean water", "", "", "", ErrMarkup},
130 "photo without alt": {"Clean water", "", "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG", "", ErrImage},
131 } {
132 if !panicsWith(func() { checkCampaignText(c.title, c.description, c.cid, c.alt) }, c.err) {
133 t.Errorf("%s: not refused with %s", name, c.err)
134 }
135 }
136}
137
138func TestCheckBeneficiary(t *testing.T) {
139 next := seqid.ID(7)
140 checkBeneficiary(testutils.TestAddress("beneficiary"), next)
141 handedOut := escrowAddr(3)
142 derived.Set(handedOut.String(), true)
143 defer derived.Remove(handedOut.String())
144 for name, a := range map[string]address{
145 "malformed": "g1notanaddress",
146 "in capitals": address(strings.ToUpper(string(testutils.TestAddress("beneficiary")))),
147 "the realm": realmAddr(),
148 "slashed": slashedAddr(),
149 "the storage deposit": depositAddr(),
150 "the registry": registryAddr(),
151 "the registry's deposit": registryDepositAddr(),
152 "an escrow handed out": handedOut,
153 "the new campaign's escrow": escrowAddr(next),
154 "the new campaign's bond": bondAddr(next),
155 } {
156 if !panicsWith(func() { checkBeneficiary(a, next) }, ErrBeneficiary) {
157 t.Errorf("%s: not refused", name)
158 }
159 }
160}
161
162func TestGetCampaignNotFound(t *testing.T) {
163 for _, id := range []string{"", "0", "042", "+1", " 1", "999999"} {
164 if !panicsWith(func() { GetCampaign(id) }, ErrNotFound) {
165 t.Errorf("GetCampaign(%q): not ErrNotFound", id)
166 }
167 }
168}
169
170// restoreCampaigns removes every campaign created after last from every tree. IDs
171// keep counting: the bank keeps the coins those campaigns' addresses got, so a later
172// test's campaigns get fresh addresses.
173func restoreCampaigns(last seqid.ID) {
174 for id := last + 1; id <= nextID; id++ {
175 v := campaigns.Get(id.Binary())
176 if v == nil {
177 continue
178 }
179 c := v.(*campaign)
180 key := id.Binary()
181 campaigns.Remove(key)
182 active.Remove(key)
183 activeByCategory.Remove(c.category + "/" + key)
184 byOrganizer.Remove(c.organizer.String() + "/" + key)
185 byBeneficiary.Remove(c.beneficiary.String() + "/" + key)
186 derived.Remove(escrowAddr(id).String())
187 derived.Remove(bondAddr(id).String())
188 }
189}