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.

organizer_test.gno

6.79 Kb · 192 lines
  1package bolo
  2
  3import (
  4	"chain"
  5	"chain/banker"
  6	"math"
  7	"strings"
  8	"testing"
  9
 10	"gno.land/p/nt/seqid/v0"
 11	"gno.land/p/nt/testutils/v0"
 12)
 13
 14const testCID = "QmYwAPJzv5CZsnA625s3Xf2nemtYgPpHdWEz79ojWnPbdG"
 15
 16// storeCampaign stores c as CreateCampaign leaves a campaign, in campaigns and the
 17// active indexes, and returns its ID and what removes it again. The organizer's
 18// writes are called straight from each test's body, as AssertOriginCall requires
 19// (plan §3.3); their refusals are in txtar (organizer.txtar).
 20func storeCampaign(c *campaign) (string, func()) {
 21	key := c.id.Binary()
 22	campaigns.Set(key, c)
 23	active.Set(key, true)
 24	activeByCategory.Set(c.category+"/"+key, true)
 25	return idString(c.id), func() {
 26		campaigns.Remove(key)
 27		dropActive(c)
 28	}
 29}
 30
 31func TestPostBond(cur realm, t *testing.T) {
 32	c := testCampaign(9101, modeEscrow, 0)
 33	id, unstore := storeCampaign(c)
 34	defer unstore()
 35	rb := banker.NewReadonlyBanker()
 36	bondBefore, realmBefore := rb.GetCoin(bondAddr(c.id), "ugnot"), rb.GetCoin(realmAddr(), "ugnot")
 37
 38	testing.SetRealm(testing.NewUserRealm(c.organizer))
 39	testing.SetOriginSend(chain.NewCoins(chain.NewCoin("ugnot", 500)))
 40	testing.IssueCoins(realmAddr(), chain.NewCoins(chain.NewCoin("ugnot", 500)))
 41	PostBond(cross(cur), id)
 42	testing.SetOriginSend(chain.NewCoins(chain.NewCoin("ugnot", 300)))
 43	testing.IssueCoins(realmAddr(), chain.NewCoins(chain.NewCoin("ugnot", 300)))
 44	PostBond(cross(cur), id)
 45
 46	// The bond grew by both envelopes, at the bond address; the realm keeps nothing
 47	// (INV-3).
 48	if c.state.bond != 800 || GetCampaign(id).Bond != 800 {
 49		t.Errorf("bond = %d, want 800", c.state.bond)
 50	}
 51	if got := rb.GetCoin(bondAddr(c.id), "ugnot") - bondBefore; got != 800 {
 52		t.Errorf("the bond address got %d, want 800", got)
 53	}
 54	if got := rb.GetCoin(realmAddr(), "ugnot"); got != realmBefore {
 55		t.Errorf("the realm holds %d, it held %d", got, realmBefore)
 56	}
 57}
 58
 59func TestCheckBond(t *testing.T) {
 60	defer func(p bool) { paused = p }(paused)
 61	c := testCampaign(9102, modeEscrow, 0)
 62	checkBond(c, 1)
 63	c.state.bond = math.MaxInt64 - 5
 64	checkBond(c, 5)
 65	for name, k := range map[string]struct {
 66		status  string
 67		slashed bool
 68		paused  bool
 69		bond    int64
 70		amount  int64
 71		err     Error
 72	}{
 73		"nothing sent":             {statusOpen, false, false, 0, 0, ErrBadAmount},
 74		"a negative amount":        {statusOpen, false, false, 0, -1, ErrBadAmount},
 75		"closed":                   {statusClosed, false, false, 0, 1, ErrCampaignClosed},
 76		"settled":                  {statusSettled, false, false, 0, 1, ErrCampaignSettled},
 77		"slashed":                  {statusOpen, true, false, 0, 1, ErrBondSlashed},
 78		"paused":                   {statusOpen, false, true, 0, 1, ErrPaused},
 79		"past int64":               {statusOpen, false, false, math.MaxInt64 - 5, 6, ErrBadAmount},
 80		"nothing sent, and closed": {statusClosed, false, false, 0, 0, ErrBadAmount},
 81		"closed, and slashed":      {statusClosed, true, false, 0, 1, ErrCampaignClosed},
 82		"slashed, and paused":      {statusOpen, true, true, 0, 1, ErrBondSlashed},
 83	} {
 84		c.state.status, c.state.bondSlashed, c.state.bond = k.status, k.slashed, k.bond
 85		paused = k.paused
 86		if !panicsWith(func() { checkBond(c, k.amount) }, k.err) {
 87			t.Errorf("%s: not %s", name, k.err)
 88		}
 89	}
 90}
 91
 92func TestAssertOpen(t *testing.T) {
 93	c := testCampaign(9103, modeEscrow, 0)
 94	assertOpen(c)
 95	for status, err := range map[string]Error{statusClosed: ErrCampaignClosed, statusSettled: ErrCampaignSettled} {
 96		c.state.status = status
 97		if !panicsWith(func() { assertOpen(c) }, err) {
 98			t.Errorf("%s: not %s", status, err)
 99		}
100	}
101}
102
103func TestPostUpdate(cur realm, t *testing.T) {
104	defer func(p bool) { paused = p }(paused)
105	c := testCampaign(9104, modeEscrow, 0)
106	id, unstore := storeCampaign(c)
107	defer unstore()
108	testing.SkipHeights(10 * heightsPerDay)
109
110	testing.SetRealm(testing.NewUserRealm(c.organizer))
111	if seq := PostUpdate(cross(cur), id, "  Day one:\r\nthe pump is ordered.  "); seq != "1" {
112		t.Fatalf("first update: seq %q, want \"1\"", seq)
113	}
114	u := c.updates.Get(seqid.ID(1).Binary()).(*update)
115	if u.seq != 1 || u.at != now() || u.body != "Day one:\nthe pump is ordered." {
116		t.Errorf("stored update = %v", *u)
117	}
118	// An update resets the dormancy clock.
119	if s := c.state; s.updateCount != 1 || s.lastUpdateAt != now() || dormantAt(c) != now()+c.params.DormancyPeriod {
120		t.Errorf("after an update: count %d, last %d, dormant at %d", s.updateCount, s.lastUpdateAt, dormantAt(c))
121	}
122
123	// In every status, and while paused.
124	c.state.status = statusSettled
125	paused = true
126	if seq := PostUpdate(cross(cur), id, "The surgery went well."); seq != "2" {
127		t.Errorf("update on a settled campaign, paused: seq %q, want \"2\"", seq)
128	}
129	if c.state.updateCount != 2 || c.updates.Size() != 2 {
130		t.Errorf("%d updates counted, %d stored, want 2", c.state.updateCount, c.updates.Size())
131	}
132}
133
134func TestCheckUpdate(t *testing.T) {
135	if got := checkStory(" News ", updateMin, updateMax, ErrUpdate); got != "News" {
136		t.Errorf("checkStory = %q", got)
137	}
138	for name, body := range map[string]string{
139		"empty":            "",
140		"spaces":           "   \n  ",
141		"an accent alone":  "́",
142		"one too many":     strings.Repeat("a", updateMax+1),
143		"invisible filler": "ㅤ⠀",
144	} {
145		if !panicsWith(func() { checkStory(body, updateMin, updateMax, ErrUpdate) }, ErrUpdate) {
146			t.Errorf("%s: not ErrUpdate", name)
147		}
148	}
149	if !panicsWith(func() { checkStory("![x](https://example.com/x.png)", updateMin, updateMax, ErrUpdate) }, ErrMarkup) {
150		t.Errorf("an image in an update: not ErrMarkup")
151	}
152}
153
154func TestHideImage(cur realm, t *testing.T) {
155	// The organizer hides the photo; the CID stays, and hiding again changes nothing.
156	c := testCampaign(9105, modeEscrow, 0)
157	c.text = &campaignText{title: "Clean water", imageCID: testCID, imageAlt: "The well"}
158	id, unstore := storeCampaign(c)
159	defer unstore()
160	testing.SetRealm(testing.NewUserRealm(c.organizer))
161	HideImage(cross(cur), id)
162	if !c.state.imageHidden || c.text.imageCID != testCID || !GetCampaign(id).ImageHidden {
163		t.Errorf("organizer: hidden %t, CID %q", c.state.imageHidden, c.text.imageCID)
164	}
165	HideImage(cross(cur), id)
166	if !c.state.imageHidden {
167		t.Errorf("hiding again showed the photo")
168	}
169
170	// The moderator hides another campaign's photo.
171	d := testCampaign(9106, modeEscrow, 0)
172	d.text = &campaignText{title: "Clean water", imageCID: testCID, imageAlt: "The well"}
173	idD, unstoreD := storeCampaign(d)
174	defer unstoreD()
175	testing.SetRealm(testing.NewUserRealm(admin))
176	HideImage(cross(cur), idD)
177	if !d.state.imageHidden {
178		t.Errorf("moderator: the photo isn't hidden")
179	}
180
181	// A campaign without a photo has nothing to hide.
182	e := testCampaign(9107, modeEscrow, 0)
183	idE, unstoreE := storeCampaign(e)
184	defer unstoreE()
185	testing.SetRealm(testing.NewUserRealm(e.organizer))
186	HideImage(cross(cur), idE)
187	if e.state.imageHidden {
188		t.Errorf("a campaign without a photo got one hidden")
189	}
190}
191
192var passerby = testutils.TestAddress("passerby")