simfinity.js / rules
Cursor rule11 starsChanged 17 days agoApache-2.0View on GitHub
Testing conventions, patterns, and requirements for when tests must be updated
- Reads credentials
We split this simfinity-testing.mdc into the rules it gives an agent
10 rules. About 0 are common, 7 are distinctive, and 3 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈7
Distinctive · estimate
This author's own choices. Worth a look.
3
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 12
Run tests: npm test (runs vitest run), npm run test:watch, npm run test:coverage
- Very specific: line 13
Run commands from the private repository root after npm ci.
- Distinctive: line 41
Verify public behavior and error cases.
- Distinctive: line 47
New public export: verify its behavior and update runtime/TypeScript consumers when the package contract changes.
- Distinctive: line 48
New function/feature: add both positive cases (happy path) and negative cases (error handling, edge cases).
- Distinctive: line 49
Changed behavior: update existing tests to reflect the new behavior.
- Distinctive: line 50
Bug fix: add a regression test that would have caught the bug.
- Distinctive: line 51
Deprecated function: keep existing tests but add a note.
- Distinctive: line 96
Verify real schema validation, execution, metadata introspection and type identity without MongoDB.
- Very specific: line 100
Run npm run lint after modifying test files to catch unused imports or style violations.
≈ “Common” is an estimate: at least 1 in 5 of the 6,350 Cursor rule files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
---
description: Testing conventions, patterns, and requirements for when tests must be updated
globs: tests/**, scripts/**, .github/**, examples/barber/**
alwaysApply: false
---
# Testing Conventions
## Framework and Setup
- **Vitest** with ES module imports: `import { describe, test, expect, beforeAll } from 'vitest';`
- Run tests: `npm test` (runs `vitest run`), `npm run test:watch`, `npm run test:coverage`
- Run commands from the private repository root after `npm ci`; all five packages are local workspaces.
- Barber applications are independent projects outside those workspaces. Root lint and Vitest discovery exclude `examples/`; install each app with its own `npm ci` and follow `.github/workflows/barber.yml`. Backend `__tests__/` suites, shared HTTP contracts, frontend checks and PostgreSQL integration scripts belong to that workflow, not the library release suite.
- In MongoDB schema/unit tests without a database, call `simfinity.preventCreatingCollection(true)` before `createSchema()` (usually in `beforeAll`). Do not apply this indiscriminately to database creation tests. On PostgreSQL the same setting forces storage validation, not an in-memory substitute.
## Test File Naming
- `tests/*.test.js` covers shared runtime/helpers, MongoDB, PostgreSQL metadata, MCP and release tooling; use the existing feature suite rather than assuming every test targets MongoDB.
- Auth tests: `tests/auth.test.js` covers the canonical `packages/core/src/auth/` implementation through the `packages/mongodb/src/auth/` compatibility facade.
- Feature-specific tests: `tests/aggregation.test.js`, `tests/scalar-naming.test.js`, etc.
## Test Structure
```javascript
describe('Module or Feature Name', () => {
beforeAll(() => {
simfinity.preventCreatingCollection(true);
});
describe('function or sub-feature', () => {
test('should describe expected behavior', () => {
// Arrange, Act, Assert
});
});
});
```
- Group by module, then by function/feature.
- Mirror the export structure: if a module exports `{ foo, bar }`, have `describe('foo', ...)` and `describe('bar', ...)`.
- Verify public behavior and error cases; use packed consumer tests to catch missing exports, declarations and dependency leaks.
## When to Update Tests
**Behavior changes in any package or release script need corresponding test coverage:**
- **New public export**: verify its behavior and update runtime/TypeScript consumers when the package contract changes.
- **New function/feature**: add both positive cases (happy path) and negative cases (error handling, edge cases).
- **Changed behavior**: update existing tests to reflect the new behavior. Add tests for any new code paths.
- **Bug fix**: add a regression test that would have caught the bug.
- **Deprecated function**: keep existing tests but add a note. Do not remove tests for deprecated functions that still exist.
Documentation edits, obsolete archive removal and behavior-preserving moves do not require artificial tests. Run the relevant existing checks and verify links/archive contents where those are the affected contracts.
## Common Test Patterns
### Testing GraphQL types without MongoDB
```javascript
const TestType = new GraphQLObjectType({
name: 'TestType',
fields: () => ({
id: { type: GraphQLID },
name: { type: GraphQLString },
}),
});
const schema = new GraphQLSchema({
query: new GraphQLObjectType({
name: 'Query',
fields: { test: { type: TestType, resolve: () => ({ id: '1', name: 'test' }) } },
}),
});
```
### Testing auth rules
```javascript
const rule = requireAuth();
expect(rule(null, {}, { user: { id: '1' } }, null)).toBe(true);
expect(() => rule(null, {}, {}, null)).toThrow(UnauthenticatedError);
```
### Testing validators
```javascript
const validation = validators.stringLength('Name', 2, 100);
await expect(validation.CREATE[0].validate('Type', 'field', 'ab', null)).resolves.not.toThrow();
await expect(validation.CREATE[0].validate('Type', 'field', 'a', null)).rejects.toThrow();
```
## Introspection Import-Order Regressions
`tests/schema-initialization.test.js` runs `tests/fixtures/schema-initialization.js` in fresh Node processes. GraphQL introspection objects are shared singletons, so a module reset inside a test runner does not reliably recreate application startup. The fixture deliberately uses top-level dynamic imports, including distinct module URLs to force repeated evaluation against the same GraphQL peer. This is a test-only import-order requirement; production imports stay at the top of the file.
Verify real schema validation, execution, metadata introspection and type identity without MongoDB. Registries are isolated per runtime; the GraphQL introspection patch is shared and still does not support tools that clone its metadata types.
## Lint After Testing
Run `npm run lint` after modifying test files to catch unused imports or style violations.
## Real database and packaging checks
- `npm run test:integration` runs only `tests/integration/`. For the full database suite use `npm test` with `SIMFINITY_MONGODB_URI` (contract replica-set database), `SIMFINITY_TEST_MONGODB_URI` (a distinct MongoDB database for top-level regressions) and `SIMFINITY_POSTGRES_URI` (disposable PostgreSQL database). Suites skip explicitly without their URI. Setup drops databases/schema objects; never use an application URI.
- Shared GraphQL fixtures live in `tests/contracts/model-fixtures.js`. Both backends execute real GraphQL scopes/hooks/nested CRUD. `query-parity.test.js` compares schemas, filters/arrays/dates, joins/counts and aggregates across both databases.
- `mongodb-integrity.test.js` exercises the opt-in integrity adapter against a real replica set, including startup audit, HTTP rejection, nested/inferred/private/dotted references, supplied-session rollback and controlled concurrent schedules across independent clients. `SIMFINITY_STANDALONE_MONGODB_URI` adds startup rejection against a disposable standalone server; CI supplies it. Do not replace lock/race tests with mocked existence checks. Each fixture uses dedicated collections/databases and no sleep-based synchronization.
- `embedded-query-parity.test.js` compares ragged nested scalar-list queries, array aggregates/sorts, direct presence markers, native/GraphQL defaults, state-name outputs, joined cardinality and historical UTC Date parameters. Run it under America/New_York as well as the database version matrix.
- `id-resolver-parity.test.js` pins entity `id` resolution on both backends, including allowlist-excluded types and the per-backend `id` of embedded copies of entity types. `embedded-update-required-parity.test.js` checks embedded-member clears on every read path in default and transactional MongoDB modes and on PostgreSQL.
- `tests/integration/mongodb.test.js` runs with `mongoose.set('strictQuery', 'throw')` (restored in `afterAll`), so Simfinity's own Mongoose queries must not use off-schema filter keys. `tests/mongodb-global-options.test.js` runs `tests/fixtures/mongodb-global-options.js` in fresh processes (`throw`, `true`, unset) to prove the facade leaves global options alone and shares the application's Mongoose instance. `tests/mongodb-ids.test.js` covers identifier casting and `NOT_VALID_ID` mapping without a database.
- `tests/mutation-transactions.test.js` (MongoDB) and `tests/sql-transactions.test.js` (SQL/PostgreSQL) stub `Math.random` in `beforeEach` so retry backoff is 0 ms, and their `afterEach` calls `vi.restoreAllMocks()` before `vi.useRealTimers()`. Under fake timers, attach the rejection expectation before `vi.runAllTimersAsync()`. Other tests that drive retryable SQL or MongoDB errors now wait real backoff time unless they stub `Math.random`. The MCP `Retry-After and jitter` tests in `tests/mcp-execution.test.js` use fake timers, stub `Math.random` and read waits as the gaps between `fetch` send times (the asctime and RFC 850 rows also switch `process.env.TZ` to two non-GMT zones and restore it in `finally`); the `timeoutMs` guard there runs on real timers, because `AbortSignal.timeout` ignores fake timers. `tests/mcp-operation-cache.test.js` counts `parse`, `validate`, `execute` and `graphql` through a file-scoped `vi.mock('graphql')` with pass-through spies.
- `tests/mongodb-custom-mutation-connection.test.js` covers which connection a custom mutation's owned session uses in off mode, with stub connections and sessions, and real `useDb()` children and grandchildren of the unopened default connection; its `afterEach` deletes the models each test compiled on the default connection, and the models of the `useDb()` descendants of the default connection it tracks with their parents (create them with its `useDb(parent, name)` helper), deepest first, removing each from its parent with `parent.removeDb(child.name)`. `tests/integration/mongodb-custom-mutation-connection.test.js` never opens the default connection except in its last test (keep that test last), sets `bufferTimeoutMS` to 1500 and restores it, and drops every `createConnection()` and `useDb()` database it opened in `afterAll`, whether or not the tests pass. `tests/integration/mongodb-custom-mutation-usedb.test.js` and `tests/integration/mongodb-custom-mutation-usedb-nested.test.js` each run `describeUseDbAudit()` from `tests/helpers/mongodb-usedb-audit.js` in a file of its own because the test opens the default connection: an application model on a `useDb()` connection of the default one, or on a `useDb()` connection of that one, keeps the session there when the app connects after the request; each suite's `afterAll` drops every database it used and disconnects.
- SQL plugin tests exercise a non-PostgreSQL recording driver, contract/capability rejection, binding, sessions and actual statement/value delegation. Golden 3.2.0 schema hashes protect physical compatibility for the default options; opt-in layouts (`refreshTriggers: 'perRoot'`, `ownerIndexes: 'minimal'`) have their own golden, `tests/fixtures/postgres-opt-in-schema.json`, and the default golden test must not change. Opt-in-only fixtures (`sqlSchemaOptInFixtures()`, such as the long-named `long-maintained-root`) keep their default hashes in that file's `baselines`; every opt-in snapshot must differ from its fixture's default, and a fixture has a snapshot exactly for each opt-in that changes its description, so regenerate the snapshots when a fixture or an opt-in changes. The recording driver does not count as supported database coverage.
- Batched collection reads (#165): `tests/collection-batching.test.js` drives core with a memory adapter whose batch reads each parent as `findChildren` does (its equivalence block is core plumbing only); `tests/postgres-collection-batching.test.js` and `tests/mongodb-collection-batching.test.js` need no database. Batching is opt-in on both backends, so a test that relies on it sets `batchedCollections: true` explicitly, and the default-off tests assert that the default and every value other than `true` define neither member and send 3.6.0's statements. The integration files compare every request with a per-parent runtime (`batchedCollections: false` or the option off). The PostgreSQL file compares every request exactly, unsorted and tied rows included, and asserts each batch's `BEGIN ISOLATION LEVEL REPEATABLE READ`, `SET LOCAL jit = off`, statement and `COMMIT` on one client; its `skewed connection statistics` block asserts from EXPLAIN that each small parent reads the connection index and that each parent of a mixed batch gets its own statement's access path, and its `JIT` block asserts plans and settings (a batch above `jit_above_cost` with a JIT block under `jit = on`, none under `SET LOCAL jit = off`, `reset_val` after release), never time, and skips only when `pg_jit_available()`, checked under `SET LOCAL jit = on`, is false. The MongoDB file keeps exact equality for total orders, and otherwise set equality for pages that hold all children and, for other pages, the same sort keys or row counts. Spying on a bundled adapter's `findChildren` turns batching off through the pairing check, so count statements or commands, or spy on `findChildrenBatch`.
- Performance regressions assert counts, statements, plans or exact payloads, never wall-clock time, and keep equivalence tests showing that results are unchanged. `tests/integration/mutation-reads.test.js` counts `adapter.getById` and asserts the in-transaction MongoDB commands (per mode; integrity mode filters the runtime's reads by projection) and PostgreSQL statements; its `'mongo'` backend awaits every model's `init()` after `createCollection()`, because a background Mongoose index build can abort a transaction and add a retry's commands and reads, so do the same in any test that pins exact MongoDB command or read sequences. `tests/integration/postgres-statements.test.js` and `postgres-initialization.test.js` use recording pools; `tests/postgres-sargable.test.js` (with `SET LOCAL enable_sort = off`) and `tests/integration/postgres-owner-indexes.test.js` (with `enable_seqscan = off`) assert plans. Tests that read `pg_stat_xact_*` diff before and after inside one transaction and never assert absolute values. `tests/integration/postgres-connection-loss.test.js` kills a pooled backend with `pg_terminate_backend` during a single-statement read, on a `pg.Pool({ max: 1 })` with the pool and client `error` listeners that `pg` requires, and starts the next read as soon as the failed one settles.
- PostgreSQL lifecycle/runtime/options suites cover rollback, retry input isolation, custom mutations, supplied sessions, concurrent state guards, embedded snapshots, explicit many-to-many, inverse scalar IDs, UUID aggregates, C collation/ICU drift and prevention of DDL.
- Database CI runs the full suite on PostgreSQL 15/MongoDB 7, PostgreSQL 16/MongoDB 8 and PostgreSQL 18/MongoDB 8. Check physical FKs, actions, ownership cascades, nullable list items, enabled FK triggers, catalog drift and transactional rollback.
- `npm run test:packages` packs the working tree with the release packer (`packRelease`, aligned versions and a Git checkout required); `npm run test:packages -- <manifest.json>` tests exactly that manifest's archives after verifying their hashes, the manifest commit (HEAD or `SIMFINITY_RELEASE_COMMIT`) and, when set, `SIMFINITY_RELEASE_MANIFEST_SHA256`. The release build uses the manifest form on the packing job's archives. It checks isolated runtime/strict TypeScript consumers: core, driver-free SQL, PostgreSQL with old/new factories, MCP without its optional SDK, MCP with its SDK, and MongoDB. It verifies dependency isolation, shared identities and legacy MongoDB deep imports against actual archives. The core consumer also compiles every public core subpath (namespace, named, default and type imports) under NodeNext, Node16, Bundler and Node10, checking each member's exact type against the root and the export names both ways. It also checks the root's runtime value exports both ways against `coreRootValues` and that the root error classes are the `auth` classes. The MongoDB consumer compiles the legacy `src/` deep imports (`legacy.ts`) under NodeNext, Node16, Bundler and Node10 with exact type equality. The core consumer also compiles `schema-plugins.ts`; the mcp consumer compiles under NodeNext, Node16, Bundler and Node10 with `barrel.ts`, and the mcp-sdk, sql and postgres consumers have `barrel.ts` (`export *` of the package with core or its `/auth` and `/plugins`, which catches a second identical `EnvelopSchemaPlugin` declaration that `Equals` cannot); the mongo consumer has `legacy-mcp-*.ts`. `tests/core-subpath-types.test.js` checks the export map, `typesVersions`, declared names and re-exported types without packing, the explicit root exports, and the MongoDB `src/` sibling declarations (each core-subpath sibling equals its shim without the `register-mongo-object-id` import, and every public shim has exactly one sibling). The archive quality check requires every `exports` `types` target to be in the archive. The SQL, PostgreSQL and MongoDB consumers also check that the `ItemValidators` type each facade re-exports equals core's.
- `tests/collection-inputs.test.js` runs `describe.each` over core with the memory adapter, the MongoDB facade with `preventCreatingCollection(true)` and PostgreSQL with a stub pool, for generated collection input names, cycles across types and `deleted: [ID]`. Its `self-referencing collections registered after a schema` block (re-registration and registration after a `createSchema()`, which must keep the 3.5.7 names) runs on core and the MongoDB facade only, because PostgreSQL rejects registration after `createSchema()`. `tests/integration/collection-inputs.test.js` skips each backend whose URI is missing, and each test builds its own schema so it fails on older versions for its own cause.
- `tests/empty-generated-inputs.test.js` runs `describe.each` over the same three backends for generated inputs that would have no fields (omitted `added`/`updated` operations, embedded fields and mutations, their exact warnings, and the non-null embedded `INVALID_MODEL` rule), plus a transactional MongoDB case and run-time checks on core; `tests/integration/empty-generated-inputs.test.js` repeats the collection flow on both backends. New test fixtures should give entity types a writable field, or they lose their add mutation and log a warning.
- `tests/field-metadata-names.test.js` runs each scenario of `tests/fixtures/field-metadata-names.js` in a fresh process, because the fallback names of the introspection metadata types are fixed per process by its first Simfinity schema. The `input-thunk-*` scenarios register a mutation input whose `fields` function calls `getInputType()` in a later schema of the process; on MongoDB they also run the mutation when `SIMFINITY_MONGODB_URI` is set.
- `tests/integration/embedded-required-reads.test.js` checks MongoDB reads of embedded objects that hold no data (update responses, by-ID and list reads, raw documents) and PostgreSQL's results, plus, without PostgreSQL, objects whose only data is a reference stored under its `connectionField` and the Mongoose-minimized empty object a create writes for a required member, and, with supplied models, that the rule runs no schema getter and counts a subdocument's `_id` as `id`, that by-ID, reference and update-response reads keep objects whose required member a getter default, a virtual or an alias supplies, as 3.5.8 rendered them, that an empty Mongoose map reads `null` on by-ID, reference, update and list reads and that a generated model with an embedded list still reads data-less objects as `null`; `objects that hold no data` in `tests/runtime.test.js` and `tests/mongodb-ids.test.js` cover the rule without a database, including shared hook-less runtimes, the selection-independent result, accessors, `rawEmbeddedValue` (getters, virtuals and aliases at any depth, maps, generated models) and errors while reading stored members.
- `tests/integration/runtime-v31.test.js` also has a MongoDB-only block with a supplied child model whose parent link is a `String` path; it covers parent IDs sent in another case, the value `onSaving` receives there and the link remediation documented in the upgrade notes. `tests/integration/validation-helpers-parity.test.js` checks `arrayLength()` item rules, impossible dates and `null` bounds on both backends.
- The package suite also checks extracted archives with the pinned Skypack checker and verifies published MongoDB files and recorded resolution aliases against `tests/fixtures/mongodb-published-paths.json`. Preserve the root type entry and those aliases when editing exports; trailing-slash directory specifiers are not supported. Never add the quality checker to runtime dependencies.
- `npm run test:security` audits the root lockfile, including development tooling. CI and release builds require a clean audit. After dependency fixes, verify both the repository lock and fresh isolated consumers; retain `tests/mongoose-security.test.js` to prevent reintroducing update-casting prototype pollution. Audit the independent docs/examples projects separately when they are in scope.
- `tests/release-packages.test.js` and `tests/publish-packages.test.js` cover version alignment, workspace locks, archive validation (tracked files only for the `pack` CLI, the manifest digest) and publication behavior, including `--execute` refusing to run without the digest and commit variables. Keep the root private and all five published package versions aligned.
- Other release-tooling suites: `tests/release-state.test.js` (tag selection, `verify-source`, the uncached npm freshness guard that fails closed on 404, and the Pages gate, with local npm and GitHub servers; its `verify-docs` CLI tests pass an explicit env and cover only the refusals before any network call), `tests/verify-published.test.js` (uncached reads, transient retries and the deadline), `tests/release-transient.test.js` (`scripts/transient.js` against real local-server failures), `tests/release-github.test.js` (`scripts/github-api.js` and `github-release.js` against local servers, CLI with an explicit env), `tests/release-cli.test.js` (every release CLI through symlinked, extensionless and relative paths, plus a ban on `process.argv[1]` outside `scripts/cli.js`; add each new release CLI to its `clis` matrix), `tests/test-packages.test.js` (both `test:packages` modes with a stub npm) and `tests/release-workflows.test.js` (all workflow structure with js-yaml and literal assertions: the pack job and digest wiring, source validation, SHA pins and exact Node/npm, Dependabot, GitHub release creation, the Pages gate, PR-only concurrency and that no called workflow gets a scope its caller lacks). Every remote `uses:` must be `owner/repo@<40-hex SHA> # vX.Y.Z` in block-style YAML.
- Development/CI use Node.js 24 for Vitest 4. The library runtime floor remains Node.js >=18.18.0.
## Barber application checks
Use `examples/barber/README.md` for the Compose and seed commands. Shared HTTP and browser tests must run against both independent backends when changing shared user flows. Preserve tests for login, role scopes, owner/client write boundaries, booking transitions, and MCP authorization. Both backends have real-database checks for transactions, derived booking/bundle/review values, and dataset load/delete behavior. PostgreSQL also checks physical FKs and frontend query selections.
These checks mutate data. Use the isolated example Compose projects or a disposable database; never target a user's existing demo or production database. Keep generated PostgreSQL SDL/SQL/FK exports in ignored `examples/barber/postgres/generated/` and keep credentials, uploads and build output out of Git. See `simfinity-barber-examples.mdc` for the example boundaries.
Where files are headingNew Cursor rule files have more rules about planning and tracking tasks, and fewer about code style.
Of the Cursor rule files we read, the 3,978 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository9 other files it gives its agents.
- AGENTS.mdAGENTS.md
- .cursor/rules/simfinity-architecture.mdcCursor rule
- .cursor/rules/simfinity-auth-module.mdcCursor rule
- .cursor/rules/simfinity-barber-examples.mdcCursor rule
- .cursor/rules/simfinity-coding-standards.mdcCursor rule
- .cursor/rules/simfinity-core-functions.mdcCursor rule
- .cursor/rules/simfinity-development-workflow.mdcCursor rule
- .cursor/rules/simfinity-documentation.mdcCursor rule
- .cursor/rules/simfinity-extensions.mdcCursor rule
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

