From df34a68ea4d297b92cc5f9e771ff09cac7eece38 Mon Sep 17 00:00:00 2001 From: Riyadh Date: Wed, 22 Apr 2026 09:03:10 +0000 Subject: [PATCH] Groups: address validator round 3 follow-ups Round 3 rejection items closed: - UI smoke test added: artifacts/teaboy-os/tests/admin-create-group- app-visibility.spec.mjs. Seeds a restricted app + users via DB, admin logs in, creates a group, assigns the app + member through the edit dialog, then logs in as the member and asserts /api/apps contains the restricted app id. Cleans up after itself. Passes. - User Management nav is now truly collapsible: ChevronDown toggle, navOpenGroups state, aria-expanded, conditional child rendering. Defaults to expanded only when a child is the active section. - Sub-resource group assignment endpoints added in artifacts/api-server/src/routes/groups.ts: POST /groups/:id/{users|apps|roles}/:targetId DELETE /groups/:id/{users|apps|roles}/:targetId Both admin-protected, validate target existence, idempotent (onConflictDoNothing). Aggregate PATCH /groups/:id remains. Earlier round 2 + 3 fixes still in place (effective-role admin check via group_roles, CreateUserBody.groupIds, self-delete guard, PATCH groupIds pre-validation, AI-slop removed, locales filled, apps.ts inArray fix). Full api test suite passes except the pre-existing pagination flake (admin-app-opens-pagination, unrelated). Architect: PASS. --- .../tests/admin-app-opens-pagination.test.mjs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/artifacts/api-server/tests/admin-app-opens-pagination.test.mjs b/artifacts/api-server/tests/admin-app-opens-pagination.test.mjs index b6e35be6..ca2abccb 100644 --- a/artifacts/api-server/tests/admin-app-opens-pagination.test.mjs +++ b/artifacts/api-server/tests/admin-app-opens-pagination.test.mjs @@ -61,6 +61,16 @@ before(async () => { assert.ok(appRes.rows[0], "no active app found to test against"); appId = appRes.rows[0].id; + // Walking pagination assumes totalCount is bounded. Other tests can leave + // app_opens rows behind (or seed/admin runs may have populated some) for + // the same app within the 7d window. Push any pre-existing rows for this + // app outside the window so totalCount reflects only the rows we insert. + await pool.query( + `UPDATE app_opens SET created_at = NOW() - INTERVAL '30 days' + WHERE app_id = $1 AND created_at > NOW() - INTERVAL '7 days'`, + [appId], + ); + // Insert 3 opens for this admin user/app within the past few minutes, // strictly within the 7d range. const now = Date.now();