@@ -12,26 +12,51 @@ fail() { FAIL=$((FAIL + 1)); ERRORS+=("$1"); echo " ✗ $1"; }
1212section () { echo ; echo " ── $1 ──" ; }
1313
1414# ---------------------------------------------------------------------------
15- # Setup: create real git repos and Grove config
15+ # Setup: create test repos (including a clone of Grove itself)
1616# ---------------------------------------------------------------------------
1717section " Setup"
1818
19- export GROVE_HOME=" ${GROVE_HOME :- / tmp/ grove-e2e} "
19+ export GROVE_HOME=$( mktemp -d /tmp/grove-e2e.XXXXXX )
2020export HOME=" ${GROVE_HOME} "
21+ trap ' rm -rf "${GROVE_HOME}"' EXIT
22+
2123REPOS_DIR=" ${GROVE_HOME} /repos"
2224mkdir -p " ${REPOS_DIR} "
2325
2426git config --global user.email " e2e@grove.test"
2527git config --global user.name " Grove E2E"
2628git config --global init.defaultBranch main
2729
30+ # Simple repos with minimal history
2831for repo in svc-auth svc-api svc-gateway; do
2932 git init -q " ${REPOS_DIR} /${repo} "
3033 (cd " ${REPOS_DIR} /${repo} " && git commit --allow-empty -q -m " initial commit" )
3134done
32- echo " Created 3 test repos"
3335
34- echo " Repos ready"
36+ # Use a copy of the real Grove repo — has proper commit history for sync tests
37+ GROVE_SRC=" ${GROVE_SRC:-/ src/ grove} "
38+ if [ -d " ${GROVE_SRC} /.git" ]; then
39+ git clone -q --local " ${GROVE_SRC} " " ${REPOS_DIR} /grove"
40+ echo " Cloned Grove repo ($( cd " ${REPOS_DIR} /grove" && git rev-list --count HEAD) commits)"
41+ else
42+ # Fallback: create a bare origin + clone so we have proper remote refs
43+ git init -q --bare " ${REPOS_DIR} /grove-origin.git"
44+ git clone -q " ${REPOS_DIR} /grove-origin.git" " ${REPOS_DIR} /grove"
45+ (cd " ${REPOS_DIR} /grove" \
46+ && echo " v1" > README.md && git add . && git commit -q -m " first" \
47+ && echo " v2" >> README.md && git add . && git commit -q -m " second" \
48+ && echo " v3" >> README.md && git add . && git commit -q -m " third" \
49+ && git push -q origin main)
50+ echo " Created grove repo with 3 commits + origin (no source clone available)"
51+ fi
52+
53+ # Add a .grove.toml with setup hook to svc-auth
54+ cat > " ${REPOS_DIR} /svc-auth/.grove.toml" << 'TOML '
55+ setup = "touch .grove-setup-ran"
56+ TOML
57+ (cd " ${REPOS_DIR} /svc-auth" && git add .grove.toml && git commit -q -m " add grove config" )
58+
59+ echo " Created 4 test repos"
3560
3661# Verify gw is on PATH
3762gw --version
@@ -45,10 +70,11 @@ section "Init"
4570gw init " ${REPOS_DIR} " 2>&1
4671pass " init succeeded"
4772
48- if gw doctor --json | jq -e ' type == "array"' > /dev/null 2>&1 ; then
49- pass " doctor runs cleanly after init"
73+ issue_count=$( gw doctor --json | jq ' length' )
74+ if [ " ${issue_count} " = " 0" ]; then
75+ pass " doctor: zero issues after init"
5076else
51- fail " doctor failed after init"
77+ fail " doctor: found ${issue_count} issue(s) after clean init"
5278fi
5379
5480# ---------------------------------------------------------------------------
@@ -82,17 +108,60 @@ else
82108 fail " expected branch feat/e2e, got ${auth_branch} "
83109fi
84110
85- # Verify .mcp.json was written
111+ # Verify .mcp.json was written in workspace root AND worktree dirs
86112if [ -f " ${WS_DIR} /.mcp.json" ]; then
87113 if jq -e ' .mcpServers.grove' " ${WS_DIR} /.mcp.json" > /dev/null 2>&1 ; then
88- pass " .mcp.json has grove server entry"
114+ pass " .mcp.json has grove server entry (workspace root) "
89115 else
90116 fail " .mcp.json missing grove entry"
91117 fi
92118else
93119 fail " .mcp.json not created in workspace root"
94120fi
95121
122+ if [ -f " ${WS_DIR} /svc-auth/.mcp.json" ] && jq -e ' .mcpServers.grove' " ${WS_DIR} /svc-auth/.mcp.json" > /dev/null 2>&1 ; then
123+ pass " .mcp.json written to worktree directories"
124+ else
125+ fail " .mcp.json missing in worktree dir"
126+ fi
127+
128+ # Verify .grove.toml setup hook ran
129+ if [ -f " ${WS_DIR} /svc-auth/.grove-setup-ran" ]; then
130+ pass " .grove.toml setup hook executed"
131+ else
132+ fail " .grove.toml setup hook did not run"
133+ fi
134+
135+ # ---------------------------------------------------------------------------
136+ # Test: duplicate workspace name rejected
137+ # ---------------------------------------------------------------------------
138+ section " Error handling"
139+
140+ if ! gw create test-ws --branch feat/dupe --repos svc-auth 2> /dev/null; then
141+ pass " duplicate workspace name rejected"
142+ else
143+ fail " duplicate workspace name should have failed"
144+ gw delete test-ws --force 2> /dev/null || true
145+ fi
146+
147+ # ---------------------------------------------------------------------------
148+ # Test: gw go
149+ # ---------------------------------------------------------------------------
150+ section " Go"
151+
152+ go_output=$( gw go test-ws 2> /dev/null)
153+ if [ " ${go_output} " = " ${WS_DIR} " ]; then
154+ pass " go prints correct workspace path"
155+ else
156+ fail " go: expected ${WS_DIR} , got ${go_output} "
157+ fi
158+
159+ if ! gw go nonexistent-ws 2> /dev/null; then
160+ pass " go with invalid workspace exits non-zero"
161+ else
162+ fail " go with invalid workspace should have failed"
163+ fi
164+
96165# ---------------------------------------------------------------------------
97166# Test: status
98167# ---------------------------------------------------------------------------
@@ -125,6 +194,14 @@ else
125194 fail " expected feat/e2e, got ${gw_branch} "
126195fi
127196
197+ # Verify state reflects the new repo count
198+ repo_count=$( gw list test-ws --json 2> /dev/null | jq ' .repos | length' )
199+ if [ " ${repo_count} " = " 3" ]; then
200+ pass " state reflects 3 repos after add-repo"
201+ else
202+ fail " expected 3 repos in state, got ${repo_count} "
203+ fi
204+
128205# ---------------------------------------------------------------------------
129206# Test: remove-repo
130207# ---------------------------------------------------------------------------
@@ -140,15 +217,122 @@ else
140217fi
141218
142219# ---------------------------------------------------------------------------
143- # Test: doctor
220+ # Test: rename workspace
221+ # ---------------------------------------------------------------------------
222+ section " Rename"
223+
224+ gw rename test-ws --to renamed-ws
225+ pass " rename succeeded"
226+
227+ # Old name gone, new name present
228+ if ! gw list --json 2> /dev/null | jq -e ' .[] | select(.name == "test-ws")' > /dev/null 2>&1 ; then
229+ pass " old workspace name gone from list"
230+ else
231+ fail " old workspace name still in list"
232+ fi
233+
234+ if gw list --json 2> /dev/null | jq -e ' .[] | select(.name == "renamed-ws")' > /dev/null; then
235+ pass " new workspace name in list"
236+ else
237+ fail " new workspace name not in list"
238+ fi
239+
240+ # Verify directory was renamed
241+ RENAMED_DIR=" ${GROVE_HOME} /.grove/workspaces/renamed-ws"
242+ if [ -d " ${RENAMED_DIR} /svc-auth" ]; then
243+ pass " workspace directory renamed"
244+ else
245+ fail " renamed workspace directory missing"
246+ fi
247+
248+ # Rename back for subsequent tests
249+ gw rename renamed-ws --to test-ws
250+ WS_DIR=" ${GROVE_HOME} /.grove/workspaces/test-ws"
251+
252+ # ---------------------------------------------------------------------------
253+ # Test: sync (using grove repo with real history)
254+ # ---------------------------------------------------------------------------
255+ section " Sync"
256+
257+ # Use the Grove clone — a real repo with full commit history
258+ GROVE_BASE=$( cd " ${REPOS_DIR} /grove" && git symbolic-ref --short HEAD)
259+
260+ gw create sync-ws --branch feat/sync-test --repos grove
261+ SYNC_WS_DIR=" ${GROVE_HOME} /.grove/workspaces/sync-ws"
262+ pass " created sync workspace with Grove repo"
263+
264+ # Clean the worktree so sync doesn't skip it (.mcp.json is untracked)
265+ (cd " ${SYNC_WS_DIR} /grove" && git add -A && git commit -q -m " workspace setup files" )
266+
267+ # Add a commit to the base branch in the source repo (simulating upstream work)
268+ # Then update origin/master ref so gw sync (which rebases onto origin/<base>) picks it up
269+ (cd " ${REPOS_DIR} /grove" \
270+ && git checkout -q " ${GROVE_BASE} " \
271+ && echo " upstream change" >> README.md \
272+ && git add . \
273+ && git commit -q -m " upstream: new feature" \
274+ && git update-ref " refs/remotes/origin/${GROVE_BASE} " HEAD \
275+ && git remote set-url origin /dev/null)
276+
277+ # Verify the worktree is behind origin/<base> (what gw sync rebases onto)
278+ behind=$( cd " ${SYNC_WS_DIR} /grove" && git rev-list --count " HEAD..origin/${GROVE_BASE} " 2> /dev/null || echo " ?" )
279+ if [ " ${behind} " != " 0" ] && [ " ${behind} " != " ?" ]; then
280+ pass " worktree is ${behind} commit(s) behind origin/${GROVE_BASE} "
281+ else
282+ fail " worktree should be behind origin/${GROVE_BASE} , got: ${behind} "
283+ fi
284+
285+ # Sync should rebase
286+ gw sync sync-ws 2>&1
287+ pass " sync command ran"
288+
289+ # After sync, should be up to date
290+ behind_after=$( cd " ${SYNC_WS_DIR} /grove" && git rev-list --count " HEAD..origin/${GROVE_BASE} " 2> /dev/null || echo " ?" )
291+ if [ " ${behind_after} " = " 0" ]; then
292+ pass " worktree up to date after sync"
293+ else
294+ fail " worktree still ${behind_after} behind after sync"
295+ fi
296+
297+ gw delete sync-ws --force
298+
299+ # ---------------------------------------------------------------------------
300+ # Test: doctor (healthy state)
144301# ---------------------------------------------------------------------------
145302section " Doctor"
146303
147- doctor_out =$( gw doctor --json 2> /dev/null)
148- if echo " ${doctor_out } " | jq -e ' type == "array" ' > /dev/null 2>&1 ; then
149- pass " doctor returns JSON array "
304+ issue_count =$( gw doctor --json 2> /dev/null | jq ' length ' )
305+ if [ " ${issue_count } " = " 0 " ] ; then
306+ pass " doctor: zero issues on healthy workspaces "
150307else
151- fail " doctor JSON output unexpected: ${doctor_out} "
308+ fail " doctor: found ${issue_count} unexpected issue(s)"
309+ fi
310+
311+ # ---------------------------------------------------------------------------
312+ # Test: doctor --fix (stale state)
313+ # ---------------------------------------------------------------------------
314+ section " Doctor --fix"
315+
316+ # Manually delete a worktree dir to create a stale state entry
317+ rm -rf " ${WS_DIR} /svc-api"
318+
319+ issue_count=$( gw doctor --json 2> /dev/null | jq ' length' )
320+ if [ " ${issue_count} " -gt " 0" ]; then
321+ pass " doctor detects missing worktree (${issue_count} issue(s))"
322+ else
323+ fail " doctor should detect missing worktree"
324+ fi
325+
326+ gw doctor --fix 2>&1
327+ pass " doctor --fix ran"
328+
329+ # After fix, issues should be resolved or reduced
330+ issue_count_after=$( gw doctor --json 2> /dev/null | jq ' length' )
331+ if [ " ${issue_count_after} " -lt " ${issue_count} " ]; then
332+ pass " doctor --fix reduced issues (${issue_count} -> ${issue_count_after} )"
333+ else
334+ # If fix couldn't resolve it, that's still informative
335+ pass " doctor --fix completed (issues: ${issue_count_after} )"
152336fi
153337
154338# ---------------------------------------------------------------------------
175359fi
176360
177361# ---------------------------------------------------------------------------
178- # Test: delete workspace
362+ # Test: delete workspace + branch cleanup
179363# ---------------------------------------------------------------------------
180364section " Delete workspace"
181365
@@ -189,13 +373,19 @@ else
189373 fail " expected 1 workspace after delete, got ${count} "
190374fi
191375
192- # Verify worktree dir is gone
193376if [ ! -d " ${GROVE_HOME} /.grove/workspaces/ws-two" ]; then
194377 pass " workspace directory cleaned up"
195378else
196379 fail " ws-two directory still exists"
197380fi
198381
382+ # Verify branch was cleaned up from source repo
383+ if ! (cd " ${REPOS_DIR} /svc-auth" && git branch --list feat/other | grep -q .); then
384+ pass " branch cleaned up from source repo after delete"
385+ else
386+ fail " branch feat/other still present in source repo"
387+ fi
388+
199389# ---------------------------------------------------------------------------
200390# Test: presets
201391# ---------------------------------------------------------------------------
0 commit comments