@stratum-hq/db-adapters 0.4.0 → 1.1.0
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 +22 -4
- package/dist/adapters/database-prisma.d.ts +4 -2
- package/dist/adapters/database-prisma.d.ts.map +1 -1
- package/dist/adapters/database-prisma.js +19 -6
- package/dist/adapters/database-prisma.js.map +1 -1
- package/dist/adapters/drizzle.d.ts +2 -2
- package/dist/adapters/drizzle.d.ts.map +1 -1
- package/dist/adapters/drizzle.js +9 -8
- package/dist/adapters/drizzle.js.map +1 -1
- package/dist/adapters/prisma.d.ts +1 -1
- package/dist/adapters/prisma.d.ts.map +1 -1
- package/dist/adapters/prisma.js +12 -5
- package/dist/adapters/prisma.js.map +1 -1
- package/dist/adapters/schema-prisma.d.ts +36 -4
- package/dist/adapters/schema-prisma.d.ts.map +1 -1
- package/dist/adapters/schema-prisma.js +55 -17
- package/dist/adapters/schema-prisma.js.map +1 -1
- package/dist/adapters/schema-raw.d.ts +14 -3
- package/dist/adapters/schema-raw.d.ts.map +1 -1
- package/dist/adapters/schema-raw.js +9 -5
- package/dist/adapters/schema-raw.js.map +1 -1
- package/dist/database/manager.d.ts.map +1 -1
- package/dist/database/manager.js +7 -1
- package/dist/database/manager.js.map +1 -1
- package/dist/database/pool-manager.d.ts.map +1 -1
- package/dist/database/pool-manager.js +2 -3
- package/dist/database/pool-manager.js.map +1 -1
- package/dist/index.d.ts +5 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -6
- package/dist/index.js.map +1 -1
- package/dist/schema/manager.d.ts +1 -0
- package/dist/schema/manager.d.ts.map +1 -1
- package/dist/schema/manager.js +11 -2
- package/dist/schema/manager.js.map +1 -1
- package/dist/schema/session.d.ts +10 -1
- package/dist/schema/session.d.ts.map +1 -1
- package/dist/schema/session.js +24 -4
- package/dist/schema/session.js.map +1 -1
- package/package.json +10 -2
- package/dist/__tests__/base-adapter.test.d.ts +0 -2
- package/dist/__tests__/base-adapter.test.d.ts.map +0 -1
- package/dist/__tests__/base-adapter.test.js +0 -194
- package/dist/__tests__/base-adapter.test.js.map +0 -1
- package/dist/__tests__/database-manager.test.d.ts +0 -2
- package/dist/__tests__/database-manager.test.d.ts.map +0 -1
- package/dist/__tests__/database-manager.test.js +0 -110
- package/dist/__tests__/database-manager.test.js.map +0 -1
- package/dist/__tests__/drizzle.test.d.ts +0 -2
- package/dist/__tests__/drizzle.test.d.ts.map +0 -1
- package/dist/__tests__/drizzle.test.js +0 -177
- package/dist/__tests__/drizzle.test.js.map +0 -1
- package/dist/__tests__/pool-manager.test.d.ts +0 -2
- package/dist/__tests__/pool-manager.test.d.ts.map +0 -1
- package/dist/__tests__/pool-manager.test.js +0 -171
- package/dist/__tests__/pool-manager.test.js.map +0 -1
- package/dist/__tests__/rls-manager.test.d.ts +0 -2
- package/dist/__tests__/rls-manager.test.d.ts.map +0 -1
- package/dist/__tests__/rls-manager.test.js +0 -180
- package/dist/__tests__/rls-manager.test.js.map +0 -1
- package/dist/__tests__/rls-session.test.d.ts +0 -2
- package/dist/__tests__/rls-session.test.d.ts.map +0 -1
- package/dist/__tests__/rls-session.test.js +0 -97
- package/dist/__tests__/rls-session.test.js.map +0 -1
- package/dist/__tests__/schema-manager.test.d.ts +0 -2
- package/dist/__tests__/schema-manager.test.d.ts.map +0 -1
- package/dist/__tests__/schema-manager.test.js +0 -116
- package/dist/__tests__/schema-manager.test.js.map +0 -1
- package/dist/__tests__/schema-session.test.d.ts +0 -2
- package/dist/__tests__/schema-session.test.d.ts.map +0 -1
- package/dist/__tests__/schema-session.test.js +0 -23
- package/dist/__tests__/schema-session.test.js.map +0 -1
- package/dist/__tests__/sequelize.test.d.ts +0 -2
- package/dist/__tests__/sequelize.test.d.ts.map +0 -1
- package/dist/__tests__/sequelize.test.js +0 -150
- package/dist/__tests__/sequelize.test.js.map +0 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
export { BaseAdapter } from "./base-adapter.js";
|
|
2
2
|
export { RawAdapter, createTenantPool } from "./adapters/raw.js";
|
|
3
|
-
export { PrismaAdapter, withTenant } from "./adapters/prisma.js";
|
|
3
|
+
export { PrismaAdapter, withTenant as prismaWithTenant } from "./adapters/prisma.js";
|
|
4
4
|
export { SchemaRawAdapter, createSchemaTenantPool, } from "./adapters/schema-raw.js";
|
|
5
|
+
export type { SchemaAdapterOptions } from "./adapters/schema-raw.js";
|
|
5
6
|
export { SchemaPrismaAdapter, withSchemaTenant, } from "./adapters/schema-prisma.js";
|
|
6
7
|
export { createPolicy, dropPolicy, enableRLS, disableRLS, isRLSEnabled, } from "./rls/manager.js";
|
|
7
8
|
export { setTenantContext, resetTenantContext, withTenantContext, withRlsBypass, getCurrentTenantId, } from "./rls/session.js";
|
|
8
9
|
export { createSchema, dropSchema, schemaExists, listTenantSchemas, replicateTableToSchema, tenantSchemaName, } from "./schema/manager.js";
|
|
9
10
|
export { setSchemaSearchPath, resetSearchPath, getCurrentSearchPath, } from "./schema/session.js";
|
|
10
|
-
export { addTenantColumn, enableRLS as
|
|
11
|
+
export { addTenantColumn, enableRLS as enableRLSForMigration, createIsolationPolicy, migrateTable, } from "./migration-helpers.js";
|
|
11
12
|
export { getDatabaseName, databaseExists, createDatabase, dropDatabase, } from "./database/manager.js";
|
|
12
13
|
export { DatabasePoolManager } from "./database/pool-manager.js";
|
|
13
14
|
export type { DatabasePoolManagerOptions } from "./database/pool-manager.js";
|
|
14
15
|
export { DatabaseRawAdapter } from "./adapters/database-raw.js";
|
|
15
16
|
export { DatabasePrismaAdapter } from "./adapters/database-prisma.js";
|
|
16
|
-
export { SequelizeAdapter, withTenantScope } from "./adapters/sequelize.js";
|
|
17
|
-
export { DrizzleAdapter, withTenant as
|
|
17
|
+
export { SequelizeAdapter, withTenantScope as sequelizeWithTenantScope } from "./adapters/sequelize.js";
|
|
18
|
+
export { DrizzleAdapter, withTenant as drizzleWithTenant, withTenantScope as drizzleWithTenantScope, } from "./adapters/drizzle.js";
|
|
18
19
|
export type { DrizzleLike } from "./adapters/drizzle.js";
|
|
19
20
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAEhD,OAAO,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AACjE,OAAO,EAAE,aAAa,EAAE,UAAU,IAAI,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACrF,OAAO,EACL,gBAAgB,EAChB,sBAAsB,GACvB,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,6BAA6B,CAAC;AAErC,OAAO,EACL,YAAY,EACZ,UAAU,EACV,SAAS,EACT,UAAU,EACV,YAAY,GACb,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,gBAAgB,EAChB,kBAAkB,EAClB,iBAAiB,EACjB,aAAa,EACb,kBAAkB,GACnB,MAAM,kBAAkB,CAAC;AAE1B,OAAO,EACL,YAAY,EACZ,UAAU,EACV,YAAY,EACZ,iBAAiB,EACjB,sBAAsB,EACtB,gBAAgB,GACjB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,mBAAmB,EACnB,eAAe,EACf,oBAAoB,GACrB,MAAM,qBAAqB,CAAC;AAE7B,OAAO,EACL,eAAe,EACf,SAAS,IAAI,qBAAqB,EAClC,qBAAqB,EACrB,YAAY,GACb,MAAM,wBAAwB,CAAC;AAGhC,OAAO,EACL,eAAe,EACf,cAAc,EACd,cAAc,EACd,YAAY,GACb,MAAM,uBAAuB,CAAC;AAE/B,OAAO,EAAE,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AACjE,YAAY,EAAE,0BAA0B,EAAE,MAAM,4BAA4B,CAAC;AAE7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AACtE,OAAO,EAAE,gBAAgB,EAAE,eAAe,IAAI,wBAAwB,EAAE,MAAM,yBAAyB,CAAC;AACxG,OAAO,EACL,cAAc,EACd,UAAU,IAAI,iBAAiB,EAC/B,eAAe,IAAI,sBAAsB,GAC1C,MAAM,uBAAuB,CAAC;AAC/B,YAAY,EAAE,WAAW,EAAE,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
3
|
+
exports.drizzleWithTenantScope = exports.drizzleWithTenant = exports.DrizzleAdapter = exports.sequelizeWithTenantScope = exports.SequelizeAdapter = exports.DatabasePrismaAdapter = exports.DatabaseRawAdapter = exports.DatabasePoolManager = exports.dropDatabase = exports.createDatabase = exports.databaseExists = exports.getDatabaseName = exports.migrateTable = exports.createIsolationPolicy = exports.enableRLSForMigration = exports.addTenantColumn = exports.getCurrentSearchPath = exports.resetSearchPath = exports.setSchemaSearchPath = exports.tenantSchemaName = exports.replicateTableToSchema = exports.listTenantSchemas = exports.schemaExists = exports.dropSchema = exports.createSchema = exports.getCurrentTenantId = exports.withRlsBypass = exports.withTenantContext = exports.resetTenantContext = exports.setTenantContext = exports.isRLSEnabled = exports.disableRLS = exports.enableRLS = exports.dropPolicy = exports.createPolicy = exports.withSchemaTenant = exports.SchemaPrismaAdapter = exports.createSchemaTenantPool = exports.SchemaRawAdapter = exports.prismaWithTenant = exports.PrismaAdapter = exports.createTenantPool = exports.RawAdapter = exports.BaseAdapter = void 0;
|
|
4
4
|
var base_adapter_js_1 = require("./base-adapter.js");
|
|
5
5
|
Object.defineProperty(exports, "BaseAdapter", { enumerable: true, get: function () { return base_adapter_js_1.BaseAdapter; } });
|
|
6
6
|
var raw_js_1 = require("./adapters/raw.js");
|
|
@@ -8,7 +8,7 @@ Object.defineProperty(exports, "RawAdapter", { enumerable: true, get: function (
|
|
|
8
8
|
Object.defineProperty(exports, "createTenantPool", { enumerable: true, get: function () { return raw_js_1.createTenantPool; } });
|
|
9
9
|
var prisma_js_1 = require("./adapters/prisma.js");
|
|
10
10
|
Object.defineProperty(exports, "PrismaAdapter", { enumerable: true, get: function () { return prisma_js_1.PrismaAdapter; } });
|
|
11
|
-
Object.defineProperty(exports, "
|
|
11
|
+
Object.defineProperty(exports, "prismaWithTenant", { enumerable: true, get: function () { return prisma_js_1.withTenant; } });
|
|
12
12
|
var schema_raw_js_1 = require("./adapters/schema-raw.js");
|
|
13
13
|
Object.defineProperty(exports, "SchemaRawAdapter", { enumerable: true, get: function () { return schema_raw_js_1.SchemaRawAdapter; } });
|
|
14
14
|
Object.defineProperty(exports, "createSchemaTenantPool", { enumerable: true, get: function () { return schema_raw_js_1.createSchemaTenantPool; } });
|
|
@@ -40,7 +40,7 @@ Object.defineProperty(exports, "resetSearchPath", { enumerable: true, get: funct
|
|
|
40
40
|
Object.defineProperty(exports, "getCurrentSearchPath", { enumerable: true, get: function () { return session_js_2.getCurrentSearchPath; } });
|
|
41
41
|
var migration_helpers_js_1 = require("./migration-helpers.js");
|
|
42
42
|
Object.defineProperty(exports, "addTenantColumn", { enumerable: true, get: function () { return migration_helpers_js_1.addTenantColumn; } });
|
|
43
|
-
Object.defineProperty(exports, "
|
|
43
|
+
Object.defineProperty(exports, "enableRLSForMigration", { enumerable: true, get: function () { return migration_helpers_js_1.enableRLS; } });
|
|
44
44
|
Object.defineProperty(exports, "createIsolationPolicy", { enumerable: true, get: function () { return migration_helpers_js_1.createIsolationPolicy; } });
|
|
45
45
|
Object.defineProperty(exports, "migrateTable", { enumerable: true, get: function () { return migration_helpers_js_1.migrateTable; } });
|
|
46
46
|
// DB_PER_TENANT isolation
|
|
@@ -57,9 +57,9 @@ var database_prisma_js_1 = require("./adapters/database-prisma.js");
|
|
|
57
57
|
Object.defineProperty(exports, "DatabasePrismaAdapter", { enumerable: true, get: function () { return database_prisma_js_1.DatabasePrismaAdapter; } });
|
|
58
58
|
var sequelize_js_1 = require("./adapters/sequelize.js");
|
|
59
59
|
Object.defineProperty(exports, "SequelizeAdapter", { enumerable: true, get: function () { return sequelize_js_1.SequelizeAdapter; } });
|
|
60
|
-
Object.defineProperty(exports, "
|
|
60
|
+
Object.defineProperty(exports, "sequelizeWithTenantScope", { enumerable: true, get: function () { return sequelize_js_1.withTenantScope; } });
|
|
61
61
|
var drizzle_js_1 = require("./adapters/drizzle.js");
|
|
62
62
|
Object.defineProperty(exports, "DrizzleAdapter", { enumerable: true, get: function () { return drizzle_js_1.DrizzleAdapter; } });
|
|
63
|
-
Object.defineProperty(exports, "
|
|
64
|
-
Object.defineProperty(exports, "
|
|
63
|
+
Object.defineProperty(exports, "drizzleWithTenant", { enumerable: true, get: function () { return drizzle_js_1.withTenant; } });
|
|
64
|
+
Object.defineProperty(exports, "drizzleWithTenantScope", { enumerable: true, get: function () { return drizzle_js_1.withTenantScope; } });
|
|
65
65
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAgD;AAAvC,8GAAA,WAAW,OAAA;AAEpB,4CAAiE;AAAxD,oGAAA,UAAU,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACrC,
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;AAAA,qDAAgD;AAAvC,8GAAA,WAAW,OAAA;AAEpB,4CAAiE;AAAxD,oGAAA,UAAU,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AACrC,kDAAqF;AAA5E,0GAAA,aAAa,OAAA;AAAE,6GAAA,UAAU,OAAoB;AACtD,0DAGkC;AAFhC,iHAAA,gBAAgB,OAAA;AAChB,uHAAA,sBAAsB,OAAA;AAGxB,gEAGqC;AAFnC,uHAAA,mBAAmB,OAAA;AACnB,oHAAA,gBAAgB,OAAA;AAGlB,+CAM0B;AALxB,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,uGAAA,SAAS,OAAA;AACT,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AAGd,+CAM0B;AALxB,8GAAA,gBAAgB,OAAA;AAChB,gHAAA,kBAAkB,OAAA;AAClB,+GAAA,iBAAiB,OAAA;AACjB,2GAAA,aAAa,OAAA;AACb,gHAAA,kBAAkB,OAAA;AAGpB,kDAO6B;AAN3B,0GAAA,YAAY,OAAA;AACZ,wGAAA,UAAU,OAAA;AACV,0GAAA,YAAY,OAAA;AACZ,+GAAA,iBAAiB,OAAA;AACjB,oHAAA,sBAAsB,OAAA;AACtB,8GAAA,gBAAgB,OAAA;AAGlB,kDAI6B;AAH3B,iHAAA,mBAAmB,OAAA;AACnB,6GAAA,eAAe,OAAA;AACf,kHAAA,oBAAoB,OAAA;AAGtB,+DAKgC;AAJ9B,uHAAA,eAAe,OAAA;AACf,6HAAA,SAAS,OAAyB;AAClC,6HAAA,qBAAqB,OAAA;AACrB,oHAAA,YAAY,OAAA;AAGd,0BAA0B;AAC1B,oDAK+B;AAJ7B,6GAAA,eAAe,OAAA;AACf,4GAAA,cAAc,OAAA;AACd,4GAAA,cAAc,OAAA;AACd,0GAAA,YAAY,OAAA;AAGd,8DAAiE;AAAxD,sHAAA,mBAAmB,OAAA;AAG5B,8DAAgE;AAAvD,qHAAA,kBAAkB,OAAA;AAC3B,oEAAsE;AAA7D,2HAAA,qBAAqB,OAAA;AAC9B,wDAAwG;AAA/F,gHAAA,gBAAgB,OAAA;AAAE,wHAAA,eAAe,OAA4B;AACtE,oDAI+B;AAH7B,4GAAA,cAAc,OAAA;AACd,+GAAA,UAAU,OAAqB;AAC/B,oHAAA,eAAe,OAA0B"}
|
package/dist/schema/manager.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import pg from "pg";
|
|
2
|
+
export declare function validateSchemaName(schemaName: string): string;
|
|
2
3
|
export declare function tenantSchemaName(tenantSlug: string): string;
|
|
3
4
|
export declare function createSchema(client: pg.PoolClient, tenantSlug: string): Promise<void>;
|
|
4
5
|
export declare function dropSchema(client: pg.PoolClient, tenantSlug: string): Promise<void>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"manager.d.ts","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAGpB,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAK7D;AAaD,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,GAAG,MAAM,CAQ3D;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,wBAAsB,UAAU,CAC9B,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAGf;AAED,wBAAsB,YAAY,CAChC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,OAAO,CAAC,CAOlB;AAED,wBAAsB,iBAAiB,CACrC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,EAAE,CAAC,CAKnB;AAED,wBAAsB,sBAAsB,CAC1C,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,SAAS,EAAE,MAAM,EACjB,UAAU,EAAE,MAAM,GACjB,OAAO,CAAC,IAAI,CAAC,CAMf"}
|
package/dist/schema/manager.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.validateSchemaName = validateSchemaName;
|
|
3
4
|
exports.tenantSchemaName = tenantSchemaName;
|
|
4
5
|
exports.createSchema = createSchema;
|
|
5
6
|
exports.dropSchema = dropSchema;
|
|
@@ -20,12 +21,20 @@ function validateTableName(tableName) {
|
|
|
20
21
|
}
|
|
21
22
|
return tableName;
|
|
22
23
|
}
|
|
24
|
+
// PostgreSQL silently truncates identifiers longer than this many bytes.
|
|
25
|
+
const MAX_IDENTIFIER_BYTES = 63;
|
|
23
26
|
function tenantSchemaName(tenantSlug) {
|
|
24
|
-
|
|
27
|
+
const schemaName = `tenant_${tenantSlug}`;
|
|
28
|
+
if (Buffer.byteLength(schemaName) > MAX_IDENTIFIER_BYTES) {
|
|
29
|
+
throw new Error(`Schema name for tenant slug "${tenantSlug}" exceeds ${MAX_IDENTIFIER_BYTES} bytes`);
|
|
30
|
+
}
|
|
31
|
+
return schemaName;
|
|
25
32
|
}
|
|
26
33
|
async function createSchema(client, tenantSlug) {
|
|
27
34
|
const schemaName = validateSchemaName(tenantSchemaName(tenantSlug));
|
|
28
|
-
|
|
35
|
+
// No IF NOT EXISTS: an existing schema may hold another tenant's data, so
|
|
36
|
+
// provisioning must fail rather than adopt it.
|
|
37
|
+
await client.query(`CREATE SCHEMA ${schemaName}`);
|
|
29
38
|
}
|
|
30
39
|
async function dropSchema(client, tenantSlug) {
|
|
31
40
|
const schemaName = validateSchemaName(tenantSchemaName(tenantSlug));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"manager.js","sourceRoot":"","sources":["../../src/schema/manager.ts"],"names":[],"mappings":";;AAGA,gDAKC;AAaD,4CAQC;AAED,oCAQC;AAED,gCAMC;AAED,oCAUC;AAED,8CAOC;AAED,wDAUC;AA9ED,yFAAyF;AACzF,SAAgB,kBAAkB,CAAC,UAAkB;IACnD,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,UAAU,CAAC,EAAE,CAAC;QACjD,MAAM,IAAI,KAAK,CAAC,wBAAwB,UAAU,EAAE,CAAC,CAAC;IACxD,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAED,wFAAwF;AACxF,SAAS,iBAAiB,CAAC,SAAiB;IAC1C,IAAI,CAAC,0BAA0B,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;QAChD,MAAM,IAAI,KAAK,CAAC,uBAAuB,SAAS,EAAE,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,yEAAyE;AACzE,MAAM,oBAAoB,GAAG,EAAE,CAAC;AAEhC,SAAgB,gBAAgB,CAAC,UAAkB;IACjD,MAAM,UAAU,GAAG,UAAU,UAAU,EAAE,CAAC;IAC1C,IAAI,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,oBAAoB,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CACb,gCAAgC,UAAU,aAAa,oBAAoB,QAAQ,CACpF,CAAC;IACJ,CAAC;IACD,OAAO,UAAU,CAAC;AACpB,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,0EAA0E;IAC1E,+CAA+C;IAC/C,MAAM,MAAM,CAAC,KAAK,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;AACpD,CAAC;AAEM,KAAK,UAAU,UAAU,CAC9B,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,kBAAkB,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC,CAAC;IACpE,MAAM,MAAM,CAAC,KAAK,CAAC,yBAAyB,UAAU,UAAU,CAAC,CAAC;AACpE,CAAC;AAEM,KAAK,UAAU,YAAY,CAChC,MAAqB,EACrB,UAAkB;IAElB,MAAM,UAAU,GAAG,gBAAgB,CAAC,UAAU,CAAC,CAAC;IAChD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,sDAAsD,EACtD,CAAC,UAAU,CAAC,CACb,CAAC;IACF,OAAO,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC;AAC7C,CAAC;AAEM,KAAK,UAAU,iBAAiB,CACrC,MAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAC5B,iFAAiF,CAClF,CAAC;IACF,OAAO,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC;AACxC,CAAC;AAEM,KAAK,UAAU,sBAAsB,CAC1C,MAAqB,EACrB,SAAiB,EACjB,UAAkB;IAElB,MAAM,SAAS,GAAG,iBAAiB,CAAC,SAAS,CAAC,CAAC;IAC/C,MAAM,UAAU,GAAG,kBAAkB,CAAC,UAAU,CAAC,CAAC;IAClD,MAAM,MAAM,CAAC,KAAK,CAChB,8BAA8B,UAAU,IAAI,SAAS,iBAAiB,SAAS,iBAAiB,CACjG,CAAC;AACJ,CAAC"}
|
package/dist/schema/session.d.ts
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
import pg from "pg";
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Builds the search_path list for a tenant: the tenant schema first, then any
|
|
4
|
+
* explicitly opted-in extra schemas (for example one holding extension
|
|
5
|
+
* functions). Every extra entry is validated as a plain identifier, the same
|
|
6
|
+
* way tenant schema names are. Note that an unqualified table missing from the
|
|
7
|
+
* tenant schema resolves in the extra schemas, so list only schemas that hold
|
|
8
|
+
* no tenant data.
|
|
9
|
+
*/
|
|
10
|
+
export declare function tenantSearchPath(schemaName: string, extraSearchPath?: string[]): string;
|
|
11
|
+
export declare function setSchemaSearchPath(client: pg.PoolClient, tenantSlug: string, extraSearchPath?: string[]): Promise<void>;
|
|
3
12
|
export declare function resetSearchPath(client: pg.PoolClient): Promise<void>;
|
|
4
13
|
export declare function getCurrentSearchPath(client: pg.PoolClient): Promise<string>;
|
|
5
14
|
//# sourceMappingURL=session.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,
|
|
1
|
+
{"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAC;AAIpB;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,UAAU,EAAE,MAAM,EAAE,eAAe,GAAE,MAAM,EAAO,GAAG,MAAM,CAE3F;AAED,wBAAsB,mBAAmB,CACvC,MAAM,EAAE,EAAE,CAAC,UAAU,EACrB,UAAU,EAAE,MAAM,EAClB,eAAe,GAAE,MAAM,EAAO,GAC7B,OAAO,CAAC,IAAI,CAAC,CAef;AAED,wBAAsB,eAAe,CAAC,MAAM,EAAE,EAAE,CAAC,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAE1E;AAED,wBAAsB,oBAAoB,CACxC,MAAM,EAAE,EAAE,CAAC,UAAU,GACpB,OAAO,CAAC,MAAM,CAAC,CAGjB"}
|
package/dist/schema/session.js
CHANGED
|
@@ -1,17 +1,37 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.tenantSearchPath = tenantSearchPath;
|
|
3
4
|
exports.setSchemaSearchPath = setSchemaSearchPath;
|
|
4
5
|
exports.resetSearchPath = resetSearchPath;
|
|
5
6
|
exports.getCurrentSearchPath = getCurrentSearchPath;
|
|
6
7
|
const core_1 = require("@stratum-hq/core");
|
|
7
8
|
const manager_js_1 = require("./manager.js");
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Builds the search_path list for a tenant: the tenant schema first, then any
|
|
11
|
+
* explicitly opted-in extra schemas (for example one holding extension
|
|
12
|
+
* functions). Every extra entry is validated as a plain identifier, the same
|
|
13
|
+
* way tenant schema names are. Note that an unqualified table missing from the
|
|
14
|
+
* tenant schema resolves in the extra schemas, so list only schemas that hold
|
|
15
|
+
* no tenant data.
|
|
16
|
+
*/
|
|
17
|
+
function tenantSearchPath(schemaName, extraSearchPath = []) {
|
|
18
|
+
return [schemaName, ...extraSearchPath.map(manager_js_1.validateSchemaName)].join(", ");
|
|
19
|
+
}
|
|
20
|
+
async function setSchemaSearchPath(client, tenantSlug, extraSearchPath = []) {
|
|
9
21
|
// The schema name is interpolated (identifiers cannot be bound), so the slug
|
|
10
22
|
// must be validated against the canonical slug charset first; validateSlug
|
|
11
23
|
// throws on anything else. SET LOCAL only takes effect for the current
|
|
12
|
-
// transaction.
|
|
13
|
-
|
|
14
|
-
|
|
24
|
+
// transaction. The path is the tenant schema alone (pg_catalog is always
|
|
25
|
+
// searched), so a table missing from it errors instead of resolving to a
|
|
26
|
+
// shared table in public. Extra schemas are appended only when opted in.
|
|
27
|
+
const searchPath = tenantSearchPath((0, manager_js_1.tenantSchemaName)((0, core_1.validateSlug)(tenantSlug)), extraSearchPath);
|
|
28
|
+
await client.query(`SET LOCAL search_path TO ${searchPath}`);
|
|
29
|
+
// Outside a transaction SET LOCAL is a no-op with only a warning. SHOW
|
|
30
|
+
// reports unquoted identifiers folded to lower case.
|
|
31
|
+
const res = await client.query("SHOW search_path");
|
|
32
|
+
if (res.rows[0].search_path !== searchPath.toLowerCase()) {
|
|
33
|
+
throw new Error("setSchemaSearchPath must be called inside a transaction");
|
|
34
|
+
}
|
|
15
35
|
}
|
|
16
36
|
async function resetSearchPath(client) {
|
|
17
37
|
await client.query("RESET search_path");
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":";;
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/schema/session.ts"],"names":[],"mappings":";;AAYA,4CAEC;AAED,kDAmBC;AAED,0CAEC;AAED,oDAKC;AA7CD,2CAAgD;AAChD,6CAAoE;AAEpE;;;;;;;GAOG;AACH,SAAgB,gBAAgB,CAAC,UAAkB,EAAE,kBAA4B,EAAE;IACjF,OAAO,CAAC,UAAU,EAAE,GAAG,eAAe,CAAC,GAAG,CAAC,+BAAkB,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC7E,CAAC;AAEM,KAAK,UAAU,mBAAmB,CACvC,MAAqB,EACrB,UAAkB,EAClB,kBAA4B,EAAE;IAE9B,6EAA6E;IAC7E,2EAA2E;IAC3E,uEAAuE;IACvE,yEAAyE;IACzE,yEAAyE;IACzE,yEAAyE;IACzE,MAAM,UAAU,GAAG,gBAAgB,CAAC,IAAA,6BAAgB,EAAC,IAAA,mBAAY,EAAC,UAAU,CAAC,CAAC,EAAE,eAAe,CAAC,CAAC;IACjG,MAAM,MAAM,CAAC,KAAK,CAAC,4BAA4B,UAAU,EAAE,CAAC,CAAC;IAC7D,uEAAuE;IACvE,qDAAqD;IACrD,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAA0B,kBAAkB,CAAC,CAAC;IAC5E,IAAI,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,KAAK,UAAU,CAAC,WAAW,EAAE,EAAE,CAAC;QACzD,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC7E,CAAC;AACH,CAAC;AAEM,KAAK,UAAU,eAAe,CAAC,MAAqB;IACzD,MAAM,MAAM,CAAC,KAAK,CAAC,mBAAmB,CAAC,CAAC;AAC1C,CAAC;AAEM,KAAK,UAAU,oBAAoB,CACxC,MAAqB;IAErB,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,KAAK,CAA0B,kBAAkB,CAAC,CAAC;IAC5E,OAAO,GAAG,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC;AACjC,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stratum-hq/db-adapters",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Stratum PostgreSQL adapters for tenant-scoped query isolation",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"multi-tenancy",
|
|
@@ -55,8 +55,16 @@
|
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@prisma/client": "^5.10.0",
|
|
57
57
|
"@types/pg": "^8.11.0",
|
|
58
|
+
"drizzle-orm": "^0.45.3",
|
|
58
59
|
"typescript": "^5.4.0",
|
|
59
|
-
"vitest": "^1.
|
|
60
|
+
"vitest": "^4.1.11"
|
|
61
|
+
},
|
|
62
|
+
"license": "MIT",
|
|
63
|
+
"author": "Christian Crank",
|
|
64
|
+
"homepage": "https://github.com/stratum-hq/Stratum/tree/main/packages/db-adapters#readme",
|
|
65
|
+
"bugs": "https://github.com/stratum-hq/Stratum/issues",
|
|
66
|
+
"engines": {
|
|
67
|
+
"node": ">=20.0.0"
|
|
60
68
|
},
|
|
61
69
|
"repository": {
|
|
62
70
|
"type": "git",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base-adapter.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/base-adapter.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,194 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const base_adapter_js_1 = require("../base-adapter.js");
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// Mock pg
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
const mockQuery = vitest_1.vi.fn();
|
|
9
|
-
const mockRelease = vitest_1.vi.fn();
|
|
10
|
-
const mockConnect = vitest_1.vi.fn();
|
|
11
|
-
// Create a mock Pool class that will be used directly
|
|
12
|
-
class MockPool {
|
|
13
|
-
connect = mockConnect;
|
|
14
|
-
}
|
|
15
|
-
vitest_1.vi.mock("pg", () => {
|
|
16
|
-
return { default: { Pool: MockPool }, Pool: MockPool };
|
|
17
|
-
});
|
|
18
|
-
// ---------------------------------------------------------------------------
|
|
19
|
-
// Concrete subclass for testing the abstract BaseAdapter
|
|
20
|
-
// ---------------------------------------------------------------------------
|
|
21
|
-
class TestAdapter extends base_adapter_js_1.BaseAdapter {
|
|
22
|
-
// Expose executeWithTenantContext publicly for testing
|
|
23
|
-
async runWithTenant(tenantId, fn) {
|
|
24
|
-
return this.executeWithTenantContext(tenantId, fn);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
// ---------------------------------------------------------------------------
|
|
28
|
-
// Helpers
|
|
29
|
-
// ---------------------------------------------------------------------------
|
|
30
|
-
function makeMockClient() {
|
|
31
|
-
const client = {
|
|
32
|
-
query: mockQuery,
|
|
33
|
-
release: mockRelease,
|
|
34
|
-
};
|
|
35
|
-
return client;
|
|
36
|
-
}
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// Tests
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
(0, vitest_1.describe)("BaseAdapter", () => {
|
|
41
|
-
let adapter;
|
|
42
|
-
(0, vitest_1.beforeEach)(() => {
|
|
43
|
-
vitest_1.vi.clearAllMocks();
|
|
44
|
-
const mockClient = makeMockClient();
|
|
45
|
-
mockConnect.mockResolvedValue(mockClient);
|
|
46
|
-
mockQuery.mockResolvedValue({ rows: [] });
|
|
47
|
-
const pool = new MockPool();
|
|
48
|
-
adapter = new TestAdapter(pool);
|
|
49
|
-
});
|
|
50
|
-
(0, vitest_1.describe)("executeWithTenantContext", () => {
|
|
51
|
-
(0, vitest_1.it)("acquires a client from the pool", async () => {
|
|
52
|
-
await adapter.runWithTenant("tenant-1", async () => "result");
|
|
53
|
-
(0, vitest_1.expect)(mockConnect).toHaveBeenCalledTimes(1);
|
|
54
|
-
});
|
|
55
|
-
(0, vitest_1.it)("begins a transaction", async () => {
|
|
56
|
-
await adapter.runWithTenant("tenant-1", async () => "result");
|
|
57
|
-
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith("BEGIN");
|
|
58
|
-
});
|
|
59
|
-
(0, vitest_1.it)("sets tenant context with correct SQL and parameters", async () => {
|
|
60
|
-
await adapter.runWithTenant("tenant-abc", async () => "ok");
|
|
61
|
-
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith("SELECT set_config('app.current_tenant_id', $1, true)", ["tenant-abc"]);
|
|
62
|
-
});
|
|
63
|
-
(0, vitest_1.it)("executes the provided query function", async () => {
|
|
64
|
-
const queryFn = vitest_1.vi.fn().mockResolvedValue("my-result");
|
|
65
|
-
await adapter.runWithTenant("tenant-1", queryFn);
|
|
66
|
-
(0, vitest_1.expect)(queryFn).toHaveBeenCalledTimes(1);
|
|
67
|
-
});
|
|
68
|
-
(0, vitest_1.it)("passes the client to the query function", async () => {
|
|
69
|
-
const queryFn = vitest_1.vi.fn().mockResolvedValue("ok");
|
|
70
|
-
await adapter.runWithTenant("tenant-1", queryFn);
|
|
71
|
-
// The queryFn receives the mock client
|
|
72
|
-
const passedClient = queryFn.mock.calls[0][0];
|
|
73
|
-
(0, vitest_1.expect)(passedClient.query).toBeDefined();
|
|
74
|
-
(0, vitest_1.expect)(passedClient.release).toBeDefined();
|
|
75
|
-
});
|
|
76
|
-
(0, vitest_1.it)("commits the transaction on success", async () => {
|
|
77
|
-
await adapter.runWithTenant("tenant-1", async () => "ok");
|
|
78
|
-
const queryCalls = mockQuery.mock.calls.map((c) => c[0]);
|
|
79
|
-
(0, vitest_1.expect)(queryCalls).toContain("COMMIT");
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.it)("returns the result from the query function", async () => {
|
|
82
|
-
const result = await adapter.runWithTenant("tenant-1", async () => ({ data: [1, 2, 3] }));
|
|
83
|
-
(0, vitest_1.expect)(result).toEqual({ data: [1, 2, 3] });
|
|
84
|
-
});
|
|
85
|
-
(0, vitest_1.it)("executes in the correct order: BEGIN, set_config, queryFn, COMMIT", async () => {
|
|
86
|
-
const callOrder = [];
|
|
87
|
-
mockQuery.mockImplementation(async (sql) => {
|
|
88
|
-
if (typeof sql === "string") {
|
|
89
|
-
if (sql === "BEGIN")
|
|
90
|
-
callOrder.push("BEGIN");
|
|
91
|
-
else if (sql.includes("set_config"))
|
|
92
|
-
callOrder.push("SET_CONFIG");
|
|
93
|
-
else if (sql === "COMMIT")
|
|
94
|
-
callOrder.push("COMMIT");
|
|
95
|
-
else if (sql === "ROLLBACK")
|
|
96
|
-
callOrder.push("ROLLBACK");
|
|
97
|
-
else if (sql === "RESET app.current_tenant_id")
|
|
98
|
-
callOrder.push("RESET");
|
|
99
|
-
}
|
|
100
|
-
return { rows: [] };
|
|
101
|
-
});
|
|
102
|
-
await adapter.runWithTenant("tenant-1", async () => {
|
|
103
|
-
callOrder.push("QUERY_FN");
|
|
104
|
-
return "done";
|
|
105
|
-
});
|
|
106
|
-
(0, vitest_1.expect)(callOrder).toEqual([
|
|
107
|
-
"BEGIN",
|
|
108
|
-
"SET_CONFIG",
|
|
109
|
-
"QUERY_FN",
|
|
110
|
-
"COMMIT",
|
|
111
|
-
"RESET",
|
|
112
|
-
]);
|
|
113
|
-
});
|
|
114
|
-
(0, vitest_1.it)("rolls back on error from query function", async () => {
|
|
115
|
-
const error = new Error("query failed");
|
|
116
|
-
await (0, vitest_1.expect)(adapter.runWithTenant("tenant-1", async () => {
|
|
117
|
-
throw error;
|
|
118
|
-
})).rejects.toThrow("query failed");
|
|
119
|
-
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith("ROLLBACK");
|
|
120
|
-
});
|
|
121
|
-
(0, vitest_1.it)("does not commit when query function throws", async () => {
|
|
122
|
-
try {
|
|
123
|
-
await adapter.runWithTenant("tenant-1", async () => {
|
|
124
|
-
throw new Error("fail");
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
catch {
|
|
128
|
-
// expected
|
|
129
|
-
}
|
|
130
|
-
const queryCalls = mockQuery.mock.calls.map((c) => c[0]);
|
|
131
|
-
(0, vitest_1.expect)(queryCalls).not.toContain("COMMIT");
|
|
132
|
-
});
|
|
133
|
-
(0, vitest_1.it)("resets tenant context in finally block on success", async () => {
|
|
134
|
-
await adapter.runWithTenant("tenant-1", async () => "ok");
|
|
135
|
-
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith("RESET app.current_tenant_id");
|
|
136
|
-
});
|
|
137
|
-
(0, vitest_1.it)("resets tenant context in finally block on error", async () => {
|
|
138
|
-
try {
|
|
139
|
-
await adapter.runWithTenant("tenant-1", async () => {
|
|
140
|
-
throw new Error("boom");
|
|
141
|
-
});
|
|
142
|
-
}
|
|
143
|
-
catch {
|
|
144
|
-
// expected
|
|
145
|
-
}
|
|
146
|
-
(0, vitest_1.expect)(mockQuery).toHaveBeenCalledWith("RESET app.current_tenant_id");
|
|
147
|
-
});
|
|
148
|
-
(0, vitest_1.it)("releases the client on success", async () => {
|
|
149
|
-
await adapter.runWithTenant("tenant-1", async () => "ok");
|
|
150
|
-
(0, vitest_1.expect)(mockRelease).toHaveBeenCalledTimes(1);
|
|
151
|
-
});
|
|
152
|
-
(0, vitest_1.it)("releases the client on error", async () => {
|
|
153
|
-
try {
|
|
154
|
-
await adapter.runWithTenant("tenant-1", async () => {
|
|
155
|
-
throw new Error("fail");
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
catch {
|
|
159
|
-
// expected
|
|
160
|
-
}
|
|
161
|
-
(0, vitest_1.expect)(mockRelease).toHaveBeenCalledTimes(1);
|
|
162
|
-
});
|
|
163
|
-
(0, vitest_1.it)("still releases client if RESET query fails", async () => {
|
|
164
|
-
let callCount = 0;
|
|
165
|
-
mockQuery.mockImplementation(async (sql) => {
|
|
166
|
-
callCount++;
|
|
167
|
-
if (typeof sql === "string" && sql.includes("RESET")) {
|
|
168
|
-
throw new Error("connection broken");
|
|
169
|
-
}
|
|
170
|
-
return { rows: [] };
|
|
171
|
-
});
|
|
172
|
-
await adapter.runWithTenant("tenant-1", async () => "ok");
|
|
173
|
-
// Client should still be released even though RESET threw
|
|
174
|
-
(0, vitest_1.expect)(mockRelease).toHaveBeenCalledTimes(1);
|
|
175
|
-
});
|
|
176
|
-
(0, vitest_1.it)("propagates the original error even if ROLLBACK fails", async () => {
|
|
177
|
-
mockQuery.mockImplementation(async (sql) => {
|
|
178
|
-
if (typeof sql === "string" && sql === "ROLLBACK") {
|
|
179
|
-
throw new Error("rollback failed");
|
|
180
|
-
}
|
|
181
|
-
return { rows: [] };
|
|
182
|
-
});
|
|
183
|
-
// The queryFn throws — we want the original error, but ROLLBACK also throws.
|
|
184
|
-
// In the current implementation, ROLLBACK failure replaces the original error
|
|
185
|
-
// because there's no catch around ROLLBACK. Let's verify behavior:
|
|
186
|
-
await (0, vitest_1.expect)(adapter.runWithTenant("tenant-1", async () => {
|
|
187
|
-
throw new Error("original error");
|
|
188
|
-
})).rejects.toThrow();
|
|
189
|
-
// Client should still be released
|
|
190
|
-
(0, vitest_1.expect)(mockRelease).toHaveBeenCalledTimes(1);
|
|
191
|
-
});
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
//# sourceMappingURL=base-adapter.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"base-adapter.test.js","sourceRoot":"","sources":["../../src/__tests__/base-adapter.test.ts"],"names":[],"mappings":";;AAAA,mCAA8D;AAC9D,wDAAiD;AAEjD,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,MAAM,SAAS,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC;AAC1B,MAAM,WAAW,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC;AAC5B,MAAM,WAAW,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC;AAE5B,sDAAsD;AACtD,MAAM,QAAQ;IACZ,OAAO,GAAG,WAAW,CAAC;CACvB;AAED,WAAE,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE;IACjB,OAAO,EAAE,OAAO,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzD,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,yDAAyD;AACzD,8EAA8E;AAE9E,MAAM,WAAY,SAAQ,6BAAW;IACnC,uDAAuD;IACvD,KAAK,CAAC,aAAa,CACjB,QAAgB,EAChB,EAAmD;QAEnD,OAAO,IAAI,CAAC,wBAAwB,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACrD,CAAC;CACF;AAED,8EAA8E;AAC9E,UAAU;AACV,8EAA8E;AAE9E,SAAS,cAAc;IACrB,MAAM,MAAM,GAAG;QACb,KAAK,EAAE,SAAS;QAChB,OAAO,EAAE,WAAW;KACrB,CAAC;IACF,OAAO,MAA4C,CAAC;AACtD,CAAC;AAED,8EAA8E;AAC9E,QAAQ;AACR,8EAA8E;AAE9E,IAAA,iBAAQ,EAAC,aAAa,EAAE,GAAG,EAAE;IAC3B,IAAI,OAAoB,CAAC;IAEzB,IAAA,mBAAU,EAAC,GAAG,EAAE;QACd,WAAE,CAAC,aAAa,EAAE,CAAC;QAEnB,MAAM,UAAU,GAAG,cAAc,EAAE,CAAC;QACpC,WAAW,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC;QAC1C,SAAS,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC,CAAC;QAE1C,MAAM,IAAI,GAAG,IAAI,QAAQ,EAAE,CAAC;QAC5B,OAAO,GAAG,IAAI,WAAW,CAAC,IAAW,CAAC,CAAC;IACzC,CAAC,CAAC,CAAC;IAEH,IAAA,iBAAQ,EAAC,0BAA0B,EAAE,GAAG,EAAE;QACxC,IAAA,WAAE,EAAC,iCAAiC,EAAE,KAAK,IAAI,EAAE;YAC/C,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC;YAE9D,IAAA,eAAM,EAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,sBAAsB,EAAE,KAAK,IAAI,EAAE;YACpC,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,QAAQ,CAAC,CAAC;YAE9D,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;QAClD,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,qDAAqD,EAAE,KAAK,IAAI,EAAE;YACnE,MAAM,OAAO,CAAC,aAAa,CAAC,YAAY,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YAE5D,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,oBAAoB,CACpC,sDAAsD,EACtD,CAAC,YAAY,CAAC,CACf,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,sCAAsC,EAAE,KAAK,IAAI,EAAE;YACpD,MAAM,OAAO,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;YAEvD,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAEjD,IAAA,eAAM,EAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC3C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,OAAO,GAAG,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,IAAI,CAAC,CAAC;YAEhD,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;YAEjD,uCAAuC;YACvC,MAAM,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAC9C,IAAA,eAAM,EAAC,YAAY,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;YACzC,IAAA,eAAM,EAAC,YAAY,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,oCAAoC,EAAE,KAAK,IAAI,EAAE;YAClD,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YAE1D,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpE,IAAA,eAAM,EAAC,UAAU,CAAC,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,aAAa,CACxC,UAAU,EACV,KAAK,IAAI,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAClC,CAAC;YAEF,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,OAAO,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC;QAC9C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,mEAAmE,EAAE,KAAK,IAAI,EAAE;YACjF,MAAM,SAAS,GAAa,EAAE,CAAC;YAE/B,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;gBACjD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;oBAC5B,IAAI,GAAG,KAAK,OAAO;wBAAE,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;yBACxC,IAAI,GAAG,CAAC,QAAQ,CAAC,YAAY,CAAC;wBAAE,SAAS,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;yBAC7D,IAAI,GAAG,KAAK,QAAQ;wBAAE,SAAS,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC;yBAC/C,IAAI,GAAG,KAAK,UAAU;wBAAE,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;yBACnD,IAAI,GAAG,KAAK,6BAA6B;wBAC5C,SAAS,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;gBAC5B,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;gBACjD,SAAS,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBAC3B,OAAO,MAAM,CAAC;YAChB,CAAC,CAAC,CAAC;YAEH,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,OAAO,CAAC;gBACxB,OAAO;gBACP,YAAY;gBACZ,UAAU;gBACV,QAAQ;gBACR,OAAO;aACR,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,KAAK,GAAG,IAAI,KAAK,CAAC,cAAc,CAAC,CAAC;YAExC,MAAM,IAAA,eAAM,EACV,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;gBAC3C,MAAM,KAAK,CAAC;YACd,CAAC,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;YAElC,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;QACrD,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;oBACjD,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC1B,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW;YACb,CAAC;YAED,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAY,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACpE,IAAA,eAAM,EAAC,UAAU,CAAC,CAAC,GAAG,CAAC,SAAS,CAAC,QAAQ,CAAC,CAAC;QAC7C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,mDAAmD,EAAE,KAAK,IAAI,EAAE;YACjE,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YAE1D,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,6BAA6B,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,iDAAiD,EAAE,KAAK,IAAI,EAAE;YAC/D,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;oBACjD,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC1B,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW;YACb,CAAC;YAED,IAAA,eAAM,EAAC,SAAS,CAAC,CAAC,oBAAoB,CAAC,6BAA6B,CAAC,CAAC;QACxE,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,gCAAgC,EAAE,KAAK,IAAI,EAAE;YAC9C,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YAE1D,IAAA,eAAM,EAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,8BAA8B,EAAE,KAAK,IAAI,EAAE;YAC5C,IAAI,CAAC;gBACH,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;oBACjD,MAAM,IAAI,KAAK,CAAC,MAAM,CAAC,CAAC;gBAC1B,CAAC,CAAC,CAAC;YACL,CAAC;YAAC,MAAM,CAAC;gBACP,WAAW;YACb,CAAC;YAED,IAAA,eAAM,EAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;YAC1D,IAAI,SAAS,GAAG,CAAC,CAAC;YAClB,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;gBACjD,SAAS,EAAE,CAAC;gBACZ,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE,CAAC;oBACrD,MAAM,IAAI,KAAK,CAAC,mBAAmB,CAAC,CAAC;gBACvC,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,MAAM,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE,CAAC,IAAI,CAAC,CAAC;YAE1D,0DAA0D;YAC1D,IAAA,eAAM,EAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;QAEH,IAAA,WAAE,EAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;YACpE,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,GAAW,EAAE,EAAE;gBACjD,IAAI,OAAO,GAAG,KAAK,QAAQ,IAAI,GAAG,KAAK,UAAU,EAAE,CAAC;oBAClD,MAAM,IAAI,KAAK,CAAC,iBAAiB,CAAC,CAAC;gBACrC,CAAC;gBACD,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,CAAC;YACtB,CAAC,CAAC,CAAC;YAEH,6EAA6E;YAC7E,8EAA8E;YAC9E,mEAAmE;YACnE,MAAM,IAAA,eAAM,EACV,OAAO,CAAC,aAAa,CAAC,UAAU,EAAE,KAAK,IAAI,EAAE;gBAC3C,MAAM,IAAI,KAAK,CAAC,gBAAgB,CAAC,CAAC;YACpC,CAAC,CAAC,CACH,CAAC,OAAO,CAAC,OAAO,EAAE,CAAC;YAEpB,kCAAkC;YAClC,IAAA,eAAM,EAAC,WAAW,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"database-manager.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/database-manager.test.ts"],"names":[],"mappings":""}
|
|
@@ -1,110 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
const vitest_1 = require("vitest");
|
|
4
|
-
const manager_js_1 = require("../database/manager.js");
|
|
5
|
-
// ---------------------------------------------------------------------------
|
|
6
|
-
// getDatabaseName
|
|
7
|
-
// ---------------------------------------------------------------------------
|
|
8
|
-
(0, vitest_1.describe)("getDatabaseName", () => {
|
|
9
|
-
(0, vitest_1.it)("returns correct name for a simple slug", () => {
|
|
10
|
-
(0, vitest_1.expect)((0, manager_js_1.getDatabaseName)("acme")).toBe("stratum_tenant_acme");
|
|
11
|
-
});
|
|
12
|
-
(0, vitest_1.it)("handles slugs with numbers and underscores", () => {
|
|
13
|
-
(0, vitest_1.expect)((0, manager_js_1.getDatabaseName)("acme_corp_2")).toBe("stratum_tenant_acme_corp_2");
|
|
14
|
-
});
|
|
15
|
-
(0, vitest_1.it)("throws on slug with hyphens", () => {
|
|
16
|
-
(0, vitest_1.expect)(() => (0, manager_js_1.getDatabaseName)("acme-corp")).toThrow(/Invalid tenant slug/);
|
|
17
|
-
});
|
|
18
|
-
(0, vitest_1.it)("throws on slug starting with a number", () => {
|
|
19
|
-
(0, vitest_1.expect)(() => (0, manager_js_1.getDatabaseName)("1acme")).toThrow(/Invalid tenant slug/);
|
|
20
|
-
});
|
|
21
|
-
(0, vitest_1.it)("throws on empty slug", () => {
|
|
22
|
-
(0, vitest_1.expect)(() => (0, manager_js_1.getDatabaseName)("")).toThrow(/Invalid tenant slug/);
|
|
23
|
-
});
|
|
24
|
-
(0, vitest_1.it)("throws on slug with uppercase letters", () => {
|
|
25
|
-
(0, vitest_1.expect)(() => (0, manager_js_1.getDatabaseName)("Acme")).toThrow(/Invalid tenant slug/);
|
|
26
|
-
});
|
|
27
|
-
(0, vitest_1.it)("throws on slug that is too long (> 63 chars)", () => {
|
|
28
|
-
// 64 chars: 'a' + 63 'x'
|
|
29
|
-
const longSlug = "a" + "x".repeat(63);
|
|
30
|
-
(0, vitest_1.expect)(() => (0, manager_js_1.getDatabaseName)(longSlug)).toThrow(/Invalid tenant slug/);
|
|
31
|
-
});
|
|
32
|
-
(0, vitest_1.it)("accepts maximum-length slug (63 chars)", () => {
|
|
33
|
-
const maxSlug = "a" + "x".repeat(62); // 63 chars total
|
|
34
|
-
(0, vitest_1.expect)((0, manager_js_1.getDatabaseName)(maxSlug)).toBe(`stratum_tenant_${maxSlug}`);
|
|
35
|
-
});
|
|
36
|
-
});
|
|
37
|
-
// ---------------------------------------------------------------------------
|
|
38
|
-
// databaseExists
|
|
39
|
-
// ---------------------------------------------------------------------------
|
|
40
|
-
(0, vitest_1.describe)("databaseExists", () => {
|
|
41
|
-
function makeClient(exists) {
|
|
42
|
-
return {
|
|
43
|
-
query: vitest_1.vi.fn().mockResolvedValue({ rows: [{ exists }] }),
|
|
44
|
-
};
|
|
45
|
-
}
|
|
46
|
-
(0, vitest_1.it)("returns true when database exists", async () => {
|
|
47
|
-
const client = makeClient(true);
|
|
48
|
-
const result = await (0, manager_js_1.databaseExists)(client, "acme");
|
|
49
|
-
(0, vitest_1.expect)(result).toBe(true);
|
|
50
|
-
(0, vitest_1.expect)(client.query).toHaveBeenCalledWith(vitest_1.expect.stringContaining("pg_database"), ["stratum_tenant_acme"]);
|
|
51
|
-
});
|
|
52
|
-
(0, vitest_1.it)("returns false when database does not exist", async () => {
|
|
53
|
-
const client = makeClient(false);
|
|
54
|
-
const result = await (0, manager_js_1.databaseExists)(client, "acme");
|
|
55
|
-
(0, vitest_1.expect)(result).toBe(false);
|
|
56
|
-
});
|
|
57
|
-
(0, vitest_1.it)("throws on invalid slug", async () => {
|
|
58
|
-
const client = makeClient(false);
|
|
59
|
-
await (0, vitest_1.expect)((0, manager_js_1.databaseExists)(client, "bad-slug")).rejects.toThrow(/Invalid tenant slug/);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
// ---------------------------------------------------------------------------
|
|
63
|
-
// createDatabase
|
|
64
|
-
// ---------------------------------------------------------------------------
|
|
65
|
-
(0, vitest_1.describe)("createDatabase", () => {
|
|
66
|
-
function makeClient() {
|
|
67
|
-
return {
|
|
68
|
-
query: vitest_1.vi.fn().mockResolvedValue({}),
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
(0, vitest_1.it)("issues CREATE DATABASE with correct name", async () => {
|
|
72
|
-
const client = makeClient();
|
|
73
|
-
await (0, manager_js_1.createDatabase)(client, "acme");
|
|
74
|
-
(0, vitest_1.expect)(client.query).toHaveBeenCalledWith('CREATE DATABASE "stratum_tenant_acme"');
|
|
75
|
-
});
|
|
76
|
-
(0, vitest_1.it)("includes TEMPLATE clause when templateDb is provided", async () => {
|
|
77
|
-
const client = makeClient();
|
|
78
|
-
await (0, manager_js_1.createDatabase)(client, "acme", "stratum_base");
|
|
79
|
-
(0, vitest_1.expect)(client.query).toHaveBeenCalledWith('CREATE DATABASE "stratum_tenant_acme" TEMPLATE "stratum_base"');
|
|
80
|
-
});
|
|
81
|
-
(0, vitest_1.it)("throws on invalid template database name", async () => {
|
|
82
|
-
const client = makeClient();
|
|
83
|
-
await (0, vitest_1.expect)((0, manager_js_1.createDatabase)(client, "acme", "bad-template!")).rejects.toThrow(/Invalid template database name/);
|
|
84
|
-
(0, vitest_1.expect)(client.query).not.toHaveBeenCalled();
|
|
85
|
-
});
|
|
86
|
-
(0, vitest_1.it)("throws on invalid slug", async () => {
|
|
87
|
-
const client = makeClient();
|
|
88
|
-
await (0, vitest_1.expect)((0, manager_js_1.createDatabase)(client, "bad-slug")).rejects.toThrow(/Invalid tenant slug/);
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
// ---------------------------------------------------------------------------
|
|
92
|
-
// dropDatabase
|
|
93
|
-
// ---------------------------------------------------------------------------
|
|
94
|
-
(0, vitest_1.describe)("dropDatabase", () => {
|
|
95
|
-
function makeClient() {
|
|
96
|
-
return {
|
|
97
|
-
query: vitest_1.vi.fn().mockResolvedValue({}),
|
|
98
|
-
};
|
|
99
|
-
}
|
|
100
|
-
(0, vitest_1.it)("issues DROP DATABASE IF EXISTS with correct name", async () => {
|
|
101
|
-
const client = makeClient();
|
|
102
|
-
await (0, manager_js_1.dropDatabase)(client, "acme");
|
|
103
|
-
(0, vitest_1.expect)(client.query).toHaveBeenCalledWith('DROP DATABASE IF EXISTS "stratum_tenant_acme"');
|
|
104
|
-
});
|
|
105
|
-
(0, vitest_1.it)("throws on invalid slug", async () => {
|
|
106
|
-
const client = makeClient();
|
|
107
|
-
await (0, vitest_1.expect)((0, manager_js_1.dropDatabase)(client, "bad slug")).rejects.toThrow(/Invalid tenant slug/);
|
|
108
|
-
});
|
|
109
|
-
});
|
|
110
|
-
//# sourceMappingURL=database-manager.test.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"database-manager.test.js","sourceRoot":"","sources":["../../src/__tests__/database-manager.test.ts"],"names":[],"mappings":";;AAAA,mCAA8D;AAC9D,uDAKgC;AAGhC,8EAA8E;AAC9E,kBAAkB;AAClB,8EAA8E;AAE9E,IAAA,iBAAQ,EAAC,iBAAiB,EAAE,GAAG,EAAE;IAC/B,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,IAAA,eAAM,EAAC,IAAA,4BAAe,EAAC,MAAM,CAAC,CAAC,CAAC,IAAI,CAAC,qBAAqB,CAAC,CAAC;IAC9D,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,GAAG,EAAE;QACpD,IAAA,eAAM,EAAC,IAAA,4BAAe,EAAC,aAAa,CAAC,CAAC,CAAC,IAAI,CAAC,4BAA4B,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,IAAA,eAAM,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAe,EAAC,WAAW,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC5E,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,IAAA,eAAM,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAe,EAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACxE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,sBAAsB,EAAE,GAAG,EAAE;QAC9B,IAAA,eAAM,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAe,EAAC,EAAE,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACnE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,uCAAuC,EAAE,GAAG,EAAE;QAC/C,IAAA,eAAM,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAe,EAAC,MAAM,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACvE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,8CAA8C,EAAE,GAAG,EAAE;QACtD,yBAAyB;QACzB,MAAM,QAAQ,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;QACtC,IAAA,eAAM,EAAC,GAAG,EAAE,CAAC,IAAA,4BAAe,EAAC,QAAQ,CAAC,CAAC,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC;IACzE,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wCAAwC,EAAE,GAAG,EAAE;QAChD,MAAM,OAAO,GAAG,GAAG,GAAG,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC,CAAC,iBAAiB;QACvD,IAAA,eAAM,EAAC,IAAA,4BAAe,EAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,kBAAkB,OAAO,EAAE,CAAC,CAAC;IACrE,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,IAAA,iBAAQ,EAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,SAAS,UAAU,CAAC,MAAe;QACjC,OAAO;YACL,KAAK,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;SAC7B,CAAC;IAChC,CAAC;IAED,IAAA,WAAE,EAAC,mCAAmC,EAAE,KAAK,IAAI,EAAE;QACjD,MAAM,MAAM,GAAG,UAAU,CAAC,IAAI,CAAC,CAAC;QAChC,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAc,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC1B,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CACvC,eAAM,CAAC,gBAAgB,CAAC,aAAa,CAAC,EACtC,CAAC,qBAAqB,CAAC,CACxB,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,4CAA4C,EAAE,KAAK,IAAI,EAAE;QAC1D,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,MAAM,GAAG,MAAM,IAAA,2BAAc,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,MAAM,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC7B,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,CAAC,KAAK,CAAC,CAAC;QACjC,MAAM,IAAA,eAAM,EAAC,IAAA,2BAAc,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC9D,qBAAqB,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,iBAAiB;AACjB,8EAA8E;AAE9E,IAAA,iBAAQ,EAAC,gBAAgB,EAAE,GAAG,EAAE;IAC9B,SAAS,UAAU;QACjB,OAAO;YACL,KAAK,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;SACT,CAAC;IAChC,CAAC;IAED,IAAA,WAAE,EAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,2BAAc,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CACvC,uCAAuC,CACxC,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,sDAAsD,EAAE,KAAK,IAAI,EAAE;QACpE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,2BAAc,EAAC,MAAM,EAAE,MAAM,EAAE,cAAc,CAAC,CAAC;QACrD,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CACvC,+DAA+D,CAChE,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;QACxD,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,eAAM,EACV,IAAA,2BAAc,EAAC,MAAM,EAAE,MAAM,EAAE,eAAe,CAAC,CAChD,CAAC,OAAO,CAAC,OAAO,CAAC,gCAAgC,CAAC,CAAC;QACpD,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAC;IAC9C,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,eAAM,EAAC,IAAA,2BAAc,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC9D,qBAAqB,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,8EAA8E;AAC9E,eAAe;AACf,8EAA8E;AAE9E,IAAA,iBAAQ,EAAC,cAAc,EAAE,GAAG,EAAE;IAC5B,SAAS,UAAU;QACjB,OAAO;YACL,KAAK,EAAE,WAAE,CAAC,EAAE,EAAE,CAAC,iBAAiB,CAAC,EAAE,CAAC;SACT,CAAC;IAChC,CAAC;IAED,IAAA,WAAE,EAAC,kDAAkD,EAAE,KAAK,IAAI,EAAE;QAChE,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,yBAAY,EAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACnC,IAAA,eAAM,EAAC,MAAM,CAAC,KAAK,CAAC,CAAC,oBAAoB,CACvC,+CAA+C,CAChD,CAAC;IACJ,CAAC,CAAC,CAAC;IAEH,IAAA,WAAE,EAAC,wBAAwB,EAAE,KAAK,IAAI,EAAE;QACtC,MAAM,MAAM,GAAG,UAAU,EAAE,CAAC;QAC5B,MAAM,IAAA,eAAM,EAAC,IAAA,yBAAY,EAAC,MAAM,EAAE,UAAU,CAAC,CAAC,CAAC,OAAO,CAAC,OAAO,CAC5D,qBAAqB,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"drizzle.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/drizzle.test.ts"],"names":[],"mappings":""}
|