Artifact e8cf4459f93d750f1e08cabbf0fa2b672443f24252e3a62f1edcb56efb78c581:
- File
internal/blob/receipt_test.go
— part of check-in
[4add332299]
at
2026-10-18 20:30:31
on branch feat/submanifest-reader
— fix: record one rcvfrom row per receipt, as fossil does
Fossil records each arrival of content (a commit, a tag, a sync or clone, a request its server answers, an unversioned add) as one rcvfrom row, made by content_rcvid_init when the first content arrives, and points every blob and unversioned file it stores at that row. go-libfossil seeded a single row at creation and pointed every blob at rcvid 1.
db.Receipt is that row: it is made lazily on first use, with the time as a Julian day, and remade if the transaction that made it rolled back. A transaction records content under the receipt it is given, and repo.WithReceipt gives one to every transaction, bound ones included, of a commit, tag, sync, clone or served request. The client credits its local user and the peer's address, as fossil's client records g.zIpAddr; the server credits nobody until a login card names a user, then that user and the card's nonce, from the request's address. Phantoms keep rcvid 0, and an applied unversioned deletion keeps its row's rcvid, as in fossil.
A new repository no longer starts with a placeholder row; an existing one keeps it. (user: [email protected], size: 3050) [annotate] [blame] [check-ins using] [more...]
package blob import ( "testing" "github.com/danmestas/go-libfossil/db" ) func blobRcvid(t *testing.T, q db.Querier, uuid string) int64 { t.Helper() var rcvid int64 if err := q.QueryRow("SELECT rcvid FROM blob WHERE uuid=?", uuid).Scan(&rcvid); err != nil { t.Fatalf("read rcvid of %s: %v", uuid, err) } return rcvid } // Every blob stored in a transaction names that transaction's receipt, as // fossil's content_put_ex names g.rcvid; a phantom names none (content_new // stores rcvid 0), and filling it names the receipt that brought its content. func TestStoreRecordsTheReceipt(t *testing.T) { d := setupTestDB(t) // An earlier receipt holds rcvid 1, so a store that names 1 regardless // cannot pass for one that names its own receipt. if _, err := (&db.Receipt{UID: 9}).ID(d); err != nil { t.Fatalf("earlier receipt: %v", err) } rc := &db.Receipt{UID: 1} var full, delta, phantom string err := d.WithTx(func(tx *db.Tx) error { tx.UseReceipt(rc) _, u, err := Store(tx, []byte("first blob content, long enough to delta against\n")) if err != nil { return err } full = u src, _ := Exists(tx, full) _, u, err = StoreDelta(tx, []byte("first blob content, long enough to delta against!\n"), src) if err != nil { return err } delta = u phantom = "1111111111111111111111111111111111111111" _, err = StorePhantom(tx, phantom) return err }) if err != nil { t.Fatalf("WithTx: %v", err) } want, err := rc.ID(d) if err != nil { t.Fatalf("receipt id: %v", err) } if got := blobRcvid(t, d, full); got != want { t.Errorf("full blob rcvid = %d, want %d", got, want) } if got := blobRcvid(t, d, delta); got != want { t.Errorf("delta blob rcvid = %d, want %d", got, want) } if got := blobRcvid(t, d, phantom); got != 0 { t.Errorf("phantom rcvid = %d, want 0", got) } var rows int if err := d.QueryRow("SELECT count(*) FROM rcvfrom").Scan(&rows); err != nil { t.Fatalf("count rcvfrom: %v", err) } if rows != 2 { t.Errorf("rcvfrom rows = %d, want 2", rows) } // Storing content already held records nothing new. later := &db.Receipt{UID: 1} if err := d.WithTx(func(tx *db.Tx) error { tx.UseReceipt(later) _, _, err := Store(tx, []byte("first blob content, long enough to delta against\n")) return err }); err != nil { t.Fatalf("restore: %v", err) } if err := d.QueryRow("SELECT count(*) FROM rcvfrom").Scan(&rows); err != nil { t.Fatalf("count rcvfrom: %v", err) } if rows != 2 { t.Errorf("rcvfrom rows after storing held content = %d, want 2", rows) } } // Every stored blob names an rcvfrom row that exists, even one stored with no // receipt given. func TestStoreWithoutReceiptNamesARow(t *testing.T) { d := setupTestDB(t) _, u, err := Store(d, []byte("loose content\n")) if err != nil { t.Fatalf("Store: %v", err) } var n int if err := d.QueryRow("SELECT count(*) FROM blob JOIN rcvfrom USING(rcvid) WHERE uuid=?", u).Scan(&n); err != nil { t.Fatalf("join rcvfrom: %v", err) } if n != 1 { t.Fatalf("blob %s names no rcvfrom row", u) } }