Artifact 8d3aa8c250003bb30f89d279c2e717dde8df195ce45960d4b5f8bd449e0ab918:
- File
conformance/artifact_delta_test.go
— part of check-in
[d52a6fe050]
at
2026-10-11 00:42:57
on branch trunk
— test(conformance): artifact-format conformance suite (#263) (#276)
* test(conformance): artifact-format conformance suite (#263)
Adds the suite for draft-fossil-artifact-format-00 with errata 4.1.2, 4.2 and 4.6.4, so the parser, writer, naming, hash policy and delta manifest code are checked requirement by requirement instead of by scattered unit tests that each encode one reading of fossil.
- requirements/artifact.tsv: 101 requirements from Sections 3 to 8, pinned to the draft and the three errata. - Parse cases are a known-good base artifact per type plus one edit, with the Z card recomputed, so the edit alone explains a rejection. Every case also runs through fossil test-parse-manifest; fossil 2.28 agrees with all of them. Card legality, duplicates, arity and hash positions are generated from the bases and the draft's own tables. - Accepted inputs must re-encode byte for byte unless marked non-canonical. - API cases cover naming, verification, the comparator, the writer, the R card, the hash policy and delta-manifest enumeration. - FuzzArtifactRoundTrip checks parse/marshal/parse stability, and TestArtifactCorpus (CONFORMANCE_REPOS) parses every artifact fossil crosslinked in real repositories.
267 cases go-libfossil fails are marked with a "#0" placeholder in artifact_gaps_test.go, grouped by the code at fault, for issues to be filed against.
* test(conformance): tighten the artifact suite after review (#263)
A rejection only counts when the rule the case names makes it, so every rejection case that is not about the Z card must now pass deck.VerifyZ first. Inputs go-libfossil rejected only through the Z check (framing, residue, sterilizer, unstripped wrappers, an extra Z token) become gaps, along with VerifyZ itself, which rejects an absent Z card and reads the last byte where Section 6.2 says it must not.
- Cases can name the substring fossil's rejection reason must contain; the unknown-letter cases sit in sorted position, the sterilizer cases are filed under card order, which is what fossil rejects them for, and generated legality cases take their requirement from the draft's table. - The ratchet that never persists the hash policy is a gap: fossil opening the repository still reads auto. - New rows for 4.7.9, 6.2 (absent Z, last byte), 6.3.4 and 7.1. - The audit refuses the #0 placeholder, a test keeps the gap map and the fuzz test's forgiven losses tied to live gap cases, and one Z-card helper replaces two.
* test(conformance): mark artifact gaps with their issues (#263)
Replace the placeholder gap marks with the issues that track each fix (#265-#275), and keep coverage_test.go within 100 columns. (user: [email protected], size: 4107) [annotate] [blame] [check-ins using]
package conformance import ( "testing" "github.com/danmestas/go-libfossil/internal/blob" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/internal/manifest" "github.com/danmestas/go-libfossil/internal/repo" ) // Delta manifests (Sections 4.7.3 and 8): a baseline and deltas against it // are stored as artifacts, then enumerated with manifest.ListFiles. const deltaBaseline = "C baseline\nD " + ts + "\nF a.txt " + h1 + "\nF b.txt " + h2 + "\nF c.txt " + h3 + "\nU u\n" // storeArtifact stores body, with its Z card, and returns its rid and name. func storeArtifact(t *testing.T, r *repo.Repo, body string) (libfossil.FslID, string) { t.Helper() rid, uuid, err := blob.Store(r.DB(), withZ(body)) if err != nil { t.Fatalf("blob.Store: %v", err) } return rid, uuid } // fileSet lists a manifest's full file set as name=hash entries. func fileSet(r *repo.Repo, rid libfossil.FslID) ([]string, error) { files, err := manifest.ListFiles(r, rid) if err != nil { return nil, err } var set []string for _, f := range files { set = append(set, f.Name+"="+f.UUID) } return set, nil } // deltaTree stores the baseline and a delta whose cards after the B card // are rest, and checks the delta's file set. func deltaTree(rest string, expected ...string) func(*testing.T) error { return func(t *testing.T) error { r := newPolicyRepo(t, "2") _, baseline := storeArtifact(t, r, deltaBaseline) rid, _ := storeArtifact(t, r, "B "+baseline+"\n"+rest) set, err := fileSet(r, rid) if err != nil { return err } return want("file set", set, expected) } } // deltaFails stores a delta whose B card names what baseline stored, and // wants enumerating its files to fail. func deltaFails(baseline func(*testing.T, *repo.Repo) string) func(*testing.T) error { return func(t *testing.T) error { r := newPolicyRepo(t, "2") rid, _ := storeArtifact(t, r, "B "+baseline(t, r)+"\nC delta\nD "+ts+"\nU u\n") set, err := fileSet(r, rid) if err == nil { return expectf("enumerated the file set %v", set) } return nil } } func deltaManifestCases() []apiCase { return []apiCase{ {Case{ID: "artifact.8.baseline-tree", Req: "AF-8-1"}, func(t *testing.T) error { r := newPolicyRepo(t, "2") rid, _ := storeArtifact(t, r, deltaBaseline) set, err := fileSet(r, rid) if err != nil { return err } return want("file set", set, []string{"a.txt=" + h1, "b.txt=" + h2, "c.txt=" + h3}) }}, {Case{ID: "artifact.8.empty-delta", Req: "AF-8-1"}, deltaTree("C delta\nD "+ts+"\nU u\n", "a.txt="+h1, "b.txt="+h2, "c.txt="+h3)}, {Case{ID: "artifact.8.2.delta-tree", Req: "AF-8.2-1"}, deltaTree("C delta\nD "+ts+ "\nF a.txt "+h4+"\nF c.txt\nF d.txt "+h5+"\nU u\n", "a.txt="+h4, "b.txt="+h2, "d.txt="+h5)}, {Case{ID: "artifact.8.2.unmatched-deletions", Req: "AF-8.2-1"}, deltaTree("C delta\nD "+ts+ "\nF aa.txt\nF zz.txt\nU u\n", "a.txt="+h1, "b.txt="+h2, "c.txt="+h3)}, // A rename is a delete of the old name and an add of the new one; an // old-name field alone does not remove the baseline's file. {Case{ID: "artifact.8.2.rename", Req: "AF-8.2-1"}, deltaTree("C delta\nD "+ts+ "\nF b.txt\nF b2.txt "+h2+" w b.txt\nU u\n", "a.txt="+h1, "b2.txt="+h2, "c.txt="+h3)}, {Case{ID: "artifact.8.2.oldname-metadata", Req: "AF-8.2-1"}, deltaTree("C delta\nD "+ts+ "\nF b2.txt "+h2+" w b.txt\nU u\n", "a.txt="+h1, "b.txt="+h2, "b2.txt="+h2, "c.txt="+h3)}, {Case{ID: "artifact.8.3.missing-baseline", Req: "AF-8.3-1"}, deltaFails(func(*testing.T, *repo.Repo) string { return h9 })}, {Case{ID: "artifact.8.3.baseline-not-manifest", Req: "AF-8.3-1"}, deltaFails(func(t *testing.T, r *repo.Repo) string { _, uuid := storeArtifact(t, r, baseControl) return uuid })}, {Case{ID: "artifact.4.7.3.delta-baseline-invalid", Req: "AF-4.7.3-1"}, deltaFails(func(t *testing.T, r *repo.Repo) string { _, baseline := storeArtifact(t, r, deltaBaseline) _, uuid := storeArtifact(t, r, "B "+baseline+"\nC first\nD "+ts+ "\nF a.txt "+h4+"\nU u\n") return uuid })}, } }