fix(folders): stop archived rows skewing sortOrder, hide them from admin, and cover the two untested modules - #6062
Conversation
…min, and cover the two untested modules nextFolderSortOrder returned min - 1 over ALL rows including soft-deleted ones, so every delete ratcheted the floor further negative and never recovered — an archived folder at -400 forced the next new folder to -401 forever. Both minima (folders and child resources) now see only rows a user can still see, which is how the Files path has always worked. The admin workspace-folders endpoint counted and paginated soft-deleted folders, so an operator saw phantom folders and an inflated total, disagreeing with every user-facing list. Adds naming.test.ts and queries.test.ts. Both modules had zero tests and are mocked at every call site, so their bodies executed in no test anywhere. That left unasserted the two bug classes that caused real defects in the folder migration: the suffix sequence (must start at (1) and skip taken suffixes) and resourceType scoping on the id-keyed lookups, where a missing clause silently files a knowledge base under a table folder. Every assertion is mutation-checked. The first version of the sortOrder test was vacuous — for a root folder the parent condition is itself an isNull node, so a presence-only check passed with the soft-delete filter deleted; it now asserts the specific column.
|
The latest updates on your projects. Learn more about Vercel for GitHub. |
PR SummaryLow Risk Overview
New coverage pins behavior that was previously only mocked at call sites: admin folder listing filters, lifecycle sort-order queries, Reviewed by Cursor Bugbot for commit c029210. Configure here. |
Greptile SummaryFixes soft-deleted folder handling in sort-order and admin listing, and adds first unit coverage for folder naming/queries plus shared mock condition helpers.
Confidence Score: 5/5This PR appears safe to merge; no blocking failures remain from prior or current review. No blocking failure remains.
|
| Filename | Overview |
|---|---|
| apps/sim/lib/folders/lifecycle.ts | Excludes soft-deleted folders and archived child rows from sortOrder minima so deletes no longer ratchet negative sort values. |
| apps/sim/app/api/v1/admin/workspaces/[id]/folders/route.ts | Admin folder list/count now filter deletedAt so operators match user-facing active folder lists. |
| packages/testing/src/mocks/database.mock.ts | Shared helpers flatten and inspect mock SQL condition trees for WHERE-clause assertions in folder tests. |
| apps/sim/lib/folders/naming.test.ts | Covers suffix sequencing and sibling scoping for deduplicateFolderName. |
| apps/sim/lib/folders/queries.test.ts | Covers resourceType scoping, cycle walk, restore short-circuit, list scopes, and toFolderApi serialization. |
Reviews (2): Last reviewed commit: "refactor(testing): share the drizzle con..." | Re-trigger Greptile
Asserting on WHERE clauses is the only way to pin a filter the row-queue mocks cannot enforce — a mock returns whatever was queued regardless of the predicate — so this pattern spreads to every test that guards a query's scoping. It had reached five local copies of the same flatten/has pair, four of them added by the tests in this branch. Moved to @sim/testing beside createMockSqlOperators, whose output shape they parse, so the helper and the node types it depends on live together.
|
@cursor review |
There was a problem hiding this comment.
✅ Bugbot reviewed your changes and found no new issues!
Comment @cursor review or bugbot run to trigger another review on this PR
Reviewed by Cursor Bugbot for commit c029210. Configure here.
Follow-ups from the generic-folders work, on a fresh branch off
origin/staging. Two correctness fixes and the first tests for the two modules that had none.Fixes
nextFolderSortOrdercounted soft-deleted rows. It returnsmin - 1to put a new folder at the top, so including archived rows let every delete ratchet the floor further negative and never recover — an archived folder at-400forces the next new folder to-401, forever. Both minima (folders and child resources) now see only rows a user can still see. The Files path (workspace-file-folder-manager) has always filtered this way; the generic engine never did.The admin workspace-folders endpoint listed soft-deleted folders. Both the count and the page omitted
deletedAt, so an operator inspecting a workspace saw folders sitting in Recently Deleted plus an inflated total — disagreeing with every user-facing folder list. Pre-existing, not cutover fallout.Tests for
naming.tsandqueries.tsBoth modules had zero tests and are
vi.mock'd at every call site, so their bodies executed in no test anywhere. That left the two bug classes that caused real defects in the folder migration completely unasserted server-side:(1), skip taken suffixes, and fill gaps. Its client twinnextUntitledFolderNameis tested and pins the same shape, so the tested client was the only guard on a contract the server could silently drift from.resourceTypescoping on the id-keyed lookups (findActiveFolder,wouldCreateFolderCycle). Every other query is scoped through a list filter; these accept a caller-supplied id, so a missing clause silently crosses resource trees — filing a knowledge base under a table folder where no page renders it.Also covers
resolveRestoredFolderId'srestoringFolderIdsshort-circuit, which is what stops a folder cascade dumping an entire subtree at the workspace root, andtoFolderApi, the single normalizer keeping every folder route on one wire shape.Every assertion is mutation-checked
Each was verified to FAIL when the behaviour it claims to guard is removed:
resourceTypefrom the sibling querydeletedAtfrom the sibling queryresourceTypefromfindActiveFolderresourceTypefrom the cycle walkrestoringFolderIdsshort-circuitdeletedAtfilter innextFolderSortOrderdeletedAtfrom the admin routeWorth flagging: the first version of the sortOrder test was vacuous. For a root folder the parent condition is itself an
isNullnode, so a "some isNull exists" check passed with the soft-delete filter deleted. It now asserts the specific column. That is the third vacuous test caught by mutation-checking in this line of work, which is why every assertion here went through it.Overlap with
realtime-roomsChecked explicitly. This branch touches 6 files; exactly one,
lib/folders/lifecycle.ts", is also touched byrealtime-rooms— which only addsnotifyFolderResourceChangedcalls at lines 22/227/313/393/555, while this change is confined tonextFolderSortOrderat 131–158. A test-merge ofrealtime-roomsinto this branch produces **zero conflicts in any file here**. It does conflict intable-grid.tsxandcheck-api-validation-contracts.ts, but those conflicts already exist betweenrealtime-roomsandorigin/staging` and are unrelated to this work.Not included, deliberately
disabled). Needs a design decision: snapshot prior state at archive time (schema change + migration) versus reporting what needs redeploying. Too big to fold in silently.maxLength50 vs 100). Broad UI work needing visual verification, and it lands infiles.tsx/tables.tsx— both touched byrealtime-rooms, so it would create exactly the overlap this branch was asked to avoid.Testing
Typecheck,
check:api-validation,check:migrations, lint, and 150 tests across the affected suites pass. No migration in this PR.Type of Change
Checklist