Skip to content

test(db): tighten includes oracle failure checks - #1717

Merged
tannerlinsley merged 2 commits into
mainfrom
codex/includes-oracle-review-fixes
Aug 11, 2026
Merged

test(db): tighten includes oracle failure checks#1717
tannerlinsley merged 2 commits into
mainfrom
codex/includes-oracle-review-fixes

Conversation

@KyleAMathews

@KyleAMathews KyleAMathews commented Aug 11, 2026

Copy link
Copy Markdown
Collaborator

Tightens the recompute oracle added in #1716 so known-bug cases accept only the intended assertion mismatch. It also restores repeated optimistic rollback coverage and fixes generator normalization; there are no runtime or API changes.

Root cause

fcTest.fails treats every thrown error as success. A setup, cleanup, runtime, or assertion error could therefore make a known-bug case look healthy.

Generated actions also had two state-machine gaps:

  • Actions converted into put operations bypassed stable-position bookkeeping, which let the known reorder bug enter the required green property.
  • The shared sync mocks cleared pending mutation state only after resolution. After a rejection, the generator had to rewrite later optimistic actions as ordinary puts, hiding repeated rollback histories.

Approach

  • Replace .fails with normal properties wrapped by expectAssertionFailure, which accepts only Vitest AssertionError mismatches.
  • Route every generated or converted put through one normalization helper.
  • Clear shared mock mutation state after either resolution or rejection while preserving the expected rejection signal.
  • Remove the post-rollback generator workaround and add a deterministic repeated-rollback regression.
  • Require at least one root in the metamorphic generator and clarify the sibling-order test name.
  • Document the correlation-key and virtual-property coverage boundaries.

Key invariants

  • A known-bug case passes only when the system result differs from the recompute oracle through an assertion mismatch.
  • A bug fix or unrelated exception fails the known-bug test.
  • Every generated put preserves an existing row's generated position, including converted actions.
  • Rejecting one optimistic mutation does not prevent a later optimistic mutation on the same source.

Non-goals

This small follow-up does not fix the include bugs represented by the known-failure cases or broaden the structural oracle. RFC #1658 now assigns these cases to the generalized trace-oracle follow-up:

The explicit depth-specific query shapes also remain in place so TypeScript checks the real public DSL at depths 1–4.

Trade-offs

The review suggested asserting each exact current wrong output. Matching the assertion type keeps the correct recompute result as the sole semantic reference instead of encoding implementation-specific wrong states.

Required green histories still keep correlation keys and existing-row positions stable. Reparenting and reordering include known-broken classes with deterministic failing cases; enabling the whole transition domain now would make required random CI fail until the runtime fixes land. The generalized runner will add those domains as a separate, controlled track.

Verification

cd packages/db
pnpm exec prettier --check tests/utils.ts tests/query/includes-oracle.property.test.ts
pnpm exec eslint tests/utils.ts tests/query/includes-oracle.property.test.ts
pnpm exec vitest --run tests/query/includes-oracle.property.test.ts tests/collection.test.ts -t "supports repeated optimistic rollbacks|open sync transaction isn't applied"
pnpm exec vitest --run --pool-options.threads.maxThreads=2

Red/green checks:

  • An unrelated injected error passed under .fails; it fails under expectAssertionFailure.
  • Repeated optimistic rollback failed while the mock retained its rejected promise; the regression passes after symmetric cleanup.
  • Original PR CI found seed 1665011958, where a converted put changed an existing position; normalization fixes it.
  • A depth-4 coverage probe shrank immediately to a root-only history, confirming the deferred guaranteed-depth work.
  • Full-row replacement, child reparenting, intermediate rekeying, a multi-child sync batch, and the pre-resolution confirmation checkpoint all passed as focused probes. They exposed no additional runtime bug.

Full local DB suite: 106 files passed, 2,493 tests passed, 5 skipped, no type errors. All required checks on this PR are green.

Files changed

  • packages/db/tests/query/includes-oracle.property.test.ts — narrows expected failures, normalizes converted puts, restores repeated rollback coverage, improves generator signal, and documents scope.
  • packages/db/tests/utils.ts — clears mock mutation state after both resolve and reject.

Related: #1716, #1658, #1495.

@coderabbitai

coderabbitai Bot commented Aug 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The includes oracle now uses shared put normalization and explicit AssertionError expectations for known deterministic failures. Repeated rollback coverage and root-row constraints were added. Sync test helpers now clear pending state after fulfillment or rejection.

