summaryrefslogtreecommitdiff
path: root/sidecar/store_test.go
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-08-02 12:36:54 -0300
committerGabriel Schneider <[email protected]>2026-08-02 17:37:58 -0300
commite16f78b255bcf46d8c6f9a4b9e76dd7a3817edeb (patch)
treede0db0c369522b5bf92ff32a8351de2224c5c9dc /sidecar/store_test.go
parent151ea12251ed876aa87bb486da1bc337e25369e9 (diff)
downloadnotevi-e16f78b255bcf46d8c6f9a4b9e76dd7a3817edeb.tar.gz
notevi-e16f78b255bcf46d8c6f9a4b9e76dd7a3817edeb.zip
Diffstat (limited to 'sidecar/store_test.go')
-rw-r--r--sidecar/store_test.go164
1 files changed, 164 insertions, 0 deletions
diff --git a/sidecar/store_test.go b/sidecar/store_test.go
new file mode 100644
index 0000000..85f50e1
--- /dev/null
+++ b/sidecar/store_test.go
@@ -0,0 +1,164 @@
+package sidecar
+
+import (
+ "fmt"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "sync"
+ "testing"
+)
+
+func TestAppendCreatesPrivateUnrelatedSidecar(t *testing.T) {
+ repo := testRepo(t)
+ // Store must be independent of a user's restrictive auto-track setting.
+ testJJ(t, repo, "config", "set", "--repo", "snapshot.auto-track", `"none()"`)
+ testJJ(t, repo, "config", "set", "--repo", "git.private-commits", `"description(glob:'secret:*')"`)
+ before := testJJ(t, repo, "status")
+ store, err := Open(repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+
+ if err := store.Append(func(current []byte) ([]byte, error) {
+ if len(current) != 0 {
+ t.Fatalf("new store contained %q", current)
+ }
+ return []byte("one\n"), nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+ if err := store.Append(func(current []byte) ([]byte, error) {
+ if string(current) != "one\n" {
+ t.Fatalf("second append saw %q", current)
+ }
+ return []byte("two\n"), nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+
+ got, err := store.Read()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(got) != "one\ntwo\n" {
+ t.Fatalf("sidecar log = %q", got)
+ }
+ if after := testJJ(t, repo, "status"); after != before {
+ t.Fatalf("working copy changed:\n--- before\n%s--- after\n%s", before, after)
+ }
+ if _, err := os.Stat(filepath.Join(repo, LogFile)); !os.IsNotExist(err) {
+ t.Fatalf("log materialized in the project working copy: %v", err)
+ }
+
+ alias := strings.TrimSpace(testJJ(t, repo, "config", "get", "revset-aliases."+Alias))
+ if alias == "" {
+ t.Fatal("repo-local sidecar alias was not configured")
+ }
+ private := testJJ(t, repo, "config", "get", "git.private-commits")
+ if !containsRevsetName(private, Alias) {
+ t.Fatalf("private commits = %q, missing %s", private, Alias)
+ }
+ if !strings.Contains(private, "secret:*") {
+ t.Fatalf("private commits = %q, previous setting was overwritten", private)
+ }
+ parent := strings.TrimSpace(testJJ(t, repo, "log", "-G", "-r", "exactly("+Alias+", 1)",
+ "-T", "parents.map(|p| p.commit_id()).join(\"\")"))
+ if parent != strings.Repeat("0", 40) {
+ t.Fatalf("sidecar parent = %q, want virtual root", parent)
+ }
+}
+
+func TestLegacyLogIsReadThenImportedOnFirstAppend(t *testing.T) {
+ repo := testRepo(t)
+ legacy := []byte("legacy\n")
+ if err := os.WriteFile(filepath.Join(repo, LogFile), legacy, 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) {
+ t.Fatalf("pre-migration read = %q, %v", got, err)
+ }
+ if err := store.Append(func(current []byte) ([]byte, error) {
+ if string(current) != string(legacy) {
+ t.Fatalf("migration callback saw %q", current)
+ }
+ return []byte("sidecar\n"), nil
+ }); err != nil {
+ t.Fatal(err)
+ }
+ if got, err := store.Read(); err != nil || string(got) != "legacy\nsidecar\n" {
+ t.Fatalf("migrated log = %q, %v", got, err)
+ }
+}
+
+func TestConcurrentAppendsAreSerialized(t *testing.T) {
+ repo := testRepo(t)
+ store, err := Open(repo)
+ if err != nil {
+ t.Fatal(err)
+ }
+ const writers = 6
+ errCh := make(chan error, writers)
+ var wg sync.WaitGroup
+ for range writers {
+ wg.Add(1)
+ go func() {
+ defer wg.Done()
+ errCh <- store.Append(func(current []byte) ([]byte, error) {
+ id := len(strings.Fields(string(current))) + 1
+ return []byte(fmt.Sprintf("%d\n", id)), nil
+ })
+ }()
+ }
+ wg.Wait()
+ close(errCh)
+ for err := range errCh {
+ if err != nil {
+ t.Fatal(err)
+ }
+ }
+ got, err := store.Read()
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(got) != "1\n2\n3\n4\n5\n6\n" {
+ t.Fatalf("serialized appends = %q", got)
+ }
+}
+
+func testRepo(t *testing.T) string {
+ t.Helper()
+ if _, err := exec.LookPath("jj"); err != nil {
+ t.Skip("jj not installed")
+ }
+ home := t.TempDir()
+ t.Setenv("HOME", home)
+ t.Setenv("JJ_USER", "test")
+ t.Setenv("JJ_EMAIL", "[email protected]")
+ repo := filepath.Join(home, "repo")
+ if err := os.Mkdir(repo, 0o755); err != nil {
+ t.Fatal(err)
+ }
+ cmd := exec.Command("jj", "git", "init")
+ cmd.Dir = repo
+ if out, err := cmd.CombinedOutput(); err != nil {
+ t.Fatalf("jj git init: %v\n%s", err, out)
+ }
+ return repo
+}
+
+func testJJ(t *testing.T, repo string, args ...string) string {
+ t.Helper()
+ cmd := exec.Command("jj", append([]string{"--ignore-working-copy", "--no-pager"}, args...)...)
+ cmd.Dir = repo
+ out, err := cmd.CombinedOutput()
+ if err != nil {
+ t.Fatalf("jj %s: %v\n%s", strings.Join(args, " "), err, out)
+ }
+ return string(out)
+}