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

impl_test.gno

4.54 Kb · 108 lines
  1package impl
  2
  3import (
  4	"testing"
  5
  6	facade "gno.land/r/moul/x/upgrade/schema/facade/v0"
  7	bad "gno.land/r/moul/x/upgrade/schema/impl/bad/v0"
  8	implv0 "gno.land/r/moul/x/upgrade/schema/impl/v0"
  9
 10	"gno.land/p/nt/uassert/v0"
 11)
 12
 13// TestGrowTheAPI is the whole pattern: three handlers propose themselves, the
 14// API is enumerable without a transaction, it grows without a new signature at
 15// the permanent path, and the one candidate that would break a caller is
 16// refused despite satisfying the interface.
 17func TestGrowTheAPI(cur realm, t *testing.T) {
 18	testing.SetRealm(testing.NewUserRealm(facade.Ownable.Owner()))
 19	uassert.Equal(t, 3, len(facade.Candidates()), "all three handlers nominated themselves")
 20
 21	// v0: two verbs, and a caller can discover them with no transaction.
 22	facade.Accept(cross(cur), implv0.Path)
 23	uassert.Equal(t, "upper, repeat", joinVerbs(facade.Verbs()))
 24	uassert.Equal(t, "repeat(s, n)", facade.Signature("repeat"))
 25	uassert.Equal(t, "GNO", facade.Call(cross(cur), "upper", "gno"))
 26	uassert.Equal(t, "abab", facade.Call(cross(cur), "repeat", "ab|2"))
 27
 28	// The facade checks arity before the handler runs, so the message names the
 29	// signature rather than whatever the handler would have done.
 30	uassert.AbortsContains(t, cur, "verb repeat takes 2 argument(s), got 1, signature is repeat(s, n)", func() {
 31		facade.Call(cross(cur), "repeat", "ab")
 32	})
 33	uassert.AbortsContains(t, cur, "unknown verb nope, known: upper, repeat", func() {
 34		facade.Call(cross(cur), "nope", "")
 35	})
 36
 37	// v1 ADDS a verb and keeps the other two, so it is accepted. No new realm,
 38	// no new signature at the permanent path, and old callers are untouched.
 39	facade.Accept(cross(cur), Path)
 40	uassert.Equal(t, "upper, repeat, surround", joinVerbs(facade.Verbs()))
 41	uassert.Equal(t, "[gno]", facade.Call(cross(cur), "surround", "gno|[|]"))
 42	uassert.Equal(t, "GNO", facade.Call(cross(cur), "upper", "gno"), "v0's verbs still answer")
 43
 44	// The refusal. bad satisfies Handler exactly as well as the others do; what
 45	// it does is drop a verb and reshape another, which only the schema sees.
 46	uassert.AbortsContains(t, cur, "schema regression: the candidate drops verb upper", func() {
 47		facade.Accept(cross(cur), bad.Path)
 48	})
 49	uassert.Equal(t, Path, facade.Live(), "a refused accept changes nothing")
 50
 51	// The diff is SYMMETRIC, which the first draft of this test got wrong: now
 52	// that v1 has added a verb, rolling back to v0 DROPS it and is a regression
 53	// by the same rule. Accept refuses the rollback.
 54	uassert.AbortsContains(t, cur, "schema regression: the candidate drops verb surround", func() {
 55		facade.Accept(cross(cur), implv0.Path)
 56	})
 57	uassert.Equal(t, Path, facade.Live())
 58
 59	// Which is why the override exists. A pattern that can only move forward is
 60	// worse than one with no diff at all.
 61	facade.AcceptBreaking(cross(cur), implv0.Path)
 62	uassert.Equal(t, "upper, repeat", joinVerbs(facade.Verbs()))
 63	facade.Accept(cross(cur), Path)
 64
 65	uassert.Equal(t, "upper s\nrepeat s n\nsurround s left right\n", facade.SchemaText())
 66	uassert.Equal(t, "schema/facade/v0: gno.land/r/moul/x/upgrade/schema/impl/v1\n"+
 67		"- upper(s)\n- repeat(s, n)\n- surround(s, left, right)\n", facade.Render(""))
 68}
 69
 70// TestDispatchCost100 does 100 dispatches through this pattern's entry point,
 71// so `gno test -print-runtime-metrics` prints a cycle count for a known
 72// workload. Its twin is TestDispatchCost100 in adminreg/impl/gen1.
 73//
 74// It measures what a call costs through each pattern AS SHIPPED, which is the
 75// number that decides between them. It is NOT the cost of the string boundary
 76// in isolation: this path also crosses a realm and the typed one does not.
 77func TestDispatchCost100(cur realm, t *testing.T) {
 78	// Runs after TestGrowTheAPI, so v1 is already live. Accepting anything here
 79	// would fight that test's state, and the verb costs the same either way.
 80	for i := 0; i < 100; i++ {
 81		facade.Call(cross(cur), "upper", "gno")
 82	}
 83	uassert.Equal(t, "GNO", facade.Call(cross(cur), "upper", "gno"))
 84}
 85
 86// TestMalformedSchemaIsRejectedAtProposal checks the other validation edge: a
 87// schema that cannot be parsed never becomes a candidate.
 88func TestMalformedSchemaIsRejectedAtProposal(cur realm, t *testing.T) {
 89	uassert.AbortsContains(t, cur, "malformed schema", func() {
 90		facade.Propose(cross(cur), badSchema{})
 91	})
 92}
 93
 94type badSchema struct{}
 95
 96func (badSchema) Schema() string                         { return "Upper s" } // capital, not [a-z0-9_]
 97func (badSchema) Invoke(string, []string) string         { return "" }
 98
 99func joinVerbs(v []string) string {
100	out := ""
101	for i, s := range v {
102		if i > 0 {
103			out += ", "
104		}
105		out += s
106	}
107	return out
108}