Changes

Oracle and sync test updates

Layer / File(s) Summary
Centralize put normalization
packages/db/tests/query/includes-oracle.property.test.ts
normalizePut preserves action positions and normalizes applicable puts. Oracle comparisons exclude collection delivery metadata. Generated root rows must contain at least one item.
Assert known oracle failures
packages/db/tests/query/includes-oracle.property.test.ts
expectAssertionFailure validates rejected AssertionError results. Deterministic known-failure properties retain their seeds and scenarios. Repeated optimistic rollback coverage was added.
Clear sync state on rejection
packages/db/tests/utils.ts
Both sync helpers use finally to clear pending promise and resolver state after fulfillment or rejection.

Estimated code review effort: 2 (Simple) | ~10 minutes

Possibly related PRs

  • TanStack/db#1716: Extends the same includes recompute-oracle tests and related test helpers.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: tightening database include-oracle failure checks.
Description check ✅ Passed The description thoroughly explains the changes, motivation, scope, trade-offs, and verification, although it omits the template headings and checklist boxes.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch codex/includes-oracle-review-fixes

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.

@pkg-pr-new

pkg-pr-new Bot commented Aug 11, 2026

Copy link
Copy Markdown
More templates

@tanstack/angular-db

npm i https://pkg.pr.new/@tanstack/angular-db@1717

@tanstack/browser-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/browser-db-sqlite-persistence@1717

@tanstack/capacitor-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/capacitor-db-sqlite-persistence@1717

@tanstack/cloudflare-durable-objects-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/cloudflare-durable-objects-db-sqlite-persistence@1717

@tanstack/db

npm i https://pkg.pr.new/@tanstack/db@1717

@tanstack/db-ivm

npm i https://pkg.pr.new/@tanstack/db-ivm@1717

@tanstack/db-sqlite-persistence-core

npm i https://pkg.pr.new/@tanstack/db-sqlite-persistence-core@1717

@tanstack/electric-db-collection

npm i https://pkg.pr.new/@tanstack/electric-db-collection@1717

@tanstack/electron-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/electron-db-sqlite-persistence@1717

@tanstack/expo-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/expo-db-sqlite-persistence@1717

@tanstack/node-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/node-db-sqlite-persistence@1717

@tanstack/offline-transactions

npm i https://pkg.pr.new/@tanstack/offline-transactions@1717

@tanstack/powersync-db-collection

npm i https://pkg.pr.new/@tanstack/powersync-db-collection@1717

@tanstack/query-db-collection

npm i https://pkg.pr.new/@tanstack/query-db-collection@1717

@tanstack/react-db

npm i https://pkg.pr.new/@tanstack/react-db@1717

@tanstack/react-native-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/react-native-db-sqlite-persistence@1717

@tanstack/rxdb-db-collection

npm i https://pkg.pr.new/@tanstack/rxdb-db-collection@1717

@tanstack/solid-db

npm i https://pkg.pr.new/@tanstack/solid-db@1717

@tanstack/svelte-db

npm i https://pkg.pr.new/@tanstack/svelte-db@1717

@tanstack/tauri-db-sqlite-persistence

npm i https://pkg.pr.new/@tanstack/tauri-db-sqlite-persistence@1717

@tanstack/trailbase-db-collection

npm i https://pkg.pr.new/@tanstack/trailbase-db-collection@1717

@tanstack/vue-db

npm i https://pkg.pr.new/@tanstack/vue-db@1717

commit: 7f72615

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 125 kB

