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

ref_test.gno

5.45 Kb · 155 lines
  1package forge
  2
  3import (
  4	"testing"
  5
  6	"gno.land/p/nt/uassert/v0"
  7	"gno.land/p/nt/urequire/v0"
  8)
  9
 10func TestSetRefCompareAndSwap(t *testing.T) {
 11	_, r := newRepo(t)
 12
 13	// Create: the expectation is "this ref does not exist".
 14	e, err := r.SetRef(carol, 101, "refs/heads/main", "", oid("a"), "initial import")
 15	urequire.NoError(t, err)
 16	uassert.Equal(t, KindCreate, e.Kind)
 17	uassert.Equal(t, int64(0), e.Seq)
 18	uassert.Equal(t, "", e.OldOID)
 19	uassert.Equal(t, oid("a"), r.Ref("refs/heads/main").OID)
 20	uassert.Equal(t, carol.String(), r.Ref("refs/heads/main").UpdatedBy.String())
 21
 22	// Creating twice is a mistake, not an overwrite.
 23	_, err = r.SetRef(carol, 102, "refs/heads/main", "", oid("b"), "")
 24	uassert.ErrorIs(t, err, ErrRefExists)
 25
 26	// A stale expectation is exactly the lost-push case --force-with-lease
 27	// exists to catch, and here the lease is held by consensus.
 28	_, err = r.SetRef(carol, 102, "refs/heads/main", oid("9"), oid("b"), "")
 29	uassert.ErrorIs(t, err, ErrStaleRef)
 30
 31	// A fresh expectation succeeds and chains onto the previous entry.
 32	e2, err := r.SetRef(carol, 103, "refs/heads/main", oid("a"), oid("b"), "fix the thing")
 33	urequire.NoError(t, err)
 34	uassert.Equal(t, KindUpdate, e2.Kind)
 35	uassert.Equal(t, oid("a"), e2.OldOID)
 36	uassert.Equal(t, EntryDigest(e.Digest, e2), e2.Digest, "digest commits to its predecessor")
 37	uassert.Equal(t, e2.Digest, r.LogHead())
 38
 39	// No-op writes are rejected: they would cost a deposit and record nothing.
 40	_, err = r.SetRef(carol, 104, "refs/heads/main", oid("b"), oid("b"), "")
 41	uassert.ErrorIs(t, err, ErrSameOID)
 42
 43	// Updating a ref that does not exist is not a create.
 44	_, err = r.SetRef(carol, 104, "refs/heads/other", oid("a"), oid("b"), "")
 45	uassert.ErrorIs(t, err, ErrRefNotFound)
 46}
 47
 48func TestSetRefAuthorizationAndValidation(t *testing.T) {
 49	_, r := newRepo(t)
 50
 51	_, err := r.SetRef(eve, 101, "refs/heads/main", "", oid("a"), "")
 52	uassert.ErrorIs(t, err, ErrUnauthorized, "a stranger cannot move a ref")
 53	_, err = r.SetRef(dave, 101, "refs/heads/main", "", oid("a"), "")
 54	uassert.ErrorIs(t, err, ErrUnauthorized, "a reader cannot move a ref")
 55
 56	_, err = r.SetRef(carol, 101, "main", "", oid("a"), "")
 57	uassert.ErrorIs(t, err, ErrInvalidRefName)
 58	_, err = r.SetRef(carol, 101, "refs/heads/main", "", "deadbeef", "")
 59	uassert.ErrorIs(t, err, ErrInvalidOID)
 60	_, err = r.SetRef(carol, 101, "refs/heads/main", "", oid("a"), "two\nlines")
 61	uassert.ErrorIs(t, err, ErrInvalidText, "a note is one line: it is a digest field")
 62}
 63
 64func TestForceAndDelete(t *testing.T) {
 65	_, r := newRepo(t)
 66	_, err := r.SetRef(carol, 101, "refs/heads/main", "", oid("a"), "initial")
 67	urequire.NoError(t, err)
 68	_, err = r.SetRef(carol, 102, "refs/heads/wip", "", oid("c"), "wip")
 69	urequire.NoError(t, err)
 70
 71	// A writer cannot force. A maintainer can, and it is on the record forever.
 72	_, err = r.ForceSetRef(carol, 103, "refs/heads/main", oid("d"), "rewrite")
 73	uassert.ErrorIs(t, err, ErrUnauthorized)
 74	e, err := r.ForceSetRef(bob, 103, "refs/heads/main", oid("d"), "drop the bad commit")
 75	urequire.NoError(t, err)
 76	uassert.Equal(t, KindForce, e.Kind)
 77	uassert.Equal(t, oid("a"), e.OldOID, "the abandoned tip stays in the log")
 78
 79	// Delete is compare-and-swap too, and the default branch is not deletable.
 80	_, err = r.DeleteRef(bob, 104, "refs/heads/main", oid("d"), "")
 81	uassert.ErrorIs(t, err, ErrUnauthorized)
 82	_, err = r.DeleteRef(bob, 104, "refs/heads/wip", oid("9"), "")
 83	uassert.ErrorIs(t, err, ErrStaleRef)
 84	_, err = r.DeleteRef(carol, 104, "refs/heads/wip", oid("c"), "")
 85	uassert.ErrorIs(t, err, ErrUnauthorized)
 86	de, err := r.DeleteRef(bob, 104, "refs/heads/wip", oid("c"), "merged")
 87	urequire.NoError(t, err)
 88	uassert.Equal(t, KindDelete, de.Kind)
 89	uassert.Equal(t, "", de.NewOID)
 90	uassert.Equal(t, 1, r.RefCount(), "the ref is gone")
 91	uassert.Equal(t, 4, r.LogSize(), "the history of it is not")
 92
 93	_, err = r.DeleteRef(bob, 105, "refs/heads/wip", "", "")
 94	uassert.ErrorIs(t, err, ErrRefNotFound)
 95}
 96
 97func TestLogChainVerifies(t *testing.T) {
 98	_, r := newRepo(t)
 99	urequire.Equal(t, "", r.LogHead())
100
101	for i, o := range []string{"a", "b", "c", "d"} {
102		expected := ""
103		if i > 0 {
104			expected = r.Ref("refs/heads/main").OID
105		}
106		_, err := r.SetRef(carol, int64(100+i), "refs/heads/main", expected, oid(o), "step")
107		urequire.NoError(t, err)
108	}
109	uassert.Equal(t, 4, r.LogSize())
110
111	ok, bad := r.VerifyLog()
112	uassert.True(t, ok, "an untouched log verifies")
113	uassert.Equal(t, int64(-1), bad)
114
115	// Rewrite history the only way it can be rewritten here: reaching into the
116	// package's own memory, and the chain detects it at the doctored entry.
117	r.LogEntryAt(1).NewOID = oid("f")
118	ok, bad = r.VerifyLog()
119	uassert.False(t, ok, "a doctored entry breaks the chain")
120	uassert.Equal(t, int64(1), bad)
121}
122
123func TestLogIteration(t *testing.T) {
124	_, r := newRepo(t)
125	for i, o := range []string{"a", "b", "c"} {
126		expected := ""
127		if i > 0 {
128			expected = r.Ref("refs/heads/main").OID
129		}
130		_, err := r.SetRef(carol, int64(100+i), "refs/heads/main", expected, oid(o), "")
131		urequire.NoError(t, err)
132	}
133
134	var fwd []string
135	r.IterateLog(0, 0, func(e *LogEntry) bool {
136		fwd = append(fwd, e.NewOID[:1])
137		return false
138	})
139	uassert.Equal(t, "a,b,c", join(fwd))
140
141	var rev []string
142	r.IterateLogReverse(0, 2, func(e *LogEntry) bool {
143		rev = append(rev, e.NewOID[:1])
144		return false
145	})
146	uassert.Equal(t, "c,b", join(rev), "newest-first, paginated")
147
148	var names []string
149	r.IterateRefs(func(ref *Ref) bool {
150		names = append(names, ref.Name)
151		return false
152	})
153	uassert.Equal(t, "refs/heads/main", join(names))
154	uassert.True(t, r.LogEntryAt(99) == nil)
155}