@c9up/atlas 0.2.0 → 0.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -0
- package/db.darwin-arm64.node +0 -0
- package/db.darwin-x64.node +0 -0
- package/db.linux-arm64-gnu.node +0 -0
- package/db.linux-x64-gnu.node +0 -0
- package/db.win32-x64-msvc.node +0 -0
- package/dist/AtlasProvider.d.ts +60 -7
- package/dist/AtlasProvider.d.ts.map +1 -1
- package/dist/AtlasProvider.js +204 -38
- package/dist/AtlasProvider.js.map +1 -1
- package/dist/BaseEntity.d.ts +17 -0
- package/dist/BaseEntity.d.ts.map +1 -1
- package/dist/BaseEntity.js.map +1 -1
- package/dist/BaseModel.d.ts +63 -27
- package/dist/BaseModel.d.ts.map +1 -1
- package/dist/BaseModel.js +108 -51
- package/dist/BaseModel.js.map +1 -1
- package/dist/BaseRepository.d.ts +11 -1
- package/dist/BaseRepository.d.ts.map +1 -1
- package/dist/BaseRepository.js +95 -20
- package/dist/BaseRepository.js.map +1 -1
- package/dist/ConnectionManager.d.ts +106 -0
- package/dist/ConnectionManager.d.ts.map +1 -0
- package/dist/ConnectionManager.js +228 -0
- package/dist/ConnectionManager.js.map +1 -0
- package/dist/ModelQuery.d.ts +256 -43
- package/dist/ModelQuery.d.ts.map +1 -1
- package/dist/ModelQuery.js +899 -231
- package/dist/ModelQuery.js.map +1 -1
- package/dist/Transaction.d.ts +54 -1
- package/dist/Transaction.d.ts.map +1 -1
- package/dist/Transaction.js +121 -59
- package/dist/Transaction.js.map +1 -1
- package/dist/adapters/NapiDbAdapter.d.ts +11 -0
- package/dist/adapters/NapiDbAdapter.d.ts.map +1 -1
- package/dist/adapters/NapiDbAdapter.js +35 -7
- package/dist/adapters/NapiDbAdapter.js.map +1 -1
- package/dist/console/contract.d.ts +62 -0
- package/dist/console/contract.d.ts.map +1 -0
- package/dist/console/contract.js +38 -0
- package/dist/console/contract.js.map +1 -0
- package/dist/console/factoryCommands.d.ts +23 -0
- package/dist/console/factoryCommands.d.ts.map +1 -0
- package/dist/console/factoryCommands.js +62 -0
- package/dist/console/factoryCommands.js.map +1 -0
- package/dist/console/migrationCommands.d.ts +38 -8
- package/dist/console/migrationCommands.d.ts.map +1 -1
- package/dist/console/migrationCommands.js +157 -50
- package/dist/console/migrationCommands.js.map +1 -1
- package/dist/console/schemaCheckCommand.d.ts +11 -17
- package/dist/console/schemaCheckCommand.d.ts.map +1 -1
- package/dist/console/schemaCheckCommand.js +21 -15
- package/dist/console/schemaCheckCommand.js.map +1 -1
- package/dist/console/schemaDumpCommand.d.ts +30 -0
- package/dist/console/schemaDumpCommand.d.ts.map +1 -0
- package/dist/console/schemaDumpCommand.js +69 -0
- package/dist/console/schemaDumpCommand.js.map +1 -0
- package/dist/console/schemaGenerateCommand.d.ts +100 -0
- package/dist/console/schemaGenerateCommand.d.ts.map +1 -0
- package/dist/console/schemaGenerateCommand.js +246 -0
- package/dist/console/schemaGenerateCommand.js.map +1 -0
- package/dist/console/seederCommands.d.ts +46 -0
- package/dist/console/seederCommands.d.ts.map +1 -0
- package/dist/console/seederCommands.js +136 -0
- package/dist/console/seederCommands.js.map +1 -0
- package/dist/decorators/entity.d.ts +13 -2
- package/dist/decorators/entity.d.ts.map +1 -1
- package/dist/decorators/entity.js +10 -0
- package/dist/decorators/entity.js.map +1 -1
- package/dist/decorators/hooks.d.ts +3 -3
- package/dist/decorators/hooks.d.ts.map +1 -1
- package/dist/decorators/hooks.js.map +1 -1
- package/dist/events.d.ts +5 -0
- package/dist/events.d.ts.map +1 -1
- package/dist/events.js.map +1 -1
- package/dist/factories.d.ts +8 -0
- package/dist/factories.d.ts.map +1 -0
- package/dist/factories.js +8 -0
- package/dist/factories.js.map +1 -0
- package/dist/index.d.ts +11 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +12 -4
- package/dist/index.js.map +1 -1
- package/dist/lucid-schema.d.ts +15 -0
- package/dist/lucid-schema.d.ts.map +1 -0
- package/dist/lucid-schema.js +16 -0
- package/dist/lucid-schema.js.map +1 -0
- package/dist/orm.d.ts +14 -0
- package/dist/orm.d.ts.map +1 -0
- package/dist/orm.js +14 -0
- package/dist/orm.js.map +1 -0
- package/dist/query/DatabaseQueryBuilder.d.ts +557 -0
- package/dist/query/DatabaseQueryBuilder.d.ts.map +1 -0
- package/dist/query/DatabaseQueryBuilder.js +1798 -0
- package/dist/query/DatabaseQueryBuilder.js.map +1 -0
- package/dist/query/DmlBuilder.d.ts +62 -0
- package/dist/query/DmlBuilder.d.ts.map +1 -0
- package/dist/query/DmlBuilder.js +84 -0
- package/dist/query/DmlBuilder.js.map +1 -0
- package/dist/query/QueryBuilder.d.ts +5 -0
- package/dist/query/QueryBuilder.d.ts.map +1 -1
- package/dist/query/QueryBuilder.js +7 -0
- package/dist/query/QueryBuilder.js.map +1 -1
- package/dist/query/RawQueryBuilder.d.ts +47 -0
- package/dist/query/RawQueryBuilder.d.ts.map +1 -0
- package/dist/query/RawQueryBuilder.js +138 -0
- package/dist/query/RawQueryBuilder.js.map +1 -0
- package/dist/query/interpolate.d.ts +45 -0
- package/dist/query/interpolate.d.ts.map +1 -0
- package/dist/query/interpolate.js +51 -0
- package/dist/query/interpolate.js.map +1 -0
- package/dist/query/native.d.ts +8 -0
- package/dist/query/native.d.ts.map +1 -1
- package/dist/query/native.js +14 -0
- package/dist/query/native.js.map +1 -1
- package/dist/query/operators.d.ts +4 -0
- package/dist/query/operators.d.ts.map +1 -0
- package/dist/query/operators.js +27 -0
- package/dist/query/operators.js.map +1 -0
- package/dist/schema/Migration.d.ts +31 -7
- package/dist/schema/Migration.d.ts.map +1 -1
- package/dist/schema/Migration.js +34 -7
- package/dist/schema/Migration.js.map +1 -1
- package/dist/schema/MigrationRunner.d.ts +56 -6
- package/dist/schema/MigrationRunner.d.ts.map +1 -1
- package/dist/schema/MigrationRunner.js +382 -112
- package/dist/schema/MigrationRunner.js.map +1 -1
- package/dist/schema/Schema.d.ts +59 -2
- package/dist/schema/Schema.d.ts.map +1 -1
- package/dist/schema/Schema.js +143 -9
- package/dist/schema/Schema.js.map +1 -1
- package/dist/schema/SchemaDumper.d.ts +121 -0
- package/dist/schema/SchemaDumper.d.ts.map +1 -0
- package/dist/schema/SchemaDumper.js +365 -0
- package/dist/schema/SchemaDumper.js.map +1 -0
- package/dist/schema/Seeder.d.ts +13 -0
- package/dist/schema/Seeder.d.ts.map +1 -1
- package/dist/schema/Seeder.js +28 -5
- package/dist/schema/Seeder.js.map +1 -1
- package/dist/schema/TableBuilder.d.ts +27 -8
- package/dist/schema/TableBuilder.d.ts.map +1 -1
- package/dist/schema/TableBuilder.js +50 -12
- package/dist/schema/TableBuilder.js.map +1 -1
- package/dist/schema/catalog.d.ts +25 -1
- package/dist/schema/catalog.d.ts.map +1 -1
- package/dist/schema/catalog.js +69 -22
- package/dist/schema/catalog.js.map +1 -1
- package/dist/schema/introspect.d.ts +1 -1
- package/dist/schema/introspect.d.ts.map +1 -1
- package/dist/schema/introspect.js +26 -11
- package/dist/schema/introspect.js.map +1 -1
- package/dist/seeders.d.ts +7 -0
- package/dist/seeders.d.ts.map +1 -0
- package/dist/seeders.js +7 -0
- package/dist/seeders.js.map +1 -0
- package/dist/services/db.d.ts +120 -9
- package/dist/services/db.d.ts.map +1 -1
- package/dist/services/db.js +182 -26
- package/dist/services/db.js.map +1 -1
- package/dist/testing/DatabaseCleanup.d.ts +29 -6
- package/dist/testing/DatabaseCleanup.d.ts.map +1 -1
- package/dist/testing/DatabaseCleanup.js +44 -17
- package/dist/testing/DatabaseCleanup.js.map +1 -1
- package/dist/testing/DbAssertions.d.ts +48 -0
- package/dist/testing/DbAssertions.d.ts.map +1 -0
- package/dist/testing/DbAssertions.js +80 -0
- package/dist/testing/DbAssertions.js.map +1 -0
- package/dist/testing/Factory.d.ts +111 -25
- package/dist/testing/Factory.d.ts.map +1 -1
- package/dist/testing/Factory.js +331 -56
- package/dist/testing/Factory.js.map +1 -1
- package/dist/testing/TestUtils.d.ts +49 -0
- package/dist/testing/TestUtils.d.ts.map +1 -0
- package/dist/testing/TestUtils.js +79 -0
- package/dist/testing/TestUtils.js.map +1 -0
- package/dist/testing/index.d.ts +3 -1
- package/dist/testing/index.d.ts.map +1 -1
- package/dist/testing/index.js +3 -1
- package/dist/testing/index.js.map +1 -1
- package/dist/types/relations.d.ts +31 -0
- package/dist/types/relations.d.ts.map +1 -0
- package/dist/types/relations.js +20 -0
- package/dist/types/relations.js.map +1 -0
- package/index.darwin-arm64.node +0 -0
- package/index.darwin-x64.node +0 -0
- package/index.linux-arm64-gnu.node +0 -0
- package/index.linux-x64-gnu.node +0 -0
- package/index.win32-x64-msvc.node +0 -0
- package/package.json +30 -2
- package/src/AtlasProvider.ts +297 -51
- package/src/BaseEntity.ts +29 -0
- package/src/BaseModel.ts +174 -37
- package/src/BaseRepository.ts +136 -17
- package/src/ConnectionManager.ts +298 -0
- package/src/ModelQuery.ts +1309 -260
- package/src/Transaction.ts +194 -60
- package/src/adapters/NapiDbAdapter.ts +72 -14
- package/src/console/contract.ts +96 -0
- package/src/console/factoryCommands.ts +73 -0
- package/src/console/migrationCommands.ts +253 -63
- package/src/console/schemaCheckCommand.ts +26 -22
- package/src/console/schemaDumpCommand.ts +96 -0
- package/src/console/schemaGenerateCommand.ts +380 -0
- package/src/console/seederCommands.ts +174 -0
- package/src/decorators/entity.ts +18 -2
- package/src/decorators/hooks.ts +5 -3
- package/src/events.ts +5 -0
- package/src/factories.ts +10 -0
- package/src/index.ts +50 -3
- package/src/lucid-schema.ts +22 -0
- package/src/orm.ts +13 -0
- package/src/query/DatabaseQueryBuilder.ts +2724 -0
- package/src/query/DmlBuilder.ts +131 -0
- package/src/query/QueryBuilder.ts +8 -0
- package/src/query/RawQueryBuilder.ts +173 -0
- package/src/query/interpolate.ts +73 -0
- package/src/query/native.ts +29 -0
- package/src/query/operators.ts +27 -0
- package/src/schema/Migration.ts +36 -7
- package/src/schema/MigrationRunner.ts +483 -119
- package/src/schema/Schema.ts +168 -19
- package/src/schema/SchemaDumper.ts +518 -0
- package/src/schema/Seeder.ts +47 -10
- package/src/schema/TableBuilder.ts +48 -11
- package/src/schema/catalog.ts +94 -24
- package/src/schema/introspect.ts +26 -9
- package/src/seeders.ts +16 -0
- package/src/services/db.ts +343 -31
- package/src/testing/DatabaseCleanup.ts +73 -21
- package/src/testing/DbAssertions.ts +127 -0
- package/src/testing/Factory.ts +559 -79
- package/src/testing/TestUtils.ts +123 -0
- package/src/testing/index.ts +12 -1
- package/src/types/relations.ts +43 -0
|
@@ -5,20 +5,51 @@
|
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
import type { AsyncDatabaseConnection } from "../adapters/NapiDbAdapter.js";
|
|
8
|
+
import { AtlasError } from "../errors.js";
|
|
8
9
|
import { compileStatementNative } from "../query/native.js";
|
|
9
|
-
import { listUserTables,
|
|
10
|
+
import { listUserTables, runWithoutForeignKeys } from "../schema/catalog.js";
|
|
11
|
+
import type { TransactionClient } from "../Transaction.js";
|
|
12
|
+
|
|
13
|
+
/** What {@link useTransaction} hands back: the pinned trx plus its teardown. */
|
|
14
|
+
export interface TestTransaction {
|
|
15
|
+
/**
|
|
16
|
+
* The interactive transaction, pinned to ONE pooled connection. Run every
|
|
17
|
+
* query the test makes through THIS handle (`trx.query` / `trx.execute`, or
|
|
18
|
+
* `repo.useTransaction(trx)`), never the pooled `db` — that is what makes the
|
|
19
|
+
* rollback isolate the test.
|
|
20
|
+
*/
|
|
21
|
+
trx: TransactionClient;
|
|
22
|
+
/** Roll everything the test did back and return the connection to the pool. */
|
|
23
|
+
rollback: () => Promise<void>;
|
|
24
|
+
}
|
|
10
25
|
|
|
11
26
|
/**
|
|
12
|
-
*
|
|
27
|
+
* Isolate a test by running it inside a transaction that is rolled back after
|
|
28
|
+
* (Adonis Lucid parity — Lucid's tests use `db.transaction()` the same way).
|
|
29
|
+
*
|
|
30
|
+
* The earlier implementation issued `SAVEPOINT` / `ROLLBACK` through the pooled
|
|
31
|
+
* `db`, so with `poolMax > 1` the savepoint and its rollback — and the test's
|
|
32
|
+
* own queries — could each land on a DIFFERENT pooled connection, and the
|
|
33
|
+
* "isolation" silently did nothing. This pins a single interactive transaction
|
|
34
|
+
* instead: every query on the returned `trx` runs on that one connection, and
|
|
35
|
+
* `rollback()` reverts them together, correctly on any pool size.
|
|
13
36
|
*/
|
|
14
37
|
export async function useTransaction(
|
|
15
38
|
db: AsyncDatabaseConnection,
|
|
16
|
-
): Promise<
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
39
|
+
): Promise<TestTransaction> {
|
|
40
|
+
if (typeof db.transaction !== "function") {
|
|
41
|
+
throw new AtlasError(
|
|
42
|
+
"E_NO_INTERACTIVE_TRANSACTION",
|
|
43
|
+
"useTransaction() needs a connection with an interactive transaction() (a real napi connection); this adapter has none.",
|
|
44
|
+
{
|
|
45
|
+
hint: "Use createNapiConnection(), or drive rollback yourself if your adapter can't pin a connection.",
|
|
46
|
+
},
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
// Manual (callback-less) mode: a pinned TransactionClient the test drives and
|
|
50
|
+
// tears down via rollback() — no cross-pool savepoint scatter.
|
|
51
|
+
const trx = await db.transaction();
|
|
52
|
+
return { trx, rollback: () => trx.rollback() };
|
|
22
53
|
}
|
|
23
54
|
|
|
24
55
|
/**
|
|
@@ -27,23 +58,44 @@ export async function useTransaction(
|
|
|
27
58
|
* shared dialect-aware catalog helper, not a SQLite-only `sqlite_master` query.
|
|
28
59
|
*
|
|
29
60
|
* Foreign keys are suspended for the duration so the delete order doesn't
|
|
30
|
-
* matter
|
|
31
|
-
*
|
|
32
|
-
*
|
|
61
|
+
* matter. The FK toggle and every DELETE run on ONE pinned connection (via
|
|
62
|
+
* `runWithoutForeignKeys` → `runInTransaction`), so a connection pool can't
|
|
63
|
+
* scatter the connection-local `PRAGMA`/`SET` away from the deletes. The row
|
|
64
|
+
* removal goes through the Rust compiler (`DELETE` — cross-dialect, unlike
|
|
65
|
+
* `TRUNCATE` which auto-commits on MySQL).
|
|
33
66
|
*/
|
|
34
|
-
export async function truncateAll(
|
|
67
|
+
export async function truncateAll(
|
|
68
|
+
db: AsyncDatabaseConnection,
|
|
69
|
+
ignoreTables: readonly string[] = [],
|
|
70
|
+
): Promise<void> {
|
|
35
71
|
// The connection's own dialect, not the module default — correct even when
|
|
36
72
|
// an app runs several connections on different engines.
|
|
37
73
|
const dialect = db.dialect;
|
|
38
|
-
const
|
|
74
|
+
const ignore = new Set(ignoreTables);
|
|
75
|
+
const tables = (await listUserTables(db, dialect)).filter(
|
|
76
|
+
(t) => !ignore.has(t),
|
|
77
|
+
);
|
|
39
78
|
if (tables.length === 0) return;
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
79
|
+
|
|
80
|
+
// Postgres: a plain DELETE respects FKs immediately (atlas FKs aren't
|
|
81
|
+
// DEFERRABLE), so deleting a parent before its children raises 23503 — and
|
|
82
|
+
// nothing orders the tables. `TRUNCATE … CASCADE` is transactional on pg,
|
|
83
|
+
// order-independent, and needs no session-level FK toggle to leak. (Names
|
|
84
|
+
// come from the DB catalog; quote them for identifiers with embedded quotes.)
|
|
85
|
+
if (dialect === "postgres") {
|
|
86
|
+
const list = tables.map((t) => `"${t.replace(/"/g, '""')}"`).join(", ");
|
|
87
|
+
await db.execute(`TRUNCATE ${list} RESTART IDENTITY CASCADE`);
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
// MySQL/SQLite: DELETE (TRUNCATE auto-commits on MySQL, breaking test
|
|
92
|
+
// transaction isolation), with FK suspension on ONE pinned connection.
|
|
93
|
+
const statements = tables.map((name) => {
|
|
94
|
+
const compiled = compileStatementNative(
|
|
95
|
+
{ kind: "delete", table: name, wheres: [] },
|
|
96
|
+
dialect,
|
|
97
|
+
);
|
|
98
|
+
return { sql: compiled.statements[0], params: compiled.params };
|
|
48
99
|
});
|
|
100
|
+
await runWithoutForeignKeys(db, dialect, statements);
|
|
49
101
|
}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `db()` — a helix plugin (AdonisJS Lucid database-assertions parity) that
|
|
3
|
+
* injects database assertions on the test context as `ctx.db`:
|
|
4
|
+
*
|
|
5
|
+
* // tests/bootstrap.ts
|
|
6
|
+
* import { configure } from "@c9up/helix";
|
|
7
|
+
* import { db } from "@c9up/atlas/testing";
|
|
8
|
+
* await configure({ plugins: [db(connection)] });
|
|
9
|
+
*
|
|
10
|
+
* test("registers the user", async ({ db }) => {
|
|
11
|
+
* await db.assertHas("users", { email: "a@b.c" });
|
|
12
|
+
* await db.assertCount("users", 1);
|
|
13
|
+
* });
|
|
14
|
+
*
|
|
15
|
+
* Values are always bound as parameters; table/column identifiers are validated
|
|
16
|
+
* against a strict pattern (letters, digits, `_`, `.`) and quoted, so neither a
|
|
17
|
+
* value nor an identifier can inject SQL.
|
|
18
|
+
*
|
|
19
|
+
* NOTE (namespace): the AdonisJS docs expose these as `db.assert*`; the exact
|
|
20
|
+
* surface is re-verified against the Adonis docs when available. Model-based
|
|
21
|
+
* `assertModelExists`/`assertModelMissing` (entity PK metadata) are pending.
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
import type { Plugin } from "@c9up/helix";
|
|
25
|
+
|
|
26
|
+
/** The minimal connection the assertions need — a parameterized query runner. */
|
|
27
|
+
export interface DbConnectionLike {
|
|
28
|
+
query(sql: string, params?: unknown[]): Promise<Record<string, unknown>[]>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/** Reject identifiers that could break out of the quoted context. */
|
|
32
|
+
function quoteIdent(name: string): string {
|
|
33
|
+
if (!/^[A-Za-z_][A-Za-z0-9_.]*$/.test(name)) {
|
|
34
|
+
throw new Error(`db assertion: unsafe identifier ${JSON.stringify(name)}`);
|
|
35
|
+
}
|
|
36
|
+
return `"${name.replace(/\./g, '"."')}"`;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async function countRows(
|
|
40
|
+
conn: DbConnectionLike,
|
|
41
|
+
table: string,
|
|
42
|
+
payload?: Record<string, unknown>,
|
|
43
|
+
): Promise<number> {
|
|
44
|
+
let where = "";
|
|
45
|
+
let params: unknown[] = [];
|
|
46
|
+
if (payload) {
|
|
47
|
+
const keys = Object.keys(payload);
|
|
48
|
+
if (keys.length > 0) {
|
|
49
|
+
where = ` WHERE ${keys.map((k) => `${quoteIdent(k)} = ?`).join(" AND ")}`;
|
|
50
|
+
params = keys.map((k) => payload[k]);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
const rows = await conn.query(
|
|
54
|
+
`SELECT COUNT(*) AS c FROM ${quoteIdent(table)}${where}`,
|
|
55
|
+
params,
|
|
56
|
+
);
|
|
57
|
+
return Number(rows[0]?.c ?? 0);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** The `ctx.db` assertion surface (AdonisJS Lucid database-assertions parity). */
|
|
61
|
+
export interface DbAssertions {
|
|
62
|
+
/** At least one row in `table` matches every column in `payload`. */
|
|
63
|
+
assertHas(table: string, payload: Record<string, unknown>): Promise<void>;
|
|
64
|
+
/** No row in `table` matches `payload`. */
|
|
65
|
+
assertMissing(table: string, payload: Record<string, unknown>): Promise<void>;
|
|
66
|
+
/** `table` has exactly `expected` rows (optionally matching `payload`). */
|
|
67
|
+
assertCount(
|
|
68
|
+
table: string,
|
|
69
|
+
expected: number,
|
|
70
|
+
payload?: Record<string, unknown>,
|
|
71
|
+
): Promise<void>;
|
|
72
|
+
/** `table` has no rows at all. */
|
|
73
|
+
assertEmpty(table: string): Promise<void>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Build the assertion surface bound to a connection. */
|
|
77
|
+
export function createDbAssertions(conn: DbConnectionLike): DbAssertions {
|
|
78
|
+
return {
|
|
79
|
+
async assertHas(table, payload) {
|
|
80
|
+
const n = await countRows(conn, table, payload);
|
|
81
|
+
if (n === 0) {
|
|
82
|
+
throw new Error(
|
|
83
|
+
`Expected "${table}" to have a row matching ${JSON.stringify(payload)}, found none.`,
|
|
84
|
+
);
|
|
85
|
+
}
|
|
86
|
+
},
|
|
87
|
+
async assertMissing(table, payload) {
|
|
88
|
+
const n = await countRows(conn, table, payload);
|
|
89
|
+
if (n > 0) {
|
|
90
|
+
throw new Error(
|
|
91
|
+
`Expected "${table}" to have NO row matching ${JSON.stringify(payload)}, found ${n}.`,
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
async assertCount(table, expected, payload) {
|
|
96
|
+
const n = await countRows(conn, table, payload);
|
|
97
|
+
if (n !== expected) {
|
|
98
|
+
const scope = payload ? ` matching ${JSON.stringify(payload)}` : "";
|
|
99
|
+
throw new Error(
|
|
100
|
+
`Expected "${table}" to have ${expected} row(s)${scope}, found ${n}.`,
|
|
101
|
+
);
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
async assertEmpty(table) {
|
|
105
|
+
const n = await countRows(conn, table);
|
|
106
|
+
if (n > 0) {
|
|
107
|
+
throw new Error(`Expected "${table}" to be empty, found ${n} row(s).`);
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** The `db()` helix plugin — registers {@link DbAssertions} as `ctx.db`. */
|
|
114
|
+
export function db(conn: DbConnectionLike): Plugin {
|
|
115
|
+
const assertions = createDbAssertions(conn);
|
|
116
|
+
return (api) => {
|
|
117
|
+
api.context.macro("db", assertions);
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Typing side of the plugin — importing `@c9up/atlas/testing` augments the
|
|
122
|
+
// helix test context with `db` (the Japa pattern).
|
|
123
|
+
declare module "@c9up/helix" {
|
|
124
|
+
interface TestContext {
|
|
125
|
+
db: DbAssertions;
|
|
126
|
+
}
|
|
127
|
+
}
|