From 3a329c592bc8ea329b8cc3a14fe5bc1a1d0ea0c3 Mon Sep 17 00:00:00 2001 From: claude code agent 227 Date: Wed, 24 Jun 2026 03:36:15 +0300 Subject: [PATCH] test(git-sync): e2e guard for the untitled-page + retitle data-loss reshuffle MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Reproduces the browser bug at the API level: create several untitled pages (all collapse to the `_` fallback name), retitle one, sync — assert NO page is trashed and all survive. Caught the data-loss bug fixed in 4376c5a6. Co-Authored-By: Claude Opus 4.8 --- apps/server/test/git-sync-e2e-advanced.sh | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/apps/server/test/git-sync-e2e-advanced.sh b/apps/server/test/git-sync-e2e-advanced.sh index 076b8039..ac3ac60a 100755 --- a/apps/server/test/git-sync-e2e-advanced.sh +++ b/apps/server/test/git-sync-e2e-advanced.sh @@ -202,6 +202,29 @@ lp_after=$(vault_sha refs/docmost/last-pushed) [ "$lp_before" = "$lp_after" ] && ok "last-pushed ref did NOT advance past the delete commit (retry-safe)" || bad "last-pushed advanced over suppressed deletes ($lp_before -> $lp_after)" # cleanup these pages (hard-delete; they are E2E-ADV-* so teardown also catches them) +# =========================================================================== +say "data-loss guard #2: untitled pages + retitle must NOT trash other pages" +# THE bug from the browser flow: Docmost creates pages UNTITLED (title=''), which +# all serialize to the `_` fallback name. Retitling one reshuffles the `_` +# collision and relocates another's file; git reports the move as delete+add and +# the push used to TRASH the relocated live page. Identity is the pageId now. +ut_before=$(psqlq "select count(*) from pages where space_id='$SPACE_ID' and deleted_at is not null;") +ut_ids="" +for i in 1 2 3 4; do + id=$(api -X POST "$SERVER/api/pages/create" -H 'Content-Type: application/json' -d "{\"spaceId\":\"$SPACE_ID\",\"title\":\"\"}" | grep -o '"id":"[^"]*"' | head -1 | cut -d'"' -f4) + ut_ids="$ut_ids $id"; sync_now +done +# retitle the first one (like typing a title in the editor), then sync twice +first=$(echo $ut_ids | awk '{print $1}') +api -X POST "$SERVER/api/pages/update" -H 'Content-Type: application/json' -d "{\"pageId\":\"$first\",\"title\":\"E2E-ADV-Titled-$RANDOM\"}" >/dev/null +sync_now; sync_now +ut_after=$(psqlq "select count(*) from pages where space_id='$SPACE_ID' and deleted_at is not null;") +live_kept=$(psqlq "select count(*) from pages where space_id='$SPACE_ID' and deleted_at is null and ($(echo $ut_ids | sed "s/ \?\([0-9a-f-]\+\)/ or id='\1'/g; s/^ or //"));") +[ "${ut_after:-9}" = "${ut_before:-0}" ] && ok "no page trashed by the untitled+retitle reshuffle (was the data-loss bug)" || bad "trashed count grew ${ut_before}->${ut_after} (page lost to the reshuffle!)" +[ "${live_kept:-0}" = "4" ] && ok "all 4 untitled/retitled pages still LIVE" || bad "only $live_kept/4 of the untitled pages survived" +# cleanup these via the E2E-ADV teardown (the retitled one) + hard-delete the rest +psqlq "delete from pages where id in ($(echo $ut_ids | sed "s/ \?\([0-9a-f-]\+\)/,'\1'/g; s/^,//"));" >/dev/null + # =========================================================================== say "RESULTS: $PASS passed, $FAIL failed" [ "$FAIL" -eq 0 ] && exit 0 || exit 1