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.

dice_test.gno

3.01 Kb · 123 lines
  1package dice
  2
  3import (
  4	"strings"
  5	"testing"
  6
  7	"gno.land/p/nt/avl/v0"
  8)
  9
 10// resetState clears package globals between tests since realm state is global.
 11func resetState() {
 12	count = [6]int{}
 13	total = 0
 14	players = avl.NewTree()
 15}
 16
 17func TestDeriveFaceRange(t *testing.T) {
 18	// Every derived face must land in [1,6] regardless of entropy inputs.
 19	cases := []struct {
 20		height int64
 21		addr   string
 22		n      int
 23	}{
 24		{0, "g1abc", 0},
 25		{1, "g1abc", 1},
 26		{999999, "g1xyzxyzxyz", 42},
 27		{-1, "", 0},
 28		{123456789, "g1deadbeef", 7},
 29	}
 30	for _, c := range cases {
 31		f := deriveFace(c.height, c.addr, c.n)
 32		if f < 1 || f > 6 {
 33			t.Errorf("deriveFace(%d,%q,%d) = %d, want 1..6", c.height, c.addr, c.n, f)
 34		}
 35	}
 36}
 37
 38func TestDeriveFaceDeterministic(t *testing.T) {
 39	// Same inputs -> same output (provably fair / reproducible).
 40	a := deriveFace(100, "g1caller", 3)
 41	b := deriveFace(100, "g1caller", 3)
 42	if a != b {
 43		t.Errorf("deriveFace not deterministic: %d != %d", a, b)
 44	}
 45	// Different roll count generally changes entropy; at least it must not panic
 46	// and must stay in range.
 47	c := deriveFace(100, "g1caller", 4)
 48	if c < 1 || c > 6 {
 49		t.Errorf("deriveFace out of range: %d", c)
 50	}
 51}
 52
 53func TestDeriveFaceVariesByCaller(t *testing.T) {
 54	// Two different addresses at the same height should not be forced equal;
 55	// verify the hash actually mixes the address by finding at least one
 56	// differing pair across a small sweep.
 57	diff := false
 58	for i := 0; i < 10; i++ {
 59		if deriveFace(50, "g1alice", i) != deriveFace(50, "g1bob", i) {
 60			diff = true
 61			break
 62		}
 63	}
 64	if !diff {
 65		t.Error("deriveFace never differs between two distinct callers over 10 rolls")
 66	}
 67}
 68
 69func TestRenderEmpty(t *testing.T) {
 70	resetState()
 71	out := Render("")
 72	if !strings.Contains(out, "Provably-Fair Dice") {
 73		t.Error("Render missing title")
 74	}
 75	if !strings.Contains(out, "Total rolls:** 0") {
 76		t.Errorf("Render should report 0 total rolls, got:\n%s", out)
 77	}
 78	if !strings.Contains(out, "No rolls yet") {
 79		t.Error("Render should show empty-state message")
 80	}
 81}
 82
 83func TestDistributionAccounting(t *testing.T) {
 84	resetState()
 85	// Simulate the state mutations Roll performs, without a realm frame.
 86	faces := []int{1, 1, 3, 6, 6, 6}
 87	for _, f := range faces {
 88		count[f-1]++
 89		total++
 90	}
 91	if total != len(faces) {
 92		t.Errorf("total = %d, want %d", total, len(faces))
 93	}
 94	d := Distribution()
 95	if d[0] != 2 {
 96		t.Errorf("face 1 count = %d, want 2", d[0])
 97	}
 98	if d[5] != 3 {
 99		t.Errorf("face 6 count = %d, want 3", d[5])
100	}
101	if d[1] != 0 {
102		t.Errorf("face 2 count = %d, want 0", d[1])
103	}
104	out := Render("")
105	if !strings.Contains(out, "Total rolls:** 6") {
106		t.Errorf("Render should report 6 total rolls, got:\n%s", out)
107	}
108}
109
110func TestBar(t *testing.T) {
111	full := bar(10, 10, 10)
112	if strings.Count(full, "▓") != 10 {
113		t.Errorf("full bar want 10 filled, got %q", full)
114	}
115	empty := bar(0, 10, 10)
116	if strings.Count(empty, "░") != 10 {
117		t.Errorf("empty bar want 10 unfilled, got %q", empty)
118	}
119	half := bar(5, 10, 10)
120	if strings.Count(half, "▓") != 5 {
121		t.Errorf("half bar want 5 filled, got %q", half)
122	}
123}