Artifact b6dd40b63881c7ccdbeb1857a543ab1797fa562d5d1ab0fc6a222fff18b51f54:
- File
internal/checkout/manage_test.go
— part of check-in
[6273a0750d]
at
2026-07-22 23:03:24
on branch trunk
— fix: correct go.mod module path to go-libfossil (#76) (#117)
* fix: correct go.mod module path to go-libfossil (#76)
The module directive still declared github.com/danmestas/libfossil after the repository was renamed to go-libfossil. pkg.go.dev/godoc scraping matches the declared module path against its hosting path, so the stale path broke SDK doc generation.
Rename the module path across all four in-repo modules (root, the two db/driver submodules, observer/otel, and docs/site) and every internal import, plus the release workflow, the doc-generation script, and the docs.
No tag has been published under the go-libfossil path yet, so the cross-module requires cannot resolve from the proxy. Add replace directives pointing the in-repo requires at their local directories so GOWORK=off builds and the CI matrix resolve without a published version. These replaces are ignored by downstream consumers and can be dropped once new-path tags are cut.
* Guard releases against replace directives; regenerate SDK docs
The go-libfossil rename bridges the in-repo modules with `replace` directives. Go refuses a main module that carries them, so a tag cut while they are present breaks `go install .../cmd/libfossil@<tag>` and every `@version` fetch of the driver submodules. Add a Release workflow step that fails the job before anything is published if any tracked go.mod still contains a replace directive, so the bridge is a CI-enforced TODO rather than a silent landmine. The step comment records what unblocks a release: re-tag the submodules with the corrected module paths, bump the root requires, then drop the replace blocks.
Regenerate the SDK reference docs. The previous pass only substituted URLs over the pre-existing generated files, leaving stale signatures (Repo.Timeline was still documented as taking LogOpts rather than TimelineOpts). Note that gomarkdoc must be run outside a git worktree — inside one it silently strips every source link.
Also reword the README rename note, which read as though the import path had not changed, and re-sort the ncruces require block. (user: [email protected], size: 8440) [annotate] [blame] [check-ins using]
package checkout import ( "testing" libfossil "github.com/danmestas/go-libfossil/internal/fsltype" "github.com/danmestas/go-libfossil/simio" ) func TestManageNewFile(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Write a new file to MemStorage if err := mem.WriteFile("/checkout/new.txt", []byte("new file"), 0644); err != nil { t.Fatal(err) } counts, err := co.Manage(ManageOpts{Paths: []string{"new.txt"}}) if err != nil { t.Fatal(err) } if counts.Added != 1 { t.Fatalf("added = %d, want 1", counts.Added) } // Verify vfile row exists with rid=0, chnged=1 var vRid, chnged int err = co.db.QueryRow("SELECT rid, chnged FROM vfile WHERE vid=? AND pathname='new.txt'", int64(rid)).Scan(&vRid, &chnged) if err != nil { t.Fatal("vfile row not found:", err) } if vRid != 0 { t.Fatalf("rid = %d, want 0 (newly added)", vRid) } if chnged != 1 { t.Fatalf("chnged = %d, want 1", chnged) } } func TestManageDuplicate(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Try to manage an already-tracked file counts, err := co.Manage(ManageOpts{Paths: []string{"hello.txt"}}) if err != nil { t.Fatal(err) } if counts.Skipped != 1 { t.Fatalf("skipped = %d, want 1", counts.Skipped) } } func TestManageCallback(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Write a new file if err := mem.WriteFile("/checkout/new.txt", []byte("new"), 0644); err != nil { t.Fatal(err) } var called bool var addedFlag bool _, err = co.Manage(ManageOpts{ Paths: []string{"new.txt"}, Callback: func(name string, added bool) error { called = true addedFlag = added if name != "new.txt" { t.Fatalf("callback name = %q, want %q", name, "new.txt") } return nil }, }) if err != nil { t.Fatal(err) } if !called { t.Fatal("callback not called") } if !addedFlag { t.Fatal("callback added flag should be true") } } func TestManageCallbackSkipped(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } var called bool var addedFlag bool _, err = co.Manage(ManageOpts{ Paths: []string{"hello.txt"}, Callback: func(name string, added bool) error { called = true addedFlag = added return nil }, }) if err != nil { t.Fatal(err) } if !called { t.Fatal("callback not called") } if addedFlag { t.Fatal("callback added flag should be false for skipped file") } } func TestUnmanageNewFile(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Add then remove a new file if err := mem.WriteFile("/checkout/new.txt", []byte("new"), 0644); err != nil { t.Fatal(err) } if _, err := co.Manage(ManageOpts{Paths: []string{"new.txt"}}); err != nil { t.Fatal(err) } err = co.Unmanage(UnmanageOpts{Paths: []string{"new.txt"}}) if err != nil { t.Fatal(err) } // Should be completely removed (rid=0 -> DELETE) var count int err = co.db.QueryRow("SELECT count(*) FROM vfile WHERE vid=? AND pathname='new.txt'", int64(rid)).Scan(&count) if err != nil { t.Fatal(err) } if count != 0 { t.Fatalf("expected 0 rows for unmanaged new file, got %d", count) } } func TestUnmanageExistingFile(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Unmanage an existing tracked file err = co.Unmanage(UnmanageOpts{Paths: []string{"hello.txt"}}) if err != nil { t.Fatal(err) } // Should be marked deleted=1 (not removed, because rid>0) var deleted int err = co.db.QueryRow("SELECT deleted FROM vfile WHERE vid=? AND pathname='hello.txt'", int64(rid)).Scan(&deleted) if err != nil { t.Fatal("vfile row not found:", err) } if deleted != 1 { t.Fatalf("deleted = %d, want 1", deleted) } } func TestUnmanageCallback(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } var called bool var calledName string err = co.Unmanage(UnmanageOpts{ Paths: []string{"hello.txt"}, Callback: func(name string) error { called = true calledName = name return nil }, }) if err != nil { t.Fatal(err) } if !called { t.Fatal("callback not called") } if calledName != "hello.txt" { t.Fatalf("callback name = %q, want %q", calledName, "hello.txt") } } func TestUnmanageNotFound(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Unmanage a file that doesn't exist — should not error err = co.Unmanage(UnmanageOpts{Paths: []string{"nonexistent.txt"}}) if err != nil { t.Fatal("expected no error for nonexistent file, got:", err) } } func TestUnmanageByVFileID(t *testing.T) { r, cleanup := newTestRepoWithCheckin(t) defer cleanup() dir := t.TempDir() co, err := Create(r, dir, CreateOpts{}) if err != nil { t.Fatal(err) } defer co.Close() rid, _, _ := co.Version() mem := simio.NewMemStorage() co.env = &simio.Env{Storage: mem, Clock: simio.RealClock{}, Rand: simio.CryptoRand{}} co.dir = "/checkout" if err := co.Extract(rid, ExtractOpts{}); err != nil { t.Fatal(err) } // Get vfile ID for hello.txt var vfileID int64 err = co.db.QueryRow("SELECT id FROM vfile WHERE vid=? AND pathname='hello.txt'", int64(rid)).Scan(&vfileID) if err != nil { t.Fatal(err) } // Unmanage by VFileID err = co.Unmanage(UnmanageOpts{VFileIDs: []libfossil.FslID{libfossil.FslID(vfileID)}}) if err != nil { t.Fatal(err) } // Should be marked deleted=1 var deleted int err = co.db.QueryRow("SELECT deleted FROM vfile WHERE id=?", vfileID).Scan(&deleted) if err != nil { t.Fatal(err) } if deleted != 1 { t.Fatalf("deleted = %d, want 1", deleted) } }