Artifact 5f5ffc3c163da66fb89bbb38d30ff8b1d74ab2add88afdf1ad859cd01b18e3d2:
- File
internal/ingest/derive_test.go
— part of check-in
[a1b816cd96]
at
2026-08-24 16:32:42
on branch trunk
— Raise the derived events from the fold
checkin.created, branch.created and tag.* come off the artifact; branch.moved comes from the computed branch after RecomputeBranches, because only a branch's first check-in carries a sym- tag.
Raising is not idempotent while folding is, so the fold asks the index which hashes are new before writing them. A rebuild raises nothing. (user: dmestas, size: 5465) [annotate] [blame] [check-ins using]
package ingest import ( "encoding/json" "testing" "time" "github.com/nats-io/nats.go" "craftdesign.group/quarry/internal/artifact" "craftdesign.group/quarry/internal/evt" ) func kinds(rs []raised) []evt.Kind { out := make([]evt.Kind, 0, len(rs)) for _, r := range rs { out = append(out, r.Kind) } return out } func TestDeriveReadsTheTagOperator(t *testing.T) { for _, tc := range []struct { name string art *artifact.Artifact want []evt.Kind }{ // A check-in's own sym- tag raises nothing extra: branch.moved // for that check-in comes from the computed branch after the // fold, and raising it here too would double it. {"a check-in on a branch", &artifact.Artifact{ Type: artifact.Manifest, Tags: []artifact.Tag{{Name: "*sym-trunk"}}, }, []evt.Kind{evt.CheckinCreated}}, {"a check-in with a plain tag", &artifact.Artifact{ Type: artifact.Manifest, Tags: []artifact.Tag{{Name: "*sym-trunk"}, {Name: "+release"}}, }, []evt.Kind{evt.CheckinCreated, evt.TagCreated}}, {"a control artifact naming a branch", &artifact.Artifact{ Type: artifact.Control, Tags: []artifact.Tag{{Name: "*sym-feature", Target: "abc"}}, }, []evt.Kind{evt.BranchCreated}}, {"a control artifact cancelling a tag", &artifact.Artifact{ Type: artifact.Control, Tags: []artifact.Tag{{Name: "-release", Target: "abc"}}, }, []evt.Kind{evt.TagRemoved}}, // Cancelling a branch tag is not closing the branch, and nothing // in the vocabulary names it — so it raises nothing rather than // something approximate. {"a cancelled branch tag", &artifact.Artifact{ Type: artifact.Control, Tags: []artifact.Tag{{Name: "-sym-old", Target: "abc"}}, }, nil}, // Everything with an op behind it is raised at that op, which // knows the previous state the artifact does not carry. {"a wiki page", &artifact.Artifact{Type: artifact.Wiki}, nil}, {"a ticket change", &artifact.Artifact{Type: artifact.Ticket}, nil}, {"a malformed tag name", &artifact.Artifact{ Type: artifact.Control, Tags: []artifact.Tag{{Name: "+"}}, }, nil}, } { got := kinds(derive("h1", tc.art)) if len(got) != len(tc.want) { t.Errorf("%s: got %v, want %v", tc.name, got, tc.want) continue } for i := range got { if got[i] != tc.want[i] { t.Errorf("%s: got %v, want %v", tc.name, got, tc.want) break } } } } func TestDeriveCarriesTheBranchAndTarget(t *testing.T) { rs := derive("h1", &artifact.Artifact{ Type: artifact.Manifest, User: "dan", Comment: "fix it\n\nmore", Tags: []artifact.Tag{{Name: "*sym-feature"}}, }) if got := rs[0].Payload["branch"]; got != "feature" { t.Errorf("checkin branch = %v, want feature", got) } if got := rs[0].Payload["summary"]; got != "fix it" { t.Errorf("summary = %v, want the first line only", got) } rs = derive("ctl", &artifact.Artifact{ Type: artifact.Control, Tags: []artifact.Tag{{Name: "+release", Target: "target-hash"}}, }) if got := rs[0].Payload["target"]; got != "target-hash" { t.Errorf("target = %v, want the artifact the tag applies to", got) } } // The pipeline end to end: a real store, a real fold, and the events a // dagnats subject trigger would actually see. // // The bootstrap round is drained FIRST and deliberately unasserted. A // first sync rebuilds from the store's census rather than the bus, and a // rebuild is not news — `checkin.created` for a history that already // existed would be the forge telling every subscriber that five years of // commits just landed. func TestFoldRaisesDerivedEventsOnce(t *testing.T) { pipe, _, repo := testPipeline(t) // Twice: the first round rebuilds from the census (no cursor), and // the second settles the once-per-process tip check. Only after that // does a round take the bus, which is the path under test. for i := 0; i < 2; i++ { if _, err := pipe.Sync(); err != nil { t.Fatal(err) } } sub, err := pipe.ev.JS().SubscribeSync("quarry.evt.test.>", nats.DeliverNew()) if err != nil { t.Fatal(err) } defer sub.Unsubscribe() commitFile(t, repo, "c.txt", "gamma\n", "add c") commitFile(t, repo, "d.txt", "delta\n", "add d") if _, err := pipe.Sync(); err != nil { t.Fatal(err) } drain := func() map[evt.Kind]int { seen := map[evt.Kind]int{} for { m, err := sub.NextMsg(400 * time.Millisecond) if err != nil { return seen } var e struct { Kind evt.Kind Repo string } if err := json.Unmarshal(m.Data, &e); err != nil { t.Fatalf("body: %v", err) } if e.Repo != "test" { t.Errorf("event carried repo %q", e.Repo) } seen[e.Kind]++ } } first := drain() if first[evt.CheckinCreated] != 2 { t.Fatalf("two new check-ins produced %d checkin.created (%v)", first[evt.CheckinCreated], first) } if first[evt.BranchMoved] == 0 { t.Errorf("no branch.moved for check-ins on trunk (%v)", first) } // Folding again must raise NOTHING. Folds are idempotent; raising is // not, and at-least-once redelivery would otherwise tell every // subscriber the history had just happened again. if _, err := pipe.Sync(); err != nil { t.Fatal(err) } if again := drain(); len(again) != 0 { t.Errorf("a refold re-raised %v", again) } // A genuinely new check-in still raises. commitFile(t, repo, "e.txt", "epsilon\n", "add e") if _, err := pipe.Sync(); err != nil { t.Fatal(err) } if third := drain(); third[evt.CheckinCreated] != 1 { t.Errorf("a new check-in raised %v", third) } }