ℹ️ View Unchanged
Filename Size
packages/db/dist/esm/collection/change-events.js 1.43 kB
packages/db/dist/esm/collection/changes.js 1.38 kB
packages/db/dist/esm/collection/cleanup-queue.js 810 B
packages/db/dist/esm/collection/events.js 434 B
packages/db/dist/esm/collection/index.js 3.62 kB
packages/db/dist/esm/collection/indexes.js 1.99 kB
packages/db/dist/esm/collection/lifecycle.js 1.69 kB
packages/db/dist/esm/collection/mutations.js 2.47 kB
packages/db/dist/esm/collection/state.js 5.48 kB
packages/db/dist/esm/collection/subscription.js 3.74 kB
packages/db/dist/esm/collection/sync.js 2.88 kB
packages/db/dist/esm/collection/transaction-metadata.js 144 B
packages/db/dist/esm/deferred.js 207 B
packages/db/dist/esm/errors.js 5.1 kB
packages/db/dist/esm/event-emitter.js 748 B
packages/db/dist/esm/index.js 3.16 kB
packages/db/dist/esm/indexes/auto-index.js 829 B
packages/db/dist/esm/indexes/base-index.js 784 B
packages/db/dist/esm/indexes/basic-index.js 2.17 kB
packages/db/dist/esm/indexes/btree-index.js 2.29 kB
packages/db/dist/esm/indexes/index-registry.js 820 B
packages/db/dist/esm/indexes/reverse-index.js 557 B
packages/db/dist/esm/live-query-adapter.js 318 B
packages/db/dist/esm/local-only.js 916 B
packages/db/dist/esm/local-storage.js 2.12 kB
packages/db/dist/esm/optimistic-action.js 359 B
packages/db/dist/esm/paced-mutations.js 496 B
packages/db/dist/esm/proxy.js 3.75 kB
packages/db/dist/esm/query/builder/functions.js 1.47 kB
packages/db/dist/esm/query/builder/index.js 5.84 kB
packages/db/dist/esm/query/builder/ref-proxy.js 1.24 kB
packages/db/dist/esm/query/compiler/evaluators.js 1.89 kB
packages/db/dist/esm/query/compiler/expressions.js 430 B
packages/db/dist/esm/query/compiler/group-by.js 3.56 kB
packages/db/dist/esm/query/compiler/index.js 6.67 kB
packages/db/dist/esm/query/compiler/joins.js 2.5 kB
packages/db/dist/esm/query/compiler/lazy-targets.js 923 B
packages/db/dist/esm/query/compiler/order-by.js 1.74 kB
packages/db/dist/esm/query/compiler/select.js 1.53 kB
packages/db/dist/esm/query/effect.js 4.77 kB
packages/db/dist/esm/query/expression-helpers.js 1.43 kB
packages/db/dist/esm/query/ir.js 1.25 kB
packages/db/dist/esm/query/live-query-collection.js 360 B
packages/db/dist/esm/query/live/collection-config-builder.js 9.1 kB
packages/db/dist/esm/query/live/collection-registry.js 264 B
packages/db/dist/esm/query/live/collection-subscriber.js 1.93 kB
packages/db/dist/esm/query/live/internal.js 145 B
packages/db/dist/esm/query/live/utils.js 1.81 kB
packages/db/dist/esm/query/optimizer.js 2.92 kB
packages/db/dist/esm/query/predicate-utils.js 2.97 kB
packages/db/dist/esm/query/query-once.js 359 B
packages/db/dist/esm/query/subset-dedupe.js 960 B
packages/db/dist/esm/scheduler.js 1.3 kB
packages/db/dist/esm/SortedMap.js 1.3 kB
packages/db/dist/esm/strategies/debounceStrategy.js 247 B
packages/db/dist/esm/strategies/queueStrategy.js 428 B
packages/db/dist/esm/strategies/throttleStrategy.js 246 B
packages/db/dist/esm/transactions.js 3.04 kB
packages/db/dist/esm/utils.js 927 B
packages/db/dist/esm/utils/array-utils.js 273 B
packages/db/dist/esm/utils/browser-polyfills.js 304 B
packages/db/dist/esm/utils/btree.js 5.61 kB
packages/db/dist/esm/utils/comparison.js 1.15 kB
packages/db/dist/esm/utils/cursor.js 457 B
packages/db/dist/esm/utils/index-optimization.js 2.39 kB
packages/db/dist/esm/utils/type-guards.js 157 B
packages/db/dist/esm/utils/uuid.js 449 B
packages/db/dist/esm/virtual-props.js 360 B

compressed-size-action::db-package-size

@github-actions

Copy link
Copy Markdown
Contributor

Size Change: 0 B

Total Size: 4.22 kB

ℹ️ View Unchanged
Filename Size
packages/react-db/dist/esm/index.js 249 B
packages/react-db/dist/esm/useLiveInfiniteQuery.js 1.32 kB
packages/react-db/dist/esm/useLiveQuery.js 1.33 kB
packages/react-db/dist/esm/useLiveQueryEffect.js 355 B
packages/react-db/dist/esm/useLiveSuspenseQuery.js 567 B
packages/react-db/dist/esm/usePacedMutations.js 401 B

compressed-size-action::react-db-package-size

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/db/tests/query/includes-oracle.property.test.ts (1)

