summaryrefslogtreecommitdiff
path: root/sidecar/store_test.go
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-02 23:43:08 -0300
committerGabriel Schneider <[email protected]>2026-08-03 09:54:39 -0300
commit5ec72f8723d795d0b02d7e9ebb9f555f0c7e6a2e (patch)
treef12f4b700fff4ff6f7e818d2a542672ba0b8d76e /sidecar/store_test.go
parenta9263daee9413c5eff3c1f1bebcd224442fd8685 (diff)
downloadnotevi-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.go134
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)