diff options
| author | Gabriel Schneider <[email protected]> | 2026-08-02 23:43:08 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-08-03 09:54:39 -0300 |
| commit | 5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e (patch) | |
| tree | f12f4b700fff4ff6f7e818d2a542672ba0b8d76e /sidecar/store_test.go | |
| parent | a9263daee9413c5eff3c1f1bebcd224442fd8685 (diff) | |
| download | notevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.tar.gz notevi-5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e.zip | |
rebrand to notevi: one CLI over the jj sidecar
vr and vrsite become a single binary. vrsite/ folds into a web package in
one module (0x4200.cafe/notevi); "notevi web" serves and exports exactly
what vrsite did, and "notevi read/grep/note/query" is unchanged.
The sidecar is renamed with it: notevi_log, notevi-log.jsonl, and the
description "private: notevi log". The pre-rebrand names are still
recognized, so an old repository opens and reads; it is renamed in place
on the first write, or up front with "notevi migrate DIR...".
That rename cannot be a single mv inside jj run. jj only auto-tracks a
*new* file in the run working copy below a size limit it does not take
from the command line, so writing a whole log under a name the change has
never held is silently dropped while jj reports success. ensureLogFile
creates the file empty first and lets every later byte be a modification
of a tracked file, which snapshots at any size; that also fixes the same
latent bug when importing a large legacy vr-log.jsonl.
Adds a bem-te-vi mark (favicon and nav brand) and a README.
Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
Diffstat (limited to 'sidecar/store_test.go')
| -rw-r--r-- | sidecar/store_test.go | 134 |
1 files changed, 130 insertions, 4 deletions
diff --git a/sidecar/store_test.go b/sidecar/store_test.go index 85f50e1..8ce3192 100644 --- a/sidecar/store_test.go +++ b/sidecar/store_test.go @@ -5,6 +5,7 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" "sync" "testing" @@ -72,19 +73,19 @@ func TestAppendCreatesPrivateUnrelatedSidecar(t *testing.T) { func TestLegacyLogIsReadThenImportedOnFirstAppend(t *testing.T) { repo := testRepo(t) - legacy := []byte("legacy\n") - if err := os.WriteFile(filepath.Join(repo, LogFile), legacy, 0o644); err != nil { + data := []byte("legacy\n") + if err := os.WriteFile(filepath.Join(repo, legacy.logFile), data, 0o644); err != nil { t.Fatal(err) } store, err := Open(repo) if err != nil { t.Fatal(err) } - if got, err := store.Read(); err != nil || string(got) != string(legacy) { + if got, err := store.Read(); err != nil || string(got) != string(data) { t.Fatalf("pre-migration read = %q, %v", got, err) } if err := store.Append(func(current []byte) ([]byte, error) { - if string(current) != string(legacy) { + if string(current) != string(data) { t.Fatalf("migration callback saw %q", current) } return []byte("sidecar\n"), nil @@ -96,6 +97,131 @@ func TestLegacyLogIsReadThenImportedOnFirstAppend(t *testing.T) { } } +// A repository written by the pre-rebrand tool must keep working, and must stop +// carrying the old name once notevi has touched it. +func TestPreRebrandSidecarIsRenamedInPlace(t *testing.T) { + repo := testRepo(t) + rev := writeLegacySidecar(t, repo, "old\n") + + store, err := Open(repo) + if err != nil { + t.Fatal(err) + } + if got, err := store.Read(); err != nil || string(got) != "old\n" { + t.Fatalf("read of an un-migrated sidecar = %q, %v", got, err) + } + + migrated, err := store.Migrate() + if err != nil { + t.Fatal(err) + } + if !migrated { + t.Fatal("Migrate reported nothing to do for a legacy sidecar") + } + + if got := strings.TrimSpace(testJJ(t, repo, "config", "get", "revset-aliases."+Alias)); got != rev { + t.Fatalf("alias %s = %q, want the original change %q", Alias, got, rev) + } + if out, err := exec.Command("jj", "--ignore-working-copy", "-R", repo, + "config", "get", "revset-aliases."+legacy.alias).CombinedOutput(); err == nil { + t.Fatalf("legacy alias %s survived: %s", legacy.alias, out) + } + private := testJJ(t, repo, "config", "get", "git.private-commits") + if !containsRevsetName(private, Alias) || containsRevsetName(private, legacy.alias) { + t.Fatalf("private commits = %q, want %s and not %s", private, Alias, legacy.alias) + } + files := testJJ(t, repo, "file", "list", "-r", "exactly("+Alias+", 1)") + if !strings.Contains(files, LogFile) || strings.Contains(files, legacy.logFile) { + t.Fatalf("sidecar files = %q, want only %s", files, LogFile) + } + subject := strings.TrimSpace(testJJ(t, repo, "log", "-G", + "-r", "exactly("+Alias+", 1)", "-T", "description")) + if subject != Description { + t.Fatalf("sidecar description = %q, want %q", subject, Description) + } + + // The trace survived the rename and the store keeps appending to it. + if err := store.Append(func(current []byte) ([]byte, error) { + if string(current) != "old\n" { + t.Fatalf("append after migration saw %q", current) + } + return []byte("new\n"), nil + }); err != nil { + t.Fatal(err) + } + if got, err := store.Read(); err != nil || string(got) != "old\nnew\n" { + t.Fatalf("migrated log = %q, %v", got, err) + } + if again, err := store.Migrate(); err != nil || again { + t.Fatalf("second Migrate = %v, %v; want no-op", again, err) + } +} + +// A real trace is hundreds of KB. jj silently declines to auto-track a *new* +// file in the run working copy past a size limit it does not take from the +// command line, so a rename that writes the whole log under a name the change +// has never held loses it — with jj reporting success. Small fixtures do not +// catch that; this one does. +func TestLargeLogSurvivesRenameAndAppend(t *testing.T) { + repo := testRepo(t) + var seed strings.Builder + for i := range 5000 { + fmt.Fprintf(&seed, `{"time":"2026-07-26T10:00:00Z","op":"read","file":"src/%d.rs","start":1,"end":400}`+"\n", i) + } + if seed.Len() < 256<<10 { + t.Fatalf("fixture is only %d bytes; too small to exercise the limit", seed.Len()) + } + writeLegacySidecar(t, repo, seed.String()) + + store, err := Open(repo) + if err != nil { + t.Fatal(err) + } + if _, err := store.Migrate(); err != nil { + t.Fatal(err) + } + got, err := store.Read() + if err != nil { + t.Fatal(err) + } + if string(got) != seed.String() { + t.Fatalf("renamed log is %d bytes, want %d", len(got), seed.Len()) + } + if err := store.Append(func([]byte) ([]byte, error) { return []byte(`{"op":"note"}`), nil }); err != nil { + t.Fatal(err) + } + if got, err = store.Read(); err != nil || len(got) <= seed.Len() { + t.Fatalf("append after a large rename left %d bytes, %v", len(got), err) + } +} + +// writeLegacySidecar builds the sidecar exactly as the pre-rebrand tool did and +// returns its change id. +func writeLegacySidecar(t *testing.T, repo, content string) string { + t.Helper() + testJJ(t, repo, "new", "--no-edit", "root()", "-m", legacy.description) + rev := strings.TrimSpace(testJJ(t, repo, "log", "-G", "-T", "change_id", + "-r", fmt.Sprintf("root()+ & subject(exact:%q)", legacy.description))) + tmp := filepath.Join(t.TempDir(), "seed") + if err := os.WriteFile(tmp, []byte(content), 0o644); err != nil { + t.Fatal(err) + } + // Touch first, then fill: the same two steps the store uses, so a large + // fixture really lands in the sidecar instead of quietly seeding nothing. + testJJ(t, repo, "--config", `snapshot.auto-track="all()"`, + "run", "--clean", "--root", "-r", "exactly("+rev+", 1)", "--", + "sh", "-c", ": > "+legacy.logFile) + testJJ(t, repo, "--config", `snapshot.auto-track="all()"`, + "run", "--clean", "--root", "-r", "exactly("+rev+", 1)", "--", + "sh", "-c", "cat \"$1\" >> "+legacy.logFile, "sh", tmp) + if got := testJJ(t, repo, "file", "show", "-r", "exactly("+rev+", 1)", "--", legacy.logFile); got != content { + t.Fatalf("legacy fixture is %d bytes in the sidecar, want %d", len(got), len(content)) + } + testJJ(t, repo, "config", "set", "--repo", "revset-aliases."+legacy.alias, strconv.Quote(rev)) + testJJ(t, repo, "config", "set", "--repo", "git.private-commits", strconv.Quote("(none()) | "+legacy.alias)) + return rev +} + func TestConcurrentAppendsAreSerialized(t *testing.T) { repo := testRepo(t) store, err := Open(repo) |
