Skip to content

test: review features on every engine (unit, component, live, e2e), and the bugs that found - #485

Merged
huyplb merged 6 commits into
mainfrom
test/review-features-dialects-xhwu6u
Oct 6, 2026
Merged

huyplb merged 6 commits into
mainfrom
test/review-features-dialects-xhwu6u

Conversation

@huyplb

@huyplb huyplb commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Implements docs/plans/2026-10-06-review-features-test-plan.md on top of #484. Every layer ran here against real servers: 11 docker-compose engines (Db2 included), plus DuckDB and SQLite.

What was added

Unit (packages/sql)
Each matrix names every engine, and a test fails when an engine is added without an answer.

  • routine-privileges.matrix.test.ts: per engine:
    • which catalog the first rung reads routine grants from
    • the exact REVOKE for PROCEDURE / FUNCTION / ROUTINE, or the refusal
    • how each catalog spelling normalises
    • where splitHeldPrivileges puts each object type, including DENY
  • backup-history.matrix.test.ts: per engine:
    • whether it keeps a history (and that the query classifies as a read)
    • whether its backup can be run from the panel (and that it needs editor.ddl)
    • that hostile restore keys (injection attempts, 13- and 15-digit Db2 stamps) never reach the restore as written
  • schema-compare.matrix.test.ts: the light Compare check equals dialectFeatures().schemaCompare for every engine, spelling and non-engine.

Component (web-ui)

  • AccessGrantsStage.dialects.test.tsx: one render per grant-capable engine.
  • BackupRestorePanel.dialects.test.tsx: run and list offered exactly where they should be; the password, error and empty states; pick, then "Restore the newest instead".

Live (FOX_IT_DB=1)
Each engine is skipped by name, with the server's message, when its own probe fails.

  • access-live.test.ts, per engine:
    • create a role, a table, a function and a procedure
    • grant on each, plus one database- or schema-wide privilege
    • read them back through probeDbAccess and split them as the Grants view does
    • revoke with both the Database Access SQL and the Grants grid SQL, then read again
  • backup-live.test.ts:
    • SQL Server: BACKUP → msdb → RESTORE VERIFYONLY on the picked file
    • Db2: CLP backup → DB_HISTORY → TAKEN AT → db2ckbkp
    • ClickHouse: backup to the allowed disk → system.backups → restore under _restored
    • DuckDB: EXPORT / IMPORT
    • CockroachDB: nodelocal BACKUP / RESTORE
  • History queries run through the same pager as /sql/execute.
  • The Db2 case takes an offline backup, so run this file on its own or with --no-file-parallelism.