133-151: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Pass normalization inputs explicitly.

normalizePut closes over action, keys, and positions inside history.map. Extract it as a small function with explicit parameters. This makes the normalization contract easier to test and makes state changes visible.

As per coding guidelines: “Prefer explicit function parameters over closures to improve testability and clarify data flow dependencies.”

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/db/tests/query/includes-oracle.property.test.ts` around lines 133 -
151, Extract normalizePut from the history.map closure and give it explicit
parameters for the current action, keys, and positions state. Update each call
site in the normalization logic, including optimistic confirmations, optimistic
rollbacks, puts, and the empty-keys fallback, to pass those values explicitly
while preserving the existing state updates and returned action.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/db/tests/query/includes-oracle.property.test.ts`:
- Around line 603-611: Update expectAssertionFailure so it only accepts the
marked chai.AssertionError produced by assertMatches, rather than any rejection
with an AssertionError name. Add a unique property to the assertMatches failure
and require that marker alongside Vitest’s chai.AssertionError; add regression
coverage for resolved promises, ordinary errors, unrelated assertions, and null
or undefined rejections.

---

Nitpick comments:
In `@packages/db/tests/query/includes-oracle.property.test.ts`:
- Around line 133-151: Extract normalizePut from the history.map closure and
give it explicit parameters for the current action, keys, and positions state.
Update each call site in the normalization logic, including optimistic
confirmations, optimistic rollbacks, puts, and the empty-keys fallback, to pass
those values explicitly while preserving the existing state updates and returned
action.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 4eb6bef5-cfb7-45bd-93e8-9d41486c8822

📥 Commits

Reviewing files that changed from the base of the PR and between 91649d7 and 1f3cac7.

📒 Files selected for processing (1)
  • packages/db/tests/query/includes-oracle.property.test.ts

Comment on lines +603 to +611
function expectAssertionFailure<TArgs extends Array<unknown>>(
assertion: (...args: TArgs) => Promise<void>,
): (...args: TArgs) => Promise<void> {
return async (...args) => {
await expect(assertion(...args)).rejects.toMatchObject({
name: `AssertionError`,
})
}
}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n -C 6 \
  'expectAssertionFailure|expectScenarioMatches|assertMatches|toEqual|cleanup' \
  packages/db/tests/query/includes-oracle.property.test.ts

fd -a -t f 'package.json|pnpm-lock.yaml|yarn.lock|package-lock.json' . \
  -x rg -n -C 3 'vitest|AssertionError' {}

Repository: TanStack/db

Length of output: 50367


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- helper and call sites ---'
sed -n '580,615p;736,770p;1160,1210p;1240,1260p;1315,1335p' \
  packages/db/tests/query/includes-oracle.property.test.ts

printf '%s\n' '--- Vitest imports and local dependency metadata ---'
rg -n -C 3 'from [`'\"'\"'vitest|from [`'\"'\"'`@vitest`|vitest' \
  packages/db/tests/query/includes-oracle.property.test.ts package.json packages/db/package.json pnpm-lock.yaml \
  | head -160

printf '%s\n' '--- installed package availability (read-only) ---'
if [ -d node_modules/vitest ]; then
  rg -n -C 4 'AssertionError|AssertionErrorConstructor|chai' node_modules/vitest node_modules/@vitest 2>/dev/null | head -200
else
  echo 'node_modules/vitest is absent'
fi

Repository: TanStack/db

Length of output: 5099


🌐 Web query:

Vitest 3.2.4 source AssertionError expect rejects toMatchObject error type

💡 Result:

In Vitest, when an assertion using expect(...).rejects.toMatchObject(...) fails, it throws an AssertionError [1][2]. This error type originates from the Chai assertion library (which Vitest uses internally) or Vitest's custom implementation designed to provide detailed failure messages and diffs [2][3][4]. When.rejects is used, Vitest tracks the promise state; if the promise resolves instead of rejecting, or if the rejection value does not match the provided object structure, an AssertionError is generated with a message detailing the expected and actual values [2][3][5]. Key technical points regarding this behavior: - AssertionError Source: Vitest uses AssertionError to signify that a specific expectation was not met [1][5]. The error object typically contains properties such as expected, actual, and showDiff, which allow the test runner to display the characteristic colored diff output in the console [2][3]. - Rejection Mismatch: When the promise rejects, Vitest compares the rejected error (or value) against the object passed to toMatchObject [2][6]. If the properties do not match as expected, Vitest constructs an AssertionError with a message formatted as "expected [actual] to match object [expected]" (or the negated version) [1][2]. - Promise Resolution Failure: If the promise resolves when it was expected to reject, Vitest throws an AssertionError with a message such as 'promise resolved "..." instead of rejecting' [2][5]. You can rely on this error type being an instance of the class AssertionError, which is common across the Vitest/Chai ecosystem for handling test failures [1][3].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- imports and package metadata ---'
rg -n -C 4 'from .*vitest|from .*`@vitest`|vitest' \
  packages/db/tests/query/includes-oracle.property.test.ts \
  package.json packages/db/package.json pnpm-lock.yaml \
  | head -220

printf '%s\n' '--- exact Vitest 3.2.4 source references ---'
for path in \
  packages/expect/src/jest-expect.ts \
  packages/expect/src/utils.ts \
  packages/utils/src/error.ts \
  packages/utils/src/index.ts
do
  url="https://raw.githubusercontent.com/vitest-dev/vitest/v3.2.4/$path"
  body="$(curl --fail --silent --show-error "$url" || true)"
  if [ -n "$body" ]; then
    printf '%s\n' "--- $path ---"
    printf '%s\n' "$body" | rg -n -C 5 'AssertionError|rejects|toMatchObject|chai' | head -180
  fi
done

printf '%s\n' '--- standalone acceptance probe for the current predicate ---'
node - <<'JS'
function currentPredicate(value) {
  return value !== null
    && typeof value === 'object'
    && value.name === 'AssertionError'
}
const values = [
  { label: 'resolved promise', value: undefined },
  { label: 'normal Error', value: new Error('runtime') },
  { label: 'unrelated named error', value: Object.assign(new Error('setup'), { name: 'AssertionError' }) },
  { label: 'plain object with name', value: { name: 'AssertionError' } },
  { label: 'null rejection', value: null },
  { label: 'undefined rejection', value: undefined },
]
for (const { label, value } of values) {
  console.log(label, currentPredicate(value))
}
JS

Repository: TanStack/db

Length of output: 17582


🌐 Web query:

site:github.com/vitest-dev/vitest/tree/v3.2.4 packages/expect AssertionError

💡 Result:

Found the requested directory:

Vitest v3.2.4 — packages/expect
Open on GitHub [1]

It contains the src directory plus README.md, package.json, rollup.config.js, and tsconfig.json. [1]


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- Vitest package exports and error class ---'
for path in \
  packages/expect/src/index.ts \
  packages/expect/package.json \
  packages/vitest/src/index.ts \
  packages/vitest/package.json \
  packages/utils/src/error.ts \
  packages/utils/src/index.ts
do
  url="https://raw.githubusercontent.com/vitest-dev/vitest/v3.2.4/$path"
  body="$(curl --fail --silent --show-error "$url")"
  printf '%s\n' "--- $path ---"
  printf '%s\n' "$body" | rg -n -C 6 'AssertionError|export|from .chai|dependencies|`@vitest/utils`' | head -220
done

printf '%s\n' '--- direct dependency declarations for Chai ---'
python3 - <<'PY'
import json
for path in ('package.json', 'packages/db/package.json'):
    with open(path) as f:
        data = json.load(f)
    deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
    print(path, 'chai' in deps, deps.get('chai'))
PY

printf '%s\n' '--- exact current predicate behavior ---'
node - <<'JS'
function currentPredicate(value) {
  return value !== null
    && typeof value === 'object'
    && value.name === 'AssertionError'
}
const values = [
  ['resolved promise sentinel', undefined],
  ['normal Error', new Error('runtime')],
  ['unrelated named error', Object.assign(new Error('setup'), { name: 'AssertionError' })],
  ['plain object with name', { name: 'AssertionError' }],
  ['null rejection', null],
  ['undefined rejection', undefined],
]
for (const [label, value] of values) {
  console.log(`${label}: ${currentPredicate(value)}`)
}
JS

Repository: TanStack/db

Length of output: 1450


🌐 Web query:

Vitest 3.2.4 AssertionError public export import from vitest or chai

💡 Result:

In Vitest 3.2.4, AssertionError is not directly exported as a public API from the vitest package. Vitest integrates Chai to provide its assertion capabilities [1][2][3]. If you are encountering an AssertionError, it is typically an instance of the class thrown by Chai when an assertion fails [4][5][6]. Because Vitest relies on Chai internally, you can access the AssertionError constructor by importing it from the chai package directly if needed for type checking or instance verification: import { AssertionError } from 'chai'; Vitest does provide the following public exports related to assertions via the vitest package [3]: 1. expect: The primary function for creating assertions [1][3]. 2. assert: The re-exported assert API from Chai [1][7][8]. 3. chai: The underlying chai object [3]. If you are trying to catch or identify assertion errors, you should use the AssertionError from the chai dependency rather than expecting it to be exported from Vitest itself [4][6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

for path in \
  packages/vitest/src/public/index.ts \
  packages/vitest/package.json \
  packages/vitest/src/integrations/chai/index.ts \
  packages/utils/src/error.ts
do
  url="https://raw.githubusercontent.com/vitest-dev/vitest/v3.2.4/$path"
  body="$(curl --fail --silent --show-error "$url" || true)"
  printf '%s\n' "--- $path ---"
  if [ -n "$body" ]; then
    printf '%s\n' "$body" | rg -n -C 8 'AssertionError|chai|export' | head -240
  else
    echo 'not found'
  fi
done

printf '%s\n' '--- direct dependency declarations ---'
python3 - <<'PY'
import json
for path in ('package.json', 'packages/db/package.json'):
    with open(path) as f:
        data = json.load(f)
    deps = {**data.get('dependencies', {}), **data.get('devDependencies', {})}
    print(path, {'vitest': deps.get('vitest'), 'chai': deps.get('chai')})
PY

printf '%s\n' '--- current predicate acceptance table ---'
node - <<'JS'
function currentPredicate(value) {
  return value !== null
    && typeof value === 'object'
    && value.name === 'AssertionError'
}
const values = [
  ['resolved promise sentinel', undefined],
  ['normal Error', new Error('runtime')],
  ['unrelated named error', Object.assign(new Error('setup'), { name: 'AssertionError' })],
  ['plain object with name', { name: 'AssertionError' }],
  ['null rejection', null],
  ['undefined rejection', undefined],
]
for (const [label, value] of values) {
  console.log(`${label}: ${currentPredicate(value)}`)
}
JS

Repository: TanStack/db

Length of output: 15186


🌐 Web query:

site:github.com/vitest-dev/vitest/blob/v3.2.4/packages/vitest/src/public/index.ts chai AssertionError

💡 Result:

In Vitest v3.2.4, packages/vitest/src/public/index.ts does not directly import or export Chai’s AssertionError. It re-exports Chai integrations here:

export { assert, chai, createExpect, expect, should } from '../integrations/chai'

See lines 102–103. (raw.githubusercontent.com)

The relevant AssertionError definition is therefore likely in Chai or Vitest’s integration/expect packages, not this file.

Citations:


Restrict expected failures to the oracle mismatch.

toMatchObject({ name: \AssertionError` })accepts any object or error with that property. This includes the setup assertion at Line 752, so the test can pass without reachingassertMatches. Mark the assertMatchesfailure with a unique property and require that marker pluschai.AssertionError, available through Vitest’s chaiexport. Add regression tests for resolved promises, normal errors, unrelated assertions, andnullorundefined` rejections.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/db/tests/query/includes-oracle.property.test.ts` around lines 603 -
611, Update expectAssertionFailure so it only accepts the marked
chai.AssertionError produced by assertMatches, rather than any rejection with an
AssertionError name. Add a unique property to the assertMatches failure and
require that marker alongside Vitest’s chai.AssertionError; add regression
coverage for resolved promises, ordinary errors, unrelated assertions, and null
or undefined rejections.

Source: Coding guidelines

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/db/tests/utils.ts (1)

241-249: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Extract the duplicated pending-sync lifecycle.

Both factories define the same pending-promise creation and cleanup logic. Extract this state machine into one focused utility. This keeps rejection handling consistent and prevents future divergence. Add explicit return types to the extracted callbacks, such as (): Promise<void> and (): void.

As per coding guidelines: Extract common logic into utility functions when identical or near-identical code blocks appear in multiple places, and prefer small, focused utility functions.

Also applies to: 341-349

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/db/tests/utils.ts` around lines 241 - 249, Extract the shared
pending-sync promise creation and cleanup state machine used by both factories
into one focused utility function, then have each factory reuse it instead of
maintaining duplicate lifecycle logic. Preserve the existing resolve, reject,
finally-cleanup, and rejection-handling behavior, and add explicit return types
to the extracted callbacks, including Promise<void> for promise-producing
callbacks and void for cleanup callbacks.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/db/tests/utils.ts`:
- Around line 241-242: Update both mockSyncCollectionOptions and
mockSyncCollectionOptionsNoInitialState in packages/db/tests/utils.ts at lines
241-242 and 341-342 to attach a rejection handler to the cleanup chain returned
by finally(), ensuring rejected pending syncs do not produce unhandled derived
rejections while preserving clearPendingSync execution.

---

Nitpick comments:
In `@packages/db/tests/utils.ts`:
- Around line 241-249: Extract the shared pending-sync promise creation and
cleanup state machine used by both factories into one focused utility function,
then have each factory reuse it instead of maintaining duplicate lifecycle
logic. Preserve the existing resolve, reject, finally-cleanup, and
rejection-handling behavior, and add explicit return types to the extracted
callbacks, including Promise<void> for promise-producing callbacks and void for
cleanup callbacks.
🪄 Autofix

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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 867f54e3-8014-42af-b048-0fd3e95cc660

📥 Commits

Reviewing files that changed from the base of the PR and between 1f3cac7 and 7f72615.

📒 Files selected for processing (2)
  • packages/db/tests/query/includes-oracle.property.test.ts
  • packages/db/tests/utils.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/db/tests/query/includes-oracle.property.test.ts

Comment on lines +241 to 242
void syncPendingPromise.finally(clearPendingSync)
return syncPendingPromise

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- target helpers ---'
sed -n '210,260p' packages/db/tests/utils.ts
sed -n '310,360p' packages/db/tests/utils.ts

printf '%s\n' '--- rejection test context ---'
sed -n '1800,1850p' packages/db/tests/collection.test.ts

printf '%s\n' '--- all relevant usages ---'
rg -n -C 3 'mockSyncCollectionOptions(NoInitialState)?|syncPendingPromise|clearPendingSync' packages/db/tests

Repository: TanStack/db

Length of output: 50368


🏁 Script executed:

#!/bin/bash
set -eu

printf '%s\n' '--- rejection helpers ---'
rg -n -C 8 'function withExpectedRejection|const withExpectedRejection|withExpectedRejection|function flushPromises|const flushPromises' packages/db/tests/utils.ts packages/db/tests/collection.test.ts

printf '%s\n' '--- existing cleanup patterns ---'
rg -n -C 3 '\.finally\(|\.catch\(\(\) *=>|then\(.*clear|void .*Promise' packages/db --glob '*.{ts,tsx,js}'

printf '%s\n' '--- Promise.finally rejection probe ---'
node - <<'JS'
'use strict'

const events = []
process.on('unhandledRejection', (reason) => {
  events.push(reason.message)
})

async function probe() {
  let reject
  const original = new Promise((_, r) => {
    reject = r
  })

  void original.finally(() => {})
  original.catch(() => {})
  reject(new Error('pending sync rejected'))

  await new Promise((resolve) => setImmediate(resolve))
  await new Promise((resolve) => setImmediate(resolve))
  console.log(JSON.stringify({ unhandledRejections: events }))
}

probe()
JS

Repository: TanStack/db

Length of output: 25351


Handle rejected promises returned by finally(). Add a rejection handler to the cleanup chain in both mockSyncCollectionOptions and mockSyncCollectionOptionsNoInitialState. Otherwise, a rejected pending sync creates an unhandled derived rejection even when the original promise is handled.

📍 Affects 1 file
  • packages/db/tests/utils.ts#L241-L242 (this comment)
  • packages/db/tests/utils.ts#L341-L342
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/db/tests/utils.ts` around lines 241 - 242, Update both
mockSyncCollectionOptions and mockSyncCollectionOptionsNoInitialState in
packages/db/tests/utils.ts at lines 241-242 and 341-342 to attach a rejection
handler to the cleanup chain returned by finally(), ensuring rejected pending
syncs do not produce unhandled derived rejections while preserving
clearPendingSync execution.

@tannerlinsley
tannerlinsley merged commit 98f3ed0 into main Aug 11, 2026
12 checks passed
@tannerlinsley
tannerlinsley deleted the codex/includes-oracle-review-fixes branch August 11, 2026 23:25
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.

Nested include flush misclassifies sync-confirmed child updates as inserts, crashing duplicate-key diagnostics

2 participants