1package coinflipduel23import (4 "testing"56 "gno.land/p/nt/avl/v0"7 "gno.land/p/nt/testutils/v0"8)910func resetState() {11 duels = avl.Tree{}12 players = avl.Tree{}13 nextID = 014 flipNonce = 015 totalDone = 016 var zero address17 champion = zero18 championWins = 019}2021func TestParseCall(t *testing.T) {22 for _, s := range []string{"heads", "Heads", " h ", "tails", "T"} {23 if _, ok := parseCall(s); !ok {24 t.Fatalf("parseCall(%q) should be valid", s)25 }26 }27 if _, ok := parseCall("sideways"); ok {28 t.Fatal(`parseCall("sideways") should be invalid`)29 }30}3132// Crossing: Challenge and Accept mutate realm state, so the test needs `cur realm`.33func TestChallengeAndAccept(cur realm, t *testing.T) {34 resetState()35 alice := testutils.TestAddress("alice")36 bob := testutils.TestAddress("bob")3738 testing.SetRealm(testing.NewUserRealm(alice))39 Challenge(cross(cur), bob.String(), "heads")4041 if !duels.Has("1") {42 t.Fatal("expected duel #1 to exist")43 }4445 testing.SetRealm(testing.NewUserRealm(bob))46 Accept(cross(cur), "1")4748 v := duels.Get("1")49 d := v.(*duel)50 if d.Status != statusResolved {51 t.Fatalf("Status = %v, want resolved", d.Status)52 }53 if d.Result != "heads" && d.Result != "tails" {54 t.Fatalf("Result = %q, want heads or tails", d.Result)55 }56 if totalDone != 1 {57 t.Fatalf("totalDone = %d, want 1", totalDone)58 }5960 winnerStats := getOrCreate(d.Winner)61 if winnerStats.Wins != 1 {62 t.Fatalf("winner Wins = %d, want 1", winnerStats.Wins)63 }64 if !champion.IsValid() || champion != d.Winner {65 t.Fatal("champion should be the duel winner")66 }67}6869func TestChallengeSelfRejected(cur realm, t *testing.T) {70 resetState()71 alice := testutils.TestAddress("alice")72 testing.SetRealm(testing.NewUserRealm(alice))7374 // Challenge panics inside a cross-realm call; a plain defer/recover in75 // this test does not catch a cross-boundary panic, so use revive().76 rec := revive(func() { Challenge(cross(cur), alice.String(), "heads") })77 if rec == nil {78 t.Fatal("expected panic when challenging yourself")79 }80}8182func TestOnlyOpponentCanAccept(cur realm, t *testing.T) {83 resetState()84 alice := testutils.TestAddress("alice")85 bob := testutils.TestAddress("bob")86 mallory := testutils.TestAddress("mallory")8788 testing.SetRealm(testing.NewUserRealm(alice))89 Challenge(cross(cur), bob.String(), "tails")9091 testing.SetRealm(testing.NewUserRealm(mallory))92 rec := revive(func() { Accept(cross(cur), "1") })93 if rec == nil {94 t.Fatal("expected panic when a non-opponent accepts")95 }96}9798func TestCancel(cur realm, t *testing.T) {99 resetState()100 alice := testutils.TestAddress("alice")101 bob := testutils.TestAddress("bob")102103 testing.SetRealm(testing.NewUserRealm(alice))104 Challenge(cross(cur), bob.String(), "heads")105 Cancel(cross(cur), "1")106107 v := duels.Get("1")108 d := v.(*duel)109 if d.Status != statusCancelled {110 t.Fatalf("Status = %v, want cancelled", d.Status)111 }112}113114// Non-crossing: Render is a plain read.115func TestRenderHome(t *testing.T) {116 resetState()117 out := Render("")118 if out == "" {119 t.Fatal(`Render("") should not be empty`)120 }121}122Accept(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id string) string
Cancel(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, id string) string
Challenge(cur interface {.seal func(); Address func() .uverse.address; IsCode func() bool; IsCurrent func() bool; IsEphemeral func() bool; IsUser func() bool; IsUserCall func() bool; IsUserRun func() bool; PkgPath func() string; Previous func() .uverse.realm; String func() string; Sub func(string) .uverse.realm; Subpath func() string}, opponentAddr string, call string) string
Render(path string) string
Signatures reconstructed verbatim from vm/qfuncs — interface params keep their inline definitions.
vm/qrender output, sanitized (docs/render-security.md) and displayed in an empty-sandbox iframe — scripts, forms and popups cannot run. Links stay inert in-preview; right-click to open.