E2E (apps/e2e)

  • Every seed gains fox_reader: SELECT on a table, EXECUTE on fn_get_discount (Db2's seed gains the function), and one wide grant.
  • access-assistant-dialects: Grants opens on that role with the boxes ticked and the wide grant listed under the grid.
  • backup-restore-ui: on SQL Server, run → confirm → listed → pick → restore names that file.

Bugs found and fixed

Each failed before its fix and passes after. That was checked by reverting each fix (or mutating the SQL) and rerunning.

Engine Bug From
SQL Server Privileges never read: collation conflict in the UNION (permission_name vs a role name on a default-collation database) earlier
SQL Server "List backups" always failed in the app: TOP (20) next to the pager's OFFSET … FETCH #484
TiDB First rung read mysql.procs_priv, which TiDB lacks, so it fell back and lost role memberships #484
TiDB information_schema TABLE_ and SCHEMA_PRIVILEGES are empty even for root; now read from mysql.tables_priv and mysql.db earlier
CockroachDB REVOKE … ON ROUTINE is a syntax error; the routine rung now reports FUNCTION or PROCEDURE from information_schema.routines #484
Db2 Routine REVOKE needs RESTRICT (both emitters) #484
Db2 CONNECT granted to a role was filtered out of DBAUTH earlier
Redis, MongoDB Were offered SQL GRANT text; they now refuse with the existing non-SQL reason earlier
all isKnownEngine('constructor') was true earlier
seed-all.sh (Db2) Ran every statement without a connection (bash execs the last command of su -c) earlier

The local ClickHouse also gets a backup disk (docker/init/clickhouse/backups.xml), so its backup path runs instead of being refused.

Results

  • npx vitest run: 5843 passed, 0 failed.
  • Typecheck (web and e2e): clean. eslint on changed paths: clean. npm run test-ids: no drift.
  • Live: 35 of 35 tests across postgres, yugabytedb, cockroachdb, redshift (Postgres stand-in), mysql, mariadb, tidb, sqlserver, oracle, db2 and clickhouse, plus duckdb.
  • E2E:
    • access-assistant-dialects: 45 passed, 1 skipped (ClickHouse has no Grants builder).
    • backup-restore-ui: 4 of 4.
    • lazy-loading: 4 of 4.
    • compare-flow (Postgres): 11 of 11.

Not covered

🤖 Generated with Claude Code

https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi


Generated by Claude Code


Note

Medium Risk
Touches privilege catalog SQL and GRANT/REVOKE generation across many engines (including SQL Server and TiDB), which are security-sensitive paths; most risk is mitigated by extensive new live and matrix tests rather than broad refactors.

Overview
Adds cross-engine test coverage for Access Grants, Backup & Restore, and schema-compare, plus production fixes those tests exposed.

Testing: New unit matrix tests in packages/sql (routine privileges, backup history/run permissions, schema-compare parity). Component tests render AccessGrantsStage and BackupRestorePanel once per dialect. FOX_IT_DB=1 live suites grant/revoke against real containers and exercise backup → history → restore round-trips. E2E gains seeded fox_reader roles in Docker init scripts, a Grants test that asserts held grid cells and “also holds” text, SQL Server backup run/list/pick in the browser, and shared helpers for the Principals/Grants flow.

SQL & infra fixes: TiDB reads grants from mysql.tables_priv / mysql.db and skips routine catalogs; SQL Server privilege UNION uses DATABASE_DEFAULT collation and drops TOP from msdb history for the SQL pager; CockroachDB routine revokes require FUNCTION/PROCEDURE; Db2 routine revokes append RESTRICT and role CONNECT grants are included; Postgres-family routine grants join information_schema.routines; Redis/MongoDB are marked unsupported for SQL GRANT; isKnownEngine uses Object.hasOwn. ClickHouse gets an allowed backup disk config; seed-all.sh keeps a single Db2 CLP session.

A test plan doc describes the layered strategy.

Reviewed by Cursor Bugbot for commit 28211f9. Bugbot is set up for automated code reviews on this repo. Configure here.

huyplb and others added 5 commits October 5, 2026 23:39
Unit matrices per feature and engine, component tests for the two screens,
FOX_IT_DB=1 live tests that run the new catalog and backup SQL against each
container, and the e2e additions. Written because none of the new SQL in
#484 has run against a real server yet.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ervers; fix what they found

Unit: routine-privileges.matrix.test.ts names every engine (fails when one is
added without an answer): which catalog its first rung reads routine grants
from, the exact REVOKE for PROCEDURE / FUNCTION / ROUTINE or the refusal, how
each spelling normalises, and where splitHeldPrivileges puts each object type.
schema-compare.matrix.test.ts: the light Compare check equals the capability
table for every engine, spelling and non-engine.

Live (FOX_IT_DB=1, access-live.test.ts): per engine, create a role, table,
function and procedure, grant, read back through probeDbAccess, split as the
Grants view does, revoke with Database Access SQL and with the grid's SQL,
read again. Skipped by name when a server does not answer its own probe.

Fixed, each failing live before and passing after:
- SQL Server: privileges were never read on a database with the server's
  default collation (UNION collation conflict on permission_name).
- TiDB: the first rung read mysql.procs_priv, which TiDB lacks, so it fell
  back and lost role memberships; and its information_schema TABLE_/
  SCHEMA_PRIVILEGES are empty, so table and schema grants now come from
  mysql.tables_priv and mysql.db. Routine grants are refused (TiDB has none).
- CockroachDB rejects ON ROUTINE: the Postgres-family routine rung now reports
  FUNCTION or PROCEDURE from information_schema.routines.
- Db2: routine REVOKE needs RESTRICT (both emitters), and CONNECT granted to a
  role was filtered out of DBAUTH.
- Redis and MongoDB were offered SQL GRANT text; they now refuse with the
  existing non-SQL reason.
- isKnownEngine('constructor') was true (`in` walks the prototype).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi
backup-live.test.ts (FOX_IT_DB=1): SQL Server runs the panel's BACKUP, msdb
lists it first, and the restore built from that key passes RESTORE
VERIFYONLY; Db2 takes the CLP backup, DB_HISTORY lists it, and TAKEN AT names
an image db2ckbkp verifies; ClickHouse backs up to its allowed disk,
system.backups lists it, and the picked backup restores beside the live
database; DuckDB EXPORT/IMPORT keeps every row; CockroachDB BACKUP INTO a
nodelocal collection restores under a new name. Each fails when its history
query returns the wrong key (checked by mutating each).

backup-history.matrix.test.ts: every engine says whether it keeps a history
(and that the query is a read), whether the panel can run its backup (and
that it needs editor.ddl), and that hostile restore keys never reach the
restore as written.

The local ClickHouse gets a backup disk (docker/init/clickhouse/backups.xml),
so its backup path runs instead of being refused.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi
…ngine

AccessGrantsStage.dialects.test.tsx renders the Grants stage once per
grant-capable engine on a catalog shaped like that engine's: one view with
no mode switch, the grid opens on what is held (routines included, whatever
the catalog calls them), the database-, schema- or system-wide grant is
listed under it, and the general section is there to edit it. ClickHouse,
which has no grid cells or builder, names its table grant instead of hiding
it, and says the builder does not support it.

BackupRestorePanel.dialects.test.tsx: run offered exactly where the backup is
SQL the server runs, history exactly where the server keeps one, both waiting
for a password not in this session, failed run and failed listing showing
the server's message, an empty history saying so, and a pick undone by
"Restore the newest instead".

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi
…fix SQL Server backup listing

Seeds: every engine gains fox_reader, holding SELECT on demo_a.customers,
EXECUTE on demo_a.fn_get_discount (where the engine has routines; Db2's seed
gains the function) and one database-, schema- or system-wide grant. Re-runnable.

access-assistant-dialects: per engine, Grants opens on fox_reader with no mode
switch, the table and function boxes ticked, the wide grant listed under the
grid, and nothing to change. The "reached at least one database" check was
inside beforeAll, where it never ran as a test; it is a test again. The
Principals and Grants-stage waits are shared helpers now.

backup-restore-ui: SQL Server runs the backup once confirmed, msdb lists it,
picking it points the restore at that file, and "Restore the newest instead"
undoes it. Postgres offers no server actions.

Fixed: the SQL Server history query used TOP (20), and /sql/execute pages
every read with OFFSET … FETCH, which SQL Server refuses beside TOP, so
"List backups" always failed in the app. backup-live now runs history through
the same pager (it passed bare before).

seed-all.sh: the Db2 seed ran every statement without a connection
(SQL1024N): bash execs the last command of `su -c`, so the CLP lost its back
end between `connect` and `-tvf`.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi
@cursor

cursor Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: 0751f9db-c4bc-41e1-ae9f-62d3a1f7288e)

Brings in main, with source-map-js 1.2.2 (#482).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016PyNv48vpYUw2HhpjYFNUi
@cursor

cursor Bot commented Oct 6, 2026

Copy link
Copy Markdown
Contributor

Bugbot couldn't run - usage limit reached

Bugbot is counted against Cursor usage for this user or team, and this run hit a usage or spend limit.

A user or team admin can review and increase usage limits in the Cursor dashboard.

(requestId: bd06a775-d06d-486c-ad4b-8f5769b4af79)

@huyplb
huyplb changed the base branch from feat/access-backup-recommendations to main October 6, 2026 16:13
@huyplb
huyplb merged commit da319f3 into main Oct 6, 2026
11 checks passed
@huyplb
huyplb deleted the test/review-features-dialects-xhwu6u branch October 6, 2026 16:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants