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 /vrsite/projects_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 'vrsite/projects_test.go')
| -rw-r--r-- | vrsite/projects_test.go | 315 |
1 files changed, 0 insertions, 315 deletions
diff --git a/vrsite/projects_test.go b/vrsite/projects_test.go deleted file mode 100644 index 79fc6bc..0000000 --- a/vrsite/projects_test.go +++ /dev/null @@ -1,315 +0,0 @@ -package main - -import ( - "encoding/json" - "net/http" - "net/http/httptest" - "net/url" - "os" - "os/exec" - "path/filepath" - "strings" - "testing" - - "0x4200.cafe/vr/sidecar" -) - -func projectTestEnv(t *testing.T, home string) { - t.Helper() - t.Setenv("HOME", home) - t.Setenv("JJ_USER", "test") - t.Setenv("JJ_EMAIL", "[email protected]") -} - -func runAt(t *testing.T, dir, name string, args ...string) string { - t.Helper() - cmd := exec.Command(name, args...) - cmd.Dir = dir - out, err := cmd.CombinedOutput() - if err != nil { - t.Fatalf("%s %s in %s: %v\n%s", name, strings.Join(args, " "), dir, err, out) - } - return string(out) -} - -func initJJAt(t *testing.T, dir string) (change, commit string) { - t.Helper() - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - runAt(t, dir, "jj", "git", "init") - if err := os.WriteFile(filepath.Join(dir, "hello.txt"), []byte("one\ntwo\n"), 0o644); err != nil { - t.Fatal(err) - } - runAt(t, dir, "jj", "describe", "-m", "test project") - change, commit, err := resolveRevision(dir, "@") - if err != nil { - t.Fatal(err) - } - return change, commit -} - -func addTestSidecar(t *testing.T, repo, change, commit string) { - t.Helper() - log, err := sidecar.Open(repo) - if err != nil { - t.Fatal(err) - } - line, err := json.Marshal(Entry{ - Time: "2026-08-02T12:00:00Z", Op: "note", Session: "s1", Agent: "codex", - Dir: repo, Rev: "@", Change: change, Commit: commit, File: "hello.txt", - Start: 1, End: 1, NoteID: 1, Text: "project note", - }) - if err != nil { - t.Fatal(err) - } - if err := log.Append(func([]byte) ([]byte, error) { return []byte(line), nil }); err != nil { - t.Fatal(err) - } -} - -func initGitAt(t *testing.T, dir string) { - t.Helper() - if err := os.MkdirAll(dir, 0o755); err != nil { - t.Fatal(err) - } - runAt(t, dir, "git", "init", "-q") - if err := os.WriteFile(filepath.Join(dir, "README.md"), []byte("git only\n"), 0o644); err != nil { - t.Fatal(err) - } -} - -func testProjectHub(t *testing.T, home string) *projectHub { - t.Helper() - dark, light := loadTheme("", "", "dark"), loadTheme("", "", "light") - h, err := newProjectHub(home, "vr projects", "tester", nil, dark, light, testThemes(dark, light)) - if err != nil { - t.Fatal(err) - } - return h -} - -func projectOfKind(t *testing.T, h *projectHub, kind projectKind, name string) *Project { - t.Helper() - h.mu.RLock() - defer h.mu.RUnlock() - for _, p := range h.order { - if p.Kind == kind && p.Name == name { - return p - } - } - t.Fatalf("no %s project named %s", kind, name) - return nil -} - -func TestDiscoverProjectsClassifiesAndDeduplicates(t *testing.T) { - if _, err := exec.LookPath("jj"); err != nil { - t.Skip("jj not installed") - } - home := t.TempDir() - projectTestEnv(t, home) - active := filepath.Join(home, "src", "active") - change, commit := initJJAt(t, active) - addTestSidecar(t, active, change, commit) - workspace := filepath.Join(home, "src", "active-workspace") - runAt(t, active, "jj", "workspace", "add", workspace) - initJJAt(t, filepath.Join(home, "src", "ready")) - initGitAt(t, filepath.Join(home, "src", "git-only")) - - projects, scanErr, err := discoverProjects(home) - if err != nil || scanErr != "" { - t.Fatalf("discover = %v, %q", err, scanErr) - } - if len(projects) != 3 { - t.Fatalf("projects = %d, want 3: %+v", len(projects), projects) - } - counts := map[projectKind]int{} - for _, p := range projects { - counts[p.Kind]++ - if p.Kind == projectActive { - if p.Entries != 1 || p.Notes != 1 || p.Agents != 1 || len(p.Workspaces) != 2 { - t.Fatalf("active project = %+v", p) - } - } - } - if counts[projectActive] != 1 || counts[projectJJ] != 1 || counts[projectGit] != 1 { - t.Fatalf("classification = %+v", counts) - } -} - -func TestProjectHubRoutesAnExistingLogUnderPrefix(t *testing.T) { - home := t.TempDir() - projectTestEnv(t, home) - repo := filepath.Join(home, "code", "alpha") - change, commit := initJJAt(t, repo) - addTestSidecar(t, repo, change, commit) - hub := testProjectHub(t, home) - p := projectOfKind(t, hub, projectActive, "alpha") - h := hub.handler() - - for _, tc := range []struct{ path, want string }{ - {"/", "alpha"}, - {"/", `id="themepick"`}, - {"/", `data-js="/themes.js"`}, - {"/", `l.href = "/themes.css"`}, - {"/p/" + p.ID + "/", "project note"}, - {"/p/" + p.ID + "/code/hello.txt", "two"}, - {"/p/" + p.ID + "/style.css", ".project-grid"}, - {"/p/" + p.ID + "/jj", "/p/" + p.ID + "/jj/log"}, - } { - w := get(t, h, tc.path, false) - if w.Code != http.StatusOK || !strings.Contains(w.Body.String(), tc.want) { - t.Errorf("GET %s = %d, want body containing %q\n%s", tc.path, w.Code, tc.want, w.Body.String()) - } - } - body := get(t, h, "/p/"+p.ID+"/", false).Body.String() - for _, want := range []string{"/p/" + p.ID + "/style.css", "/p/" + p.ID + "/activity", ">projects</a>"} { - if !strings.Contains(body, want) { - t.Errorf("prefixed page lacks %q", want) - } - } - - form := url.Values{"text": {"written through the project route"}, "change": {change}} - r := httptest.NewRequest("POST", "/p/"+p.ID+"/notes", strings.NewReader(form.Encode())) - r.Header.Set("Content-Type", "application/x-www-form-urlencoded") - w := httptest.NewRecorder() - h.ServeHTTP(w, r) - if w.Code != http.StatusSeeOther || w.Header().Get("Location") != "/p/"+p.ID+"/notes" { - t.Fatalf("prefixed POST = %d, location %q: %s", w.Code, w.Header().Get("Location"), w.Body.String()) - } - log, err := sidecar.Open(repo) - if err != nil { - t.Fatal(err) - } - b, err := log.Read() - if err != nil { - t.Fatal(err) - } - entries := parseLog(b) - if last := entries[len(entries)-1]; last.Text != "written through the project route" || last.Change != change { - t.Fatalf("prefixed POST recorded %+v", last) - } -} - -func TestProjectHubStartsAnEmptyPrivateSidecar(t *testing.T) { - home := t.TempDir() - projectTestEnv(t, home) - repo := filepath.Join(home, "code", "ready") - initJJAt(t, repo) - hub := testProjectHub(t, home) - p := projectOfKind(t, hub, projectJJ, "ready") - beforeStatus, _ := jjRun(repo, "status") - _, beforeCommit, err := resolveRevision(repo, "@") - if err != nil { - t.Fatal(err) - } - - r := httptest.NewRequest("POST", "/projects/"+p.ID+"/start", nil) - w := httptest.NewRecorder() - hub.handler().ServeHTTP(w, r) - if w.Code != http.StatusSeeOther || !strings.HasPrefix(w.Header().Get("Location"), "/p/") { - t.Fatalf("start = %d, location %q: %s", w.Code, w.Header().Get("Location"), w.Body.String()) - } - opened := get(t, hub.handler(), w.Header().Get("Location"), false) - if opened.Code != http.StatusOK || !strings.Contains(opened.Body.String(), "ready") { - t.Fatalf("new empty project page = %d: %s", opened.Code, opened.Body.String()) - } - active, err := hasVRSidecar(repo) - if err != nil || !active { - t.Fatalf("sidecar = %v, %v", active, err) - } - afterStatus, _ := jjRun(repo, "status") - _, afterCommit, err := resolveRevision(repo, "@") - if err != nil { - t.Fatal(err) - } - if string(afterStatus) != string(beforeStatus) || afterCommit != beforeCommit { - t.Fatal("starting the sidecar changed the project working copy") - } - log, err := sidecar.Open(repo) - if err != nil { - t.Fatal(err) - } - if b, err := log.Read(); err != nil || len(b) != 0 { - t.Fatalf("new sidecar log = %q, %v", b, err) - } - if _, err := os.Stat(filepath.Join(repo, sidecar.LogFile)); !os.IsNotExist(err) { - t.Fatalf("sidecar materialized in the project: %v", err) - } - private, err := jjRun(repo, "config", "get", "git.private-commits") - if err != nil || !mentionsRevset(string(private), sidecar.Alias) { - t.Fatalf("private commits = %q, %v", private, err) - } -} - -func TestEnsureVRSidecarRefusesAConflictingAlias(t *testing.T) { - home := t.TempDir() - projectTestEnv(t, home) - repo := filepath.Join(home, "code", "conflict") - initJJAt(t, repo) - runAt(t, repo, "jj", "config", "set", "--repo", "revset-aliases."+sidecar.Alias, `"root()"`) - - err := ensureVRSidecar(repo) - if err == nil || !strings.Contains(err.Error(), "does not name vr's sidecar") { - t.Fatalf("ensure with conflicting alias = %v", err) - } - if active, err := hasVRSidecar(repo); err != nil || active { - t.Fatalf("conflicting alias created a sidecar: %v, %v", active, err) - } - alias := strings.TrimSpace(runAt(t, repo, "jj", "config", "get", "revset-aliases."+sidecar.Alias)) - if alias != "root()" { - t.Fatalf("conflicting alias was overwritten: %q", alias) - } -} - -func TestProjectHubInitializesGitThenRefreshes(t *testing.T) { - home := t.TempDir() - projectTestEnv(t, home) - repo := filepath.Join(home, "code", "git-only") - initGitAt(t, repo) - hub := testProjectHub(t, home) - p := projectOfKind(t, hub, projectGit, "git-only") - - r := httptest.NewRequest("POST", "/git/"+p.ID+"/init", nil) - w := httptest.NewRecorder() - hub.handler().ServeHTTP(w, r) - if w.Code != http.StatusSeeOther { - t.Fatalf("init = %d: %s", w.Code, w.Body.String()) - } - if !markerExists(filepath.Join(repo, ".jj")) { - t.Fatal("colocated jj metadata was not created") - } - projectOfKind(t, hub, projectJJ, "git-only") - if got := string(mustRead(t, filepath.Join(repo, "README.md"))); got != "git only\n" { - t.Fatalf("working file changed: %q", got) - } - - late := filepath.Join(home, "code", "appeared-later") - initJJAt(t, late) - r = httptest.NewRequest("POST", "/projects/refresh", nil) - w = httptest.NewRecorder() - hub.handler().ServeHTTP(w, r) - if w.Code != http.StatusSeeOther { - t.Fatalf("refresh = %d", w.Code) - } - projectOfKind(t, hub, projectJJ, "appeared-later") -} - -func TestProjectHubRefusesCrossOriginMutation(t *testing.T) { - home := t.TempDir() - projectTestEnv(t, home) - repo := filepath.Join(home, "code", "ready") - initJJAt(t, repo) - hub := testProjectHub(t, home) - p := projectOfKind(t, hub, projectJJ, "ready") - r := httptest.NewRequest("POST", "/projects/"+p.ID+"/start", nil) - r.Header.Set("Origin", "https://elsewhere.example") - w := httptest.NewRecorder() - hub.handler().ServeHTTP(w, r) - if w.Code != http.StatusForbidden { - t.Fatalf("cross-origin start = %d", w.Code) - } - if active, err := hasVRSidecar(repo); err != nil || active { - t.Fatalf("cross-origin request created sidecar: %v, %v", active, err) - } -} |
