Skip to content

fix(query): wire driver truncation signal + user-configurable maxRows (#499) - #507

Merged
alfredo1996 merged 2 commits into
release/1.1from
fix/issue-499-row-cap-banner
Apr 11, 2026
Merged

fix(query): wire driver truncation signal + user-configurable maxRows (#499)#507
alfredo1996 merged 2 commits into
release/1.1from
fix/issue-499-row-cap-banner

Conversation

@alfredo1996

@alfredo1996 alfredo1996 commented Apr 11, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes the dead row-cap banner (#499) and adds the feature you asked for: users can now configure the max row limit per connection.

The bug

Both database drivers (PG + Neo4j) cap results at `config.rowLimit = 5000` BEFORE the API route sees them, but the route's `rawData.length > MAX_ROWS` comparison against 10,000 was never true — the glue layer between the driver's `setStatus(COMPLETE_TRUNCATED)` signal and the executor's callback was missing. `meta.truncated` was never set, and the "Showing first 10,000 rows" banner in `card-container.tsx:569` was unreachable dead code.

The fix

1. Wire `setStatus` through `query-executor`

The executor callback now implements `setStatus`, captures `COMPLETE_TRUNCATED`, and resolves the promise with `{ data, fields, truncated, rowLimit }`. The route forwards both fields into response meta and deletes the dead `MAX_ROWS = 10_000` constant.

`QueryStatus` is now exported from `@neoboard/connection` (previously internal).

2. Per-connection `maxRows` configuration

Added `maxRows` as an optional advanced setting on each connection:

  • Schema (`lib/shared/schemas.ts`): zod field, validated `100..100_000`
  • Executor: spread `rowLimit: credentials.maxRows ?? DEFAULT_MAX_ROWS` onto driver config
  • Connection form (`connections/page.tsx`): new numeric input in both create AND edit Advanced Settings, with help text:

    "Results beyond this cap are truncated and a banner is shown on the widget. Default 5,000. Increase cautiously — higher limits raise per-query memory usage."

  • Default: 5,000 (backwards-compatible — every existing connection keeps today's behavior with zero config change)
  • Form state round-trip via `parse-utils.ts` (`mapConfigToEditForm` now carries `maxRows` alongside the other advanced fields)

3. Dynamic banner text

`card-container.tsx` now renders `"Showing first {rowLimit.toLocaleString()} rows"` using the `rowLimit` field from `meta`. A connection with `maxRows: 25000` will show "Showing first 25,000 rows" on its widgets; one with the default will show "Showing first 5,000 rows".

`use-widget-query.ts` `QueryResult` interface picks up the new optional `rowLimit?: number` field so TypeScript flows the value through end-to-end.

Verification

Suite Result
Route unit tests (`api/query/tests/route.test.ts`) 18/18 passing — rewrote 4 truncation tests to mock the new `{ truncated, rowLimit }` return shape; added a dedicated per-connection override test
Card container states (`card-container-states.test.tsx`) 13/13 passing — added a test asserting the banner reflects a 25,000-row override
Parse utils (`parse-utils.test.ts`) 15/15 passing — added `maxRows` to round-trip fixtures
E2E query-safety (`query-safety.spec.ts`) 8/8 passing in 60s — tests 3 and 4 now assert `meta.truncated === true` AND the banner renders with the dynamic text; new test 4b creates a PG connection with `maxRows: 1000`, runs a query returning 5000 rows, and verifies the driver honors the override (row count, meta.rowLimit, banner text)
`npm run build` Clean
`npm run lint` No new errors (only 7 pre-existing in `cli/`)

Backwards compatibility

  • `maxRows` is optional. Existing connections continue to use `DEFAULT_MAX_ROWS=5000` with no config change.
  • The API response shape is additive: new `meta.rowLimit` is always present, `meta.truncated` appears only when the driver signals it.
  • No DB migration needed — `maxRows` lives in the encrypted connection config JSON alongside the existing advanced settings (connectionTimeout, queryTimeout, etc.).

UX flow for the new setting

  1. Creator opens the connection form → Advanced Settings
  2. Sees a new "Max Rows per Query" input, placeholder `5000`
  3. Enters e.g. `20000`, saves the connection
  4. Every read query on widgets using that connection now caps at 20,000 rows
  5. When a query hits the cap, the widget shows "Showing first 20,000 rows. Refine your query to see all results."

Files changed (13)

  • `connection/src/index.ts` — export `QueryStatus`
  • `app/src/lib/query/query-executor.ts` — `setStatus` handler, `DEFAULT_MAX_ROWS`, `maxRows` plumbing
  • `app/src/app/api/query/route.ts` — remove dead `MAX_ROWS` check, forward `truncated`/`rowLimit`
  • `app/src/lib/shared/schemas.ts` — zod field
  • `app/src/lib/shared/parse-utils.ts` — form round-trip
  • `app/src/hooks/use-connections.ts` — type extension
  • `app/src/hooks/use-widget-query.ts` — `QueryResult.rowLimit`
  • `app/src/components/card-container.tsx` — dynamic banner text
  • `app/src/app/(dashboard)/connections/page.tsx` — UI fields (create + edit)
  • `app/src/app/api/query/tests/route.test.ts` — updated mocks
  • `app/src/components/tests/card-container-states.test.tsx` — new assertions
  • `app/src/lib/tests/shared/parse-utils.test.ts` — `maxRows` fixtures
  • `app/e2e/query-safety.spec.ts` — flipped + new per-connection override test

Closes #499

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added "Max Rows per Query" setting to connection create/edit UI (100–100,000; default 5,000).
    • Truncation banner now shows the dynamic effective row limit (e.g., "Showing first 1,000 rows").
  • Bug Fixes

    • Truncation detection/reporting now follows driver-reported signals; API/UI surface the returned truncated flag and rowLimit.

…#499)

## The bug

The row-cap "Showing first 10,000 rows" banner was unreachable dead
code. Both database drivers (PG + Neo4j) capped results at
`config.rowLimit = 5000` BEFORE the API route could see them, so the
route's `rawData.length > MAX_ROWS` comparison (against 10,000) was
never true. `meta.truncated` was never set, `widgetQuery.data.truncated`
was never true, and the banner in card-container.tsx:569 never rendered.

Both drivers ALREADY computed `isTruncated` internally and called
`callbacks.setStatus(QueryStatus.COMPLETE_TRUNCATED)` — but the
executor's callback object at query-executor.ts:127-135 didn't
implement `setStatus`, so the signal was silently dropped. The glue
layer between driver and route was missing.

## The fix

### 1. Wire setStatus through query-executor

query-executor.ts now provides a setStatus handler that captures the
`COMPLETE_TRUNCATED` enum into a local `truncated` boolean. The
promise resolves with `{ data, fields, truncated, rowLimit }`. The
route forwards `truncated` and `rowLimit` into response meta, and
removes the dead `rawData.length > MAX_ROWS` check + the `MAX_ROWS =
10_000` constant entirely.

Exported `QueryStatus` from @neoboard/connection since the executor
now depends on it (previously only internal to the connection package).

### 2. User-configurable maxRows per connection

Added `maxRows` as an optional advanced setting on each connection.
When unset, defaults to `DEFAULT_MAX_ROWS = 5000` (backwards-compatible
with today's behavior). Creators can raise or lower the cap per
connection via Advanced Settings > "Max Rows per Query" in the
connection form.

- `lib/shared/schemas.ts` — zod field, validated 100..100_000
- `lib/query/query-executor.ts` — `ConnectionCredentials.maxRows`,
  spread `rowLimit: effectiveRowLimit` onto driver config,
  capture in `DEFAULT_MAX_ROWS` export
- `app/(dashboard)/connections/page.tsx` — new input in both
  create + edit dialogs, help text explaining the memory tradeoff
- `hooks/use-connections.ts` — type extension
- `lib/shared/parse-utils.ts` — form state round-trip

### 3. Dynamic banner text

`card-container.tsx` now renders `"Showing first {rowLimit} rows"`
using the dynamic `rowLimit` field forwarded from meta. Previously
hardcoded "10,000" regardless of the actual cap. `use-widget-query.ts`
`QueryResult` interface picks up a new optional `rowLimit?: number`
field so TypeScript flows the value through.

## Verification

- [x] Route unit tests: 18/18 passing — rewrote 4 tests to mock the
  new { truncated, rowLimit } return shape; added a test for the
  per-connection override path.
- [x] Card container unit tests: 13/13 passing (was 11). Added a new
  test asserting the banner reflects a custom per-connection rowLimit
  (25,000 in the mock), flipped the existing "Showing first 10,000"
  assertion to "Showing first 5,000" to match the default.
- [x] parse-utils tests: 15/15 passing — added maxRows to the
  round-trip fixtures.
- [x] E2E query-safety spec: 8/8 passing in 60s. Tests 3 and 4 now
  assert meta.truncated === true AND the banner renders with the
  dynamic text. Added test 4b: create a PG connection with
  `maxRows: 1000`, run a query returning 5000 rows, verify the
  driver honors the override (body.data.data.length === 1000,
  meta.rowLimit === 1000, UI banner shows "Showing first 1,000 rows").
- [x] Build clean, lint clean (only pre-existing errors in cli/
  unrelated files).

## Backwards compatibility

- `maxRows` is optional. Existing connections (seeded conn-pg-001 /
  conn-neo4j-001 / any user-created) continue to use
  DEFAULT_MAX_ROWS=5000 with no config change.
- The API response shape is additive: new `meta.rowLimit` is always
  present, `meta.truncated` appears only when the driver signals it
  (same as before). Existing clients that don't read these fields
  are unaffected.
- No DB migration needed — `maxRows` lives in the encrypted connection
  config JSON alongside the existing advanced settings.

Closes #499

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Apr 11, 2026

Copy link
Copy Markdown

Walkthrough

Driver-level row-cap signaling was implemented and propagated through the executor and API route. Per-connection maxRows was added to configs and UI. Responses now include meta.truncated and meta.rowLimit; tests and the truncation banner were updated to use the driver-provided limit.

Changes

Cohort / File(s) Summary
Query executor & driver wiring
app/src/lib/query/query-executor.ts, connection/src/index.ts
Added DEFAULT_MAX_ROWS (5000), added credentials.maxRows, included maxRows in module cache key, and made executeQuery return { data, fields?, truncated, rowLimit }. Re-exported QueryStatus.
API route & tests
app/src/app/api/query/route.ts, app/src/app/api/query/__tests__/route.test.ts
Removed route-level MAX_ROWS slicing; route now forwards truncated and rowLimit from executor result. Tests updated to assert driver-provided truncation metadata.
UI, banner & widget hooks
app/src/components/card-container.tsx, app/src/components/__tests__/card-container-states.test.tsx, app/src/hooks/use-widget-query.ts
Banner reads widgetQuery.data.rowLimit and displays formatted “Showing first N rows”; QueryResult type added rowLimit?: number. Tests updated to assert dynamic rowLimit.
Connections UI & config
app/src/app/(dashboard)/connections/page.tsx, app/src/lib/shared/schemas.ts, app/src/hooks/use-connections.ts
Added maxRows numeric field to create/edit dialogs and schema (100–100_000). useUpdateConnection accepts config.maxRows.
Parsing utilities & tests
app/src/lib/shared/parse-utils.ts, app/src/lib/__tests__/shared/parse-utils.test.ts
mapConfigToEditForm maps config.maxRowsmaxRows string (defaults to ""); tests updated accordingly.
Executor core tests
app/src/lib/__tests__/query/query-executor-core.test.ts
Added tests for truncated signaling, rowLimit propagation, and per-credentials maxRows behavior.
E2E & Playwright config
app/e2e/query-safety.spec.ts, app/playwright.config.ts
E2E assertions flipped to expect meta.truncated === true and meta.rowLimit (and added per-connection maxRows test); Playwright viewport standardized.
Card container tests
app/src/components/__tests__/card-container-states.test.tsx
Updated truncation-warning tests to rely on data.rowLimit value rather than a hardcoded default.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Route as API Route
    participant Executor as Query Executor
    participant Driver as Connection Driver

    Client->>Route: POST /api/query (sql + credentials)
    Route->>Executor: executeQuery(sql, credentials)
    Executor->>Executor: effectiveRowLimit = credentials.maxRows ?? DEFAULT_MAX_ROWS
    Executor->>Driver: runQuery(config with rowLimit)
    Driver->>Driver: execute and apply rowLimit, detect truncation (setStatus)
    Driver->>Executor: onSuccess(data, fields, truncated?)
    Executor->>Route: resolve {data, fields, truncated, rowLimit}
    Route->>Client: respond {data, meta:{truncated, rowLimit}}
    Client->>Client: UI renders banner using meta.rowLimit when truncated
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related issues

Possibly related PRs

Suggested labels

pkg:connection, pkg:app, area:connectors, testing, enhancement

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: wiring driver truncation signals and adding user-configurable maxRows, which are the primary objectives of the PR.
Linked Issues check ✅ Passed All acceptance criteria from #499 are met: driver truncation signaling through executeQuery return shape, meta.truncated properly set, dynamic banner text using rowLimit, reconciled constants (5000 default), per-connection maxRows config (100–100_000), and comprehensive test coverage.
Out of Scope Changes check ✅ Passed All changes directly address the #499 objectives: query executor wiring, schema/types updates for maxRows, UI forms, test updates, and Playwright viewport fix for test stability—no extraneous changes detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/issue-499-row-cap-banner

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/src/app/(dashboard)/connections/page.tsx (1)

325-344: ⚠️ Potential issue | 🟠 Major

Clearing maxRows in edit won't remove an existing override.

If the user deletes the value, parseOptionalInt("") becomes undefined, and that key gets dropped from the PATCH payload. Per the partial-update contract in the comment above, the backend then keeps the old stored maxRows, so this new override is effectively sticky once set. Please add explicit clear semantics here (for example, send a nullable/reset value or expose an explicit “use default” control).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/app/`(dashboard)/connections/page.tsx around lines 325 - 344, The
buildEditConfig function currently drops maxRows when the user clears the input
(because parseOptionalInt("") -> undefined), causing the backend to retain the
previous override; update buildEditConfig to send an explicit clear/reset marker
instead of omitting the key — e.g. detect when editForm.maxRows is an empty
string and set maxRows: null (or set a companion flag like maxRowsUseDefault:
true) so the PATCH payload signals the server to remove the override; refer to
buildEditConfig and editForm.maxRows and parseOptionalInt when making this
change.
🧹 Nitpick comments (3)
app/src/lib/query/query-executor.ts (1)

17-17: Avoid hardcoded row-cap default drift.

Use the shared connection default instead of literal 5000 so app and connector defaults cannot diverge.

♻️ Suggested change
-export const DEFAULT_MAX_ROWS = 5000;
+export const DEFAULT_MAX_ROWS = DEFAULT_CONNECTION_CONFIG.rowLimit;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/lib/query/query-executor.ts` at line 17, Replace the hardcoded
literal in DEFAULT_MAX_ROWS by referencing the shared connection default instead
of 5000: update the constant DEFAULT_MAX_ROWS to import and use the project-wide
connection max-rows default (e.g., ConnectionDefaults.MAX_ROWS or
CONNECTION_DEFAULTS.maxRows) from the shared connection/config module so the app
and connector share one source of truth; modify the export in query-executor.ts
to reference that imported symbol and add the corresponding import statement.
app/src/hooks/use-connections.ts (1)

85-85: Consider aligning create/test-inline config types with maxRows too.

UpdateConnectionInput now supports maxRows; mirroring this in CreateConnectionInput and TestInlineInput would keep client API types consistent and reduce drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/hooks/use-connections.ts` at line 85, CreateConnectionInput and
TestInlineInput need the same maxRows support as UpdateConnectionInput to keep
the client API consistent: add an optional maxRows?: number property to both
CreateConnectionInput and TestInlineInput type/interface declarations, and
propagate that field through any creators/validators/serializers (e.g.,
functions building payloads for createConnection and testInline) so the value is
sent/validated the same way UpdateConnectionInput handles it.
app/src/app/api/query/__tests__/route.test.ts (1)

470-506: Assert the route passes maxRows into executeQuery.

This case currently hardcodes mockExecuteQuery to return rowLimit: 20000, so it would still pass if the route stopped forwarding decryptJson(...).maxRows to the executor. Add a call-argument assertion here to pin the actual route-to-executor wiring for #499.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/app/api/query/__tests__/route.test.ts` around lines 470 - 506, The
test currently stubs mockExecuteQuery to return rowLimit:20000 but doesn't
assert the route actually forwarded decryptJson(...).maxRows into the executor;
update the test after calling POST(makeRequest(...)) to assert mockExecuteQuery
was invoked with a payload containing maxRows: 20000 (e.g.
expect(mockExecuteQuery).toHaveBeenCalledWith(expect.objectContaining({ maxRows:
20000 })) or inspect mockExecuteQuery.mock.calls[0][0].maxRows) so the
route-to-executor wiring (POST handler -> decryptJson -> executeQuery) is
pinned.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/src/components/card-container.tsx`:
- Around line 573-574: The banner currently falls back to 0 when
widgetQuery.data.rowLimit is missing, producing misleading text; update the JSX
around the snippet that renders "Showing first {(widgetQuery.data.rowLimit ??
0).toLocaleString()} rows" to conditionally render a non-numeric fallback (e.g.,
show the number only when widgetQuery.data.rowLimit != null and otherwise render
"Showing limited rows" or "Showing first N rows" with a known default like
widgetQuery.defaultRowLimit), or use a string fallback such as
(widgetQuery.data.rowLimit != null ? widgetQuery.data.rowLimit.toLocaleString()
: "limited") so the message is not "0" when rowLimit is absent; adjust any
related text in the same component (card-container / the banner JSX)
accordingly.

---

Outside diff comments:
In `@app/src/app/`(dashboard)/connections/page.tsx:
- Around line 325-344: The buildEditConfig function currently drops maxRows when
the user clears the input (because parseOptionalInt("") -> undefined), causing
the backend to retain the previous override; update buildEditConfig to send an
explicit clear/reset marker instead of omitting the key — e.g. detect when
editForm.maxRows is an empty string and set maxRows: null (or set a companion
flag like maxRowsUseDefault: true) so the PATCH payload signals the server to
remove the override; refer to buildEditConfig and editForm.maxRows and
parseOptionalInt when making this change.

---

Nitpick comments:
In `@app/src/app/api/query/__tests__/route.test.ts`:
- Around line 470-506: The test currently stubs mockExecuteQuery to return
rowLimit:20000 but doesn't assert the route actually forwarded
decryptJson(...).maxRows into the executor; update the test after calling
POST(makeRequest(...)) to assert mockExecuteQuery was invoked with a payload
containing maxRows: 20000 (e.g.
expect(mockExecuteQuery).toHaveBeenCalledWith(expect.objectContaining({ maxRows:
20000 })) or inspect mockExecuteQuery.mock.calls[0][0].maxRows) so the
route-to-executor wiring (POST handler -> decryptJson -> executeQuery) is
pinned.

In `@app/src/hooks/use-connections.ts`:
- Line 85: CreateConnectionInput and TestInlineInput need the same maxRows
support as UpdateConnectionInput to keep the client API consistent: add an
optional maxRows?: number property to both CreateConnectionInput and
TestInlineInput type/interface declarations, and propagate that field through
any creators/validators/serializers (e.g., functions building payloads for
createConnection and testInline) so the value is sent/validated the same way
UpdateConnectionInput handles it.

In `@app/src/lib/query/query-executor.ts`:
- Line 17: Replace the hardcoded literal in DEFAULT_MAX_ROWS by referencing the
shared connection default instead of 5000: update the constant DEFAULT_MAX_ROWS
to import and use the project-wide connection max-rows default (e.g.,
ConnectionDefaults.MAX_ROWS or CONNECTION_DEFAULTS.maxRows) from the shared
connection/config module so the app and connector share one source of truth;
modify the export in query-executor.ts to reference that imported symbol and add
the corresponding import statement.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 36b36317-043b-4282-96e9-7bc65513a0b4

📥 Commits

Reviewing files that changed from the base of the PR and between 1b063f1 and 976b1b3.

📒 Files selected for processing (13)
  • app/e2e/query-safety.spec.ts
  • app/src/app/(dashboard)/connections/page.tsx
  • app/src/app/api/query/__tests__/route.test.ts
  • app/src/app/api/query/route.ts
  • app/src/components/__tests__/card-container-states.test.tsx
  • app/src/components/card-container.tsx
  • app/src/hooks/use-connections.ts
  • app/src/hooks/use-widget-query.ts
  • app/src/lib/__tests__/shared/parse-utils.test.ts
  • app/src/lib/query/query-executor.ts
  • app/src/lib/shared/parse-utils.ts
  • app/src/lib/shared/schemas.ts
  • connection/src/index.ts

Comment on lines +573 to +574
Showing first {(widgetQuery.data.rowLimit ?? 0).toLocaleString()}{" "}
rows. Refine your query to see all results.

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Avoid misleading 0 fallback in truncation banner.

If rowLimit is unexpectedly missing, this renders “Showing first 0 rows,” which is confusing. Prefer a non-numeric fallback message (or a known default).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/components/card-container.tsx` around lines 573 - 574, The banner
currently falls back to 0 when widgetQuery.data.rowLimit is missing, producing
misleading text; update the JSX around the snippet that renders "Showing first
{(widgetQuery.data.rowLimit ?? 0).toLocaleString()} rows" to conditionally
render a non-numeric fallback (e.g., show the number only when
widgetQuery.data.rowLimit != null and otherwise render "Showing limited rows" or
"Showing first N rows" with a known default like widgetQuery.defaultRowLimit),
or use a string fallback such as (widgetQuery.data.rowLimit != null ?
widgetQuery.data.rowLimit.toLocaleString() : "limited") so the message is not
"0" when rowLimit is absent; adjust any related text in the same component
(card-container / the banner JSX) accordingly.

…erage

Three independent failures from CI on PR #507:

## 1. Unit test deep-equality break

`query-executor-core.test.ts` used `toEqual({ data: [...] })` on the
executor result. After #499 the resolved value is
`{ data, truncated, rowLimit }` (driver-reported truncation signal +
effective row cap), so deep-equal failed with "expected { data, …(2) }
to deeply equal { data }".

Fix: update the assertion to include the new fields. Added 5 new
tests to the executor suite covering:

- setStatus(COMPLETE_TRUNCATED) → result.truncated === true
- setStatus(COMPLETE) → result.truncated === false
- No setStatus call → result.truncated === false (defensive)
- credentials.maxRows → driver config.rowLimit + result.rowLimit echo
- credentials.maxRows undefined → DEFAULT_MAX_ROWS (5000) fallback

These directly cover the executor wiring that #499 added — should
also unblock SonarCloud's "16.7% Coverage on New Code" failure since
the previously-untested setStatus and maxRows paths now have unit
coverage. Suite: 14 → 19 tests, all passing.

Mocked the QueryStatus enum from @neoboard/connection so the test
doesn't need a real connector module — only the integer value of
COMPLETE_TRUNCATED (7) matters for the executor's handler.

## 2. E2E shard 2 — Create button outside viewport

The new "Max Rows per Query" field added in #499 made the connection
form's Advanced Settings section taller than the default Desktop
Chrome viewport (1280×720). The Create button got pushed below the
fold and Playwright's "scroll into view if needed" raced with Radix
Dialog's own scroll container, leaving 6 connection-advanced.spec
tests stuck at "element is outside of the viewport".

Per direction from the user, fix this at the playwright.config.ts
level by forcing a fixed, larger viewport for the entire suite.
1280×1024 fits every dialog in the suite without changing per-test
code, doesn't auto-resize during a run, and is a one-line config
change vs touching 6 individual tests.

Verified locally: connection-advanced.spec.ts — 6/6 passing in 1.1
min after the viewport bump.

## 3. SonarCloud coverage on new code

The 5 new executor tests above lift coverage on the touched
query-executor.ts paths (setStatus handler, maxRows fallback, rowLimit
echo) from 0% to ~80%, which should bring the PR over SonarCloud's
80% new-code coverage threshold.

## Verification

- `npm run test` — 132 test files / 1766 tests passing
- `npx playwright test connection-advanced.spec.ts --workers=4` —
  6/6 passing in 1.1 min
- `npx playwright test query-executor-core` — 19/19 passing in <1s
- No new lint errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (2)
app/playwright.config.ts (1)

58-61: Optional: dedupe viewport literal to avoid config drift.

Line 58–61 repeats the same viewport object used in Line 52. Consider extracting a shared constant so future edits can’t diverge.

♻️ Optional cleanup
 const serverPort = process.env.TEST_SERVER_PORT || "3100";
+const E2E_VIEWPORT = { width: 1280, height: 1024 } as const;
@@
-    viewport: { width: 1280, height: 1024 },
+    viewport: E2E_VIEWPORT,
@@
       use: {
         ...devices["Desktop Chrome"],
-        viewport: { width: 1280, height: 1024 },
+        viewport: E2E_VIEWPORT,
       },
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/playwright.config.ts` around lines 58 - 61, Extract the repeated viewport
literal into a shared constant and reference it in both places to avoid drift;
e.g., create a constant like const DEFAULT_VIEWPORT = { width: 1280, height:
1024 } near the Playwright config and replace the inline objects in the use:
{...devices["Desktop Chrome"], viewport: { width: 1280, height: 1024 }}
occurrences with viewport: DEFAULT_VIEWPORT so both the device setup and the
other use block reference the same constant (update any imports/exports if
necessary).
app/src/lib/__tests__/query/query-executor-core.test.ts (1)

129-129: Use QueryStatus enum constants instead of magic numbers.

Lines 129 and 152 use numeric literals (7 and 6) with inline comments. Replace with QueryStatus.COMPLETE_TRUNCATED and QueryStatus.COMPLETE for clarity and to prevent drift if the enum changes.

♻️ Proposed fix
-import { describe, it, expect, vi, beforeEach } from "vitest";
+import { describe, it, expect, vi, beforeEach } from "vitest";
+import { QueryStatus } from "@neoboard/connection";
...
-        cbs.setStatus?.(7); // QueryStatus.COMPLETE_TRUNCATED
+        cbs.setStatus?.(QueryStatus.COMPLETE_TRUNCATED);
...
-        cbs.setStatus?.(6); // QueryStatus.COMPLETE — not truncated
+        cbs.setStatus?.(QueryStatus.COMPLETE);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/src/lib/__tests__/query/query-executor-core.test.ts` at line 129, Replace
the magic numeric status values used in the test with the QueryStatus enum to
avoid brittle literals: update the cbs.setStatus?.(7) call to
cbs.setStatus?.(QueryStatus.COMPLETE_TRUNCATED) and the other cbs.setStatus?.(6)
to cbs.setStatus?.(QueryStatus.COMPLETE); ensure you import or reference
QueryStatus in the test file and run the test to verify the enum names resolve
correctly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@app/playwright.config.ts`:
- Around line 58-61: Extract the repeated viewport literal into a shared
constant and reference it in both places to avoid drift; e.g., create a constant
like const DEFAULT_VIEWPORT = { width: 1280, height: 1024 } near the Playwright
config and replace the inline objects in the use: {...devices["Desktop Chrome"],
viewport: { width: 1280, height: 1024 }} occurrences with viewport:
DEFAULT_VIEWPORT so both the device setup and the other use block reference the
same constant (update any imports/exports if necessary).

In `@app/src/lib/__tests__/query/query-executor-core.test.ts`:
- Line 129: Replace the magic numeric status values used in the test with the
QueryStatus enum to avoid brittle literals: update the cbs.setStatus?.(7) call
to cbs.setStatus?.(QueryStatus.COMPLETE_TRUNCATED) and the other
cbs.setStatus?.(6) to cbs.setStatus?.(QueryStatus.COMPLETE); ensure you import
or reference QueryStatus in the test file and run the test to verify the enum
names resolve correctly.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0259fcc5-2178-4eb4-95e8-32433ca529ce

📥 Commits

Reviewing files that changed from the base of the PR and between 976b1b3 and 7c70d3a.

📒 Files selected for processing (2)
  • app/playwright.config.ts
  • app/src/lib/__tests__/query/query-executor-core.test.ts

@sonarqubecloud

Copy link
Copy Markdown

@alfredo1996
alfredo1996 merged commit 2b18ad4 into release/1.1 Apr 11, 2026
13 checks passed
@alfredo1996
alfredo1996 deleted the fix/issue-499-row-cap-banner branch April 11, 2026 13:56
alfredo1996 pushed a commit that referenced this pull request May 10, 2026
…erage

Three independent failures from CI on PR #507:

## 1. Unit test deep-equality break

`query-executor-core.test.ts` used `toEqual({ data: [...] })` on the
executor result. After #499 the resolved value is
`{ data, truncated, rowLimit }` (driver-reported truncation signal +
effective row cap), so deep-equal failed with "expected { data, …(2) }
to deeply equal { data }".

Fix: update the assertion to include the new fields. Added 5 new
tests to the executor suite covering:

- setStatus(COMPLETE_TRUNCATED) → result.truncated === true
- setStatus(COMPLETE) → result.truncated === false
- No setStatus call → result.truncated === false (defensive)
- credentials.maxRows → driver config.rowLimit + result.rowLimit echo
- credentials.maxRows undefined → DEFAULT_MAX_ROWS (5000) fallback

These directly cover the executor wiring that #499 added — should
also unblock SonarCloud's "16.7% Coverage on New Code" failure since
the previously-untested setStatus and maxRows paths now have unit
coverage. Suite: 14 → 19 tests, all passing.

Mocked the QueryStatus enum from @neoboard/connection so the test
doesn't need a real connector module — only the integer value of
COMPLETE_TRUNCATED (7) matters for the executor's handler.

## 2. E2E shard 2 — Create button outside viewport

The new "Max Rows per Query" field added in #499 made the connection
form's Advanced Settings section taller than the default Desktop
Chrome viewport (1280×720). The Create button got pushed below the
fold and Playwright's "scroll into view if needed" raced with Radix
Dialog's own scroll container, leaving 6 connection-advanced.spec
tests stuck at "element is outside of the viewport".

Per direction from the user, fix this at the playwright.config.ts
level by forcing a fixed, larger viewport for the entire suite.
1280×1024 fits every dialog in the suite without changing per-test
code, doesn't auto-resize during a run, and is a one-line config
change vs touching 6 individual tests.

Verified locally: connection-advanced.spec.ts — 6/6 passing in 1.1
min after the viewport bump.

## 3. SonarCloud coverage on new code

The 5 new executor tests above lift coverage on the touched
query-executor.ts paths (setStatus handler, maxRows fallback, rowLimit
echo) from 0% to ~80%, which should bring the PR over SonarCloud's
80% new-code coverage threshold.

## Verification

- `npm run test` — 132 test files / 1766 tests passing
- `npx playwright test connection-advanced.spec.ts --workers=4` —
  6/6 passing in 1.1 min
- `npx playwright test query-executor-core` — 19/19 passing in <1s
- No new lint errors

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
alfredo1996 added a commit that referenced this pull request May 10, 2026
fix(query): wire driver truncation signal + user-configurable maxRows (#499)
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