@tstdl/base 0.93.87 → 0.93.89
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/ai/genkit/helpers.d.ts +3 -1
- package/ai/genkit/helpers.js +3 -3
- package/api/server/gateway.d.ts +3 -0
- package/api/server/gateway.js +15 -4
- package/api/server/middlewares/catch-error.middleware.js +2 -4
- package/api/server/middlewares/cors.middleware.js +2 -3
- package/api/server/middlewares/csrf.middleware.d.ts +41 -0
- package/api/server/middlewares/csrf.middleware.js +108 -0
- package/api/server/middlewares/index.d.ts +1 -0
- package/api/server/middlewares/index.js +1 -0
- package/api/server/module.d.ts +8 -2
- package/api/server/module.js +14 -8
- package/api/server/tests/csrf.middleware.test.js +91 -0
- package/audit/drizzle/{0000_bored_stick.sql → 0000_lumpy_thunderball.sql} +3 -3
- package/audit/drizzle/meta/0000_snapshot.json +4 -4
- package/audit/drizzle/meta/_journal.json +2 -9
- package/audit/module.d.ts +4 -1
- package/audit/module.js +3 -2
- package/audit/schemas.d.ts +1 -1
- package/audit/types.d.ts +1 -1
- package/audit/types.js +1 -1
- package/authentication/client/authentication.service.d.ts +14 -1
- package/authentication/client/authentication.service.js +82 -23
- package/authentication/client/http-client.middleware.d.ts +6 -0
- package/authentication/client/http-client.middleware.js +36 -0
- package/authentication/client/module.js +8 -2
- package/authentication/models/service-account.model.d.ts +2 -2
- package/authentication/models/service-account.model.js +10 -5
- package/authentication/models/subject.model.d.ts +19 -5
- package/authentication/models/subject.model.js +25 -29
- package/authentication/models/system-account.model.d.ts +3 -2
- package/authentication/models/system-account.model.js +11 -5
- package/authentication/models/user.model.d.ts +2 -11
- package/authentication/models/user.model.js +5 -16
- package/authentication/server/authentication-api-request-token.provider.d.ts +0 -2
- package/authentication/server/authentication-api-request-token.provider.js +3 -11
- package/authentication/server/authentication.api-controller.d.ts +1 -2
- package/authentication/server/authentication.api-controller.js +8 -9
- package/authentication/server/authentication.audit.d.ts +3 -2
- package/authentication/server/authentication.service.d.ts +27 -1
- package/authentication/server/authentication.service.js +67 -18
- package/authentication/server/drizzle/{0000_normal_paper_doll.sql → 0000_soft_tag.sql} +25 -32
- package/authentication/server/drizzle/meta/0000_snapshot.json +180 -205
- package/authentication/server/drizzle/meta/_journal.json +2 -2
- package/authentication/server/helper.js +9 -2
- package/authentication/server/module.d.ts +4 -1
- package/authentication/server/module.js +9 -5
- package/authentication/server/schemas.d.ts +2 -1
- package/authentication/server/schemas.js +2 -2
- package/authentication/server/subject.service.d.ts +14 -8
- package/authentication/server/subject.service.js +86 -84
- package/authentication/tests/authentication-ancillary.service.test.d.ts +1 -0
- package/authentication/tests/authentication-ancillary.service.test.js +13 -0
- package/authentication/tests/authentication-secret-requirements.validator.test.d.ts +1 -0
- package/authentication/tests/authentication-secret-requirements.validator.test.js +29 -0
- package/authentication/tests/authentication.api-controller.test.d.ts +1 -0
- package/authentication/tests/authentication.api-controller.test.js +88 -0
- package/authentication/tests/authentication.api-request-token.provider.test.d.ts +1 -0
- package/authentication/tests/authentication.api-request-token.provider.test.js +48 -0
- package/authentication/tests/authentication.client-middleware.test.d.ts +1 -0
- package/authentication/tests/authentication.client-middleware.test.js +23 -0
- package/authentication/tests/authentication.client-service.test.d.ts +1 -0
- package/authentication/tests/authentication.client-service.test.js +70 -0
- package/authentication/tests/authentication.service.test.d.ts +1 -0
- package/authentication/tests/authentication.service.test.js +186 -0
- package/authentication/tests/authentication.test-ancillary-service.d.ts +9 -0
- package/authentication/tests/authentication.test-ancillary-service.js +27 -0
- package/authentication/tests/helper.test.d.ts +1 -0
- package/authentication/tests/helper.test.js +107 -0
- package/authentication/tests/secret-requirements.error.test.d.ts +1 -0
- package/authentication/tests/secret-requirements.error.test.js +14 -0
- package/authentication/tests/subject.service.test.d.ts +1 -0
- package/authentication/tests/subject.service.test.js +140 -0
- package/circuit-breaker/postgres/drizzle/meta/0000_snapshot.json +1 -1
- package/circuit-breaker/postgres/drizzle/meta/_journal.json +2 -2
- package/circuit-breaker/postgres/module.d.ts +7 -1
- package/circuit-breaker/postgres/module.js +8 -6
- package/circuit-breaker/tests/circuit-breaker.test.js +2 -22
- package/document-management/api/document-management.api.js +2 -6
- package/document-management/server/services/document-validation.service.js +6 -5
- package/document-management/server/services/document-workflow.service.js +5 -5
- package/document-management/service-models/document-folders.view-model.d.ts +5 -2
- package/document-management/service-models/document-folders.view-model.js +42 -9
- package/document-management/service-models/enriched/enriched-document-management-data.view.js +1 -1
- package/examples/document-management/main.js +4 -4
- package/http/client/adapters/undici.adapter.d.ts +7 -5
- package/http/client/adapters/undici.adapter.js +13 -10
- package/http/client/module.d.ts +3 -1
- package/http/client/module.js +8 -9
- package/http/server/http-server.d.ts +2 -0
- package/http/server/node/module.d.ts +6 -2
- package/http/server/node/module.js +6 -4
- package/http/server/node/node-http-server.d.ts +2 -0
- package/http/server/node/node-http-server.js +7 -0
- package/http/types.d.ts +1 -1
- package/key-value-store/postgres/module.d.ts +7 -1
- package/key-value-store/postgres/module.js +7 -3
- package/lock/postgres/lock.js +0 -1
- package/lock/postgres/module.d.ts +7 -1
- package/lock/postgres/module.js +9 -5
- package/logger/formatter.d.ts +2 -0
- package/logger/formatters/json.js +2 -2
- package/logger/formatters/pretty-print.js +8 -10
- package/logger/logger.d.ts +1 -1
- package/logger/logger.js +15 -12
- package/message-bus/local/module.d.ts +5 -2
- package/message-bus/local/module.js +5 -4
- package/module/module.d.ts +2 -1
- package/module/module.js +3 -0
- package/module/modules/web-server.module.d.ts +11 -6
- package/module/modules/web-server.module.js +15 -10
- package/orm/decorators.d.ts +24 -1
- package/orm/decorators.js +40 -4
- package/orm/query/base.d.ts +17 -17
- package/orm/query/base.js +1 -1
- package/orm/repository.types.d.ts +45 -1
- package/orm/schemas/tsvector.js +1 -1
- package/orm/server/drizzle/schema-converter.d.ts +3 -1
- package/orm/server/drizzle/schema-converter.js +120 -14
- package/orm/server/index.d.ts +1 -0
- package/orm/server/index.js +1 -0
- package/orm/server/module.d.ts +4 -2
- package/orm/server/module.js +6 -5
- package/orm/server/query-converter.d.ts +6 -3
- package/orm/server/query-converter.js +32 -20
- package/orm/server/repository-config.d.ts +8 -0
- package/orm/server/repository-config.js +8 -0
- package/orm/server/repository.d.ts +117 -43
- package/orm/server/repository.js +757 -253
- package/orm/server/transaction.d.ts +4 -2
- package/orm/server/transaction.js +14 -5
- package/orm/server/transactional.d.ts +6 -2
- package/orm/server/transactional.js +39 -9
- package/orm/server/types.d.ts +2 -0
- package/orm/sqls/case-when.d.ts +3 -3
- package/orm/sqls/case-when.js +2 -2
- package/orm/sqls/sqls.d.ts +31 -5
- package/orm/sqls/sqls.js +69 -6
- package/orm/tests/data-types.test.d.ts +1 -0
- package/orm/tests/data-types.test.js +39 -0
- package/orm/tests/decorators.test.d.ts +1 -0
- package/orm/tests/decorators.test.js +77 -0
- package/orm/tests/encryption.test.d.ts +1 -0
- package/orm/tests/encryption.test.js +34 -0
- package/orm/tests/query-complex.test.d.ts +1 -0
- package/orm/tests/query-complex.test.js +203 -0
- package/orm/tests/query-converter-complex.test.d.ts +1 -0
- package/orm/tests/query-converter-complex.test.js +126 -0
- package/orm/tests/query-converter.test.d.ts +1 -0
- package/orm/tests/query-converter.test.js +123 -0
- package/orm/tests/repository-advanced.test.d.ts +1 -0
- package/orm/tests/repository-advanced.test.js +232 -0
- package/orm/tests/repository-attributes.test.d.ts +1 -0
- package/orm/tests/repository-attributes.test.js +99 -0
- package/orm/tests/repository-comprehensive.test.d.ts +1 -0
- package/orm/tests/repository-comprehensive.test.js +187 -0
- package/orm/tests/repository-coverage.test.d.ts +1 -0
- package/orm/tests/repository-coverage.test.js +303 -0
- package/orm/tests/repository-cti-complex.test.d.ts +1 -0
- package/orm/tests/repository-cti-complex.test.js +170 -0
- package/orm/tests/repository-cti-embedded.test.d.ts +1 -0
- package/orm/tests/repository-cti-embedded.test.js +188 -0
- package/orm/tests/repository-cti-extensive.test.d.ts +1 -0
- package/orm/tests/repository-cti-extensive.test.js +308 -0
- package/orm/tests/repository-cti-mapping.test.d.ts +1 -0
- package/orm/tests/repository-cti-mapping.test.js +121 -0
- package/orm/tests/repository-cti-search.test.d.ts +1 -0
- package/orm/tests/repository-cti-search.test.js +152 -0
- package/orm/tests/repository-cti-soft-delete.test.d.ts +1 -0
- package/orm/tests/repository-cti-soft-delete.test.js +115 -0
- package/orm/tests/repository-cti-transactions.test.d.ts +1 -0
- package/orm/tests/repository-cti-transactions.test.js +126 -0
- package/orm/tests/repository-cti-upsert-many.test.d.ts +1 -0
- package/orm/tests/repository-cti-upsert-many.test.js +127 -0
- package/orm/tests/repository-cti.test.d.ts +1 -0
- package/orm/tests/repository-cti.test.js +456 -0
- package/orm/tests/repository-edge-cases.test.d.ts +1 -0
- package/orm/tests/repository-edge-cases.test.js +216 -0
- package/orm/tests/repository-expiration.test.d.ts +1 -0
- package/orm/tests/repository-expiration.test.js +153 -0
- package/orm/tests/repository-extra-coverage.test.d.ts +1 -0
- package/orm/tests/repository-extra-coverage.test.js +546 -0
- package/orm/tests/repository-mapping.test.d.ts +1 -0
- package/orm/tests/repository-mapping.test.js +71 -0
- package/orm/tests/repository-regression.test.d.ts +1 -0
- package/orm/tests/repository-regression.test.js +330 -0
- package/orm/tests/repository-search-coverage.test.d.ts +1 -0
- package/orm/tests/repository-search-coverage.test.js +129 -0
- package/orm/tests/repository-search.test.d.ts +1 -0
- package/orm/tests/repository-search.test.js +116 -0
- package/orm/tests/repository-soft-delete.test.d.ts +1 -0
- package/orm/tests/repository-soft-delete.test.js +143 -0
- package/orm/tests/repository-transactions-nested.test.d.ts +1 -0
- package/orm/tests/repository-transactions-nested.test.js +202 -0
- package/orm/tests/repository-types.test.d.ts +1 -0
- package/orm/tests/repository-types.test.js +218 -0
- package/orm/tests/schema-converter.test.d.ts +1 -0
- package/orm/tests/schema-converter.test.js +81 -0
- package/orm/tests/schema-generation.test.d.ts +1 -0
- package/orm/tests/schema-generation.test.js +127 -0
- package/orm/tests/sql-helpers.test.d.ts +1 -0
- package/orm/tests/sql-helpers.test.js +67 -0
- package/orm/tests/transaction-safety.test.d.ts +1 -0
- package/orm/tests/transaction-safety.test.js +81 -0
- package/orm/tests/transactional.test.d.ts +1 -0
- package/orm/tests/transactional.test.js +224 -0
- package/orm/tests/utils.test.d.ts +1 -0
- package/orm/tests/utils.test.js +70 -0
- package/orm/utils.d.ts +7 -0
- package/orm/utils.js +26 -6
- package/package.json +12 -7
- package/pool/pool.js +1 -1
- package/rate-limit/index.d.ts +2 -0
- package/rate-limit/index.js +2 -0
- package/rate-limit/postgres/drizzle/0000_watery_rage.sql +7 -0
- package/{queue → rate-limit}/postgres/drizzle/meta/0000_snapshot.json +14 -39
- package/rate-limit/postgres/drizzle/meta/_journal.json +13 -0
- package/{queue → rate-limit}/postgres/drizzle.config.js +1 -1
- package/rate-limit/postgres/index.d.ts +4 -0
- package/rate-limit/postgres/index.js +4 -0
- package/rate-limit/postgres/module.d.ts +12 -0
- package/rate-limit/postgres/module.js +28 -0
- package/rate-limit/postgres/postgres-rate-limiter.d.ts +9 -0
- package/rate-limit/postgres/postgres-rate-limiter.js +56 -0
- package/rate-limit/postgres/rate-limit.model.d.ts +8 -0
- package/rate-limit/postgres/rate-limit.model.js +35 -0
- package/rate-limit/postgres/rate-limiter.provider.d.ts +6 -0
- package/rate-limit/postgres/rate-limiter.provider.js +21 -0
- package/rate-limit/postgres/schemas.d.ts +3 -0
- package/rate-limit/postgres/schemas.js +4 -0
- package/rate-limit/provider.d.ts +9 -0
- package/rate-limit/provider.js +2 -0
- package/rate-limit/rate-limiter.d.ts +35 -0
- package/rate-limit/rate-limiter.js +3 -0
- package/rate-limit/tests/postgres-rate-limiter.test.d.ts +1 -0
- package/rate-limit/tests/postgres-rate-limiter.test.js +92 -0
- package/signals/implementation/configure.d.ts +3 -0
- package/signals/implementation/configure.js +3 -0
- package/sse/data-stream-source.d.ts +1 -1
- package/sse/data-stream-source.js +6 -6
- package/task-queue/enqueue-batch.d.ts +17 -0
- package/task-queue/enqueue-batch.js +24 -0
- package/{queue → task-queue}/index.d.ts +1 -1
- package/{queue → task-queue}/index.js +1 -1
- package/task-queue/postgres/drizzle/0000_thin_black_panther.sql +74 -0
- package/task-queue/postgres/drizzle/meta/0000_snapshot.json +592 -0
- package/task-queue/postgres/drizzle/meta/_journal.json +13 -0
- package/task-queue/postgres/drizzle.config.d.ts +2 -0
- package/task-queue/postgres/drizzle.config.js +11 -0
- package/task-queue/postgres/index.d.ts +4 -0
- package/task-queue/postgres/index.js +4 -0
- package/task-queue/postgres/module.d.ts +12 -0
- package/task-queue/postgres/module.js +28 -0
- package/task-queue/postgres/schemas.d.ts +16 -0
- package/task-queue/postgres/schemas.js +8 -0
- package/task-queue/postgres/task-queue.d.ts +83 -0
- package/task-queue/postgres/task-queue.js +1054 -0
- package/task-queue/postgres/task-queue.provider.d.ts +7 -0
- package/{queue/postgres/queue.provider.js → task-queue/postgres/task-queue.provider.js} +8 -8
- package/task-queue/postgres/task.model.d.ts +39 -0
- package/task-queue/postgres/task.model.js +178 -0
- package/{queue → task-queue}/provider.d.ts +3 -3
- package/task-queue/provider.js +2 -0
- package/{queue → task-queue}/task-context.d.ts +7 -7
- package/{queue → task-queue}/task-context.js +8 -8
- package/{queue/queue.d.ts → task-queue/task-queue.d.ts} +128 -59
- package/task-queue/task-queue.js +200 -0
- package/task-queue/tests/complex.test.d.ts +1 -0
- package/task-queue/tests/complex.test.js +299 -0
- package/task-queue/tests/dependencies.test.d.ts +1 -0
- package/task-queue/tests/dependencies.test.js +174 -0
- package/task-queue/tests/queue.test.d.ts +1 -0
- package/task-queue/tests/queue.test.js +334 -0
- package/task-queue/tests/worker.test.d.ts +1 -0
- package/task-queue/tests/worker.test.js +163 -0
- package/test1.js +1 -1
- package/test4.js +2 -2
- package/unit-test/index.d.ts +1 -0
- package/unit-test/index.js +1 -0
- package/unit-test/integration-setup.d.ts +55 -0
- package/unit-test/integration-setup.js +182 -0
- package/utils/patterns.d.ts +3 -0
- package/utils/patterns.js +6 -1
- package/audit/drizzle/0001_previous_network.sql +0 -2
- package/audit/drizzle/meta/0001_snapshot.json +0 -195
- package/queue/enqueue-batch.d.ts +0 -17
- package/queue/enqueue-batch.js +0 -18
- package/queue/postgres/drizzle/0000_zippy_moondragon.sql +0 -11
- package/queue/postgres/drizzle/0001_certain_wild_pack.sql +0 -2
- package/queue/postgres/drizzle/0002_dear_meggan.sql +0 -2
- package/queue/postgres/drizzle/0003_tricky_venom.sql +0 -30
- package/queue/postgres/drizzle/meta/0001_snapshot.json +0 -103
- package/queue/postgres/drizzle/meta/0002_snapshot.json +0 -90
- package/queue/postgres/drizzle/meta/0003_snapshot.json +0 -288
- package/queue/postgres/drizzle/meta/_journal.json +0 -34
- package/queue/postgres/index.d.ts +0 -4
- package/queue/postgres/index.js +0 -4
- package/queue/postgres/module.d.ts +0 -9
- package/queue/postgres/module.js +0 -29
- package/queue/postgres/queue.d.ts +0 -60
- package/queue/postgres/queue.js +0 -681
- package/queue/postgres/queue.provider.d.ts +0 -7
- package/queue/postgres/schemas.d.ts +0 -14
- package/queue/postgres/schemas.js +0 -6
- package/queue/postgres/task.model.d.ts +0 -24
- package/queue/postgres/task.model.js +0 -115
- package/queue/provider.js +0 -2
- package/queue/queue.js +0 -131
- package/queue/tests/queue.test.js +0 -623
- package/test3.d.ts +0 -1
- package/test3.js +0 -47
- /package/{queue/tests/queue.test.d.ts → api/server/tests/csrf.middleware.test.d.ts} +0 -0
- /package/circuit-breaker/postgres/drizzle/{0000_hard_shocker.sql → 0000_cooing_korath.sql} +0 -0
- /package/{queue → rate-limit}/postgres/drizzle.config.d.ts +0 -0
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
11
|
+
import { Integer, StringProperty } from '../../schema/index.js';
|
|
12
|
+
import { toArrayAsync } from '../../utils/async-iterable-helpers/to-array.js';
|
|
13
|
+
import { sql } from 'drizzle-orm';
|
|
14
|
+
import { beforeAll, describe, expect, test } from 'vitest';
|
|
15
|
+
import { Table } from '../decorators.js';
|
|
16
|
+
import { Entity } from '../entity.js';
|
|
17
|
+
import { configureOrm, Database } from '../server/index.js';
|
|
18
|
+
import { injectRepository } from '../server/repository.js';
|
|
19
|
+
describe('ORM Repository Advanced (Integration)', () => {
|
|
20
|
+
let injector;
|
|
21
|
+
let db;
|
|
22
|
+
const schema = 'test_orm_advanced';
|
|
23
|
+
let AdvancedEntity = class AdvancedEntity extends Entity {
|
|
24
|
+
name;
|
|
25
|
+
value;
|
|
26
|
+
};
|
|
27
|
+
__decorate([
|
|
28
|
+
StringProperty(),
|
|
29
|
+
__metadata("design:type", String)
|
|
30
|
+
], AdvancedEntity.prototype, "name", void 0);
|
|
31
|
+
__decorate([
|
|
32
|
+
Integer(),
|
|
33
|
+
__metadata("design:type", Number)
|
|
34
|
+
], AdvancedEntity.prototype, "value", void 0);
|
|
35
|
+
AdvancedEntity = __decorate([
|
|
36
|
+
Table('advanced_entities', { schema })
|
|
37
|
+
], AdvancedEntity);
|
|
38
|
+
beforeAll(async () => {
|
|
39
|
+
injector = new Injector('Test');
|
|
40
|
+
configureOrm({
|
|
41
|
+
repositoryConfig: { schema },
|
|
42
|
+
connection: {
|
|
43
|
+
host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
|
|
44
|
+
},
|
|
45
|
+
});
|
|
46
|
+
db = injector.resolve(Database);
|
|
47
|
+
await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
|
|
48
|
+
await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
49
|
+
await db.execute(sql `
|
|
50
|
+
CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} (
|
|
51
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
52
|
+
name TEXT NOT NULL,
|
|
53
|
+
value INTEGER NOT NULL,
|
|
54
|
+
revision INTEGER NOT NULL,
|
|
55
|
+
revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
56
|
+
create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
57
|
+
delete_timestamp TIMESTAMP WITH TIME ZONE,
|
|
58
|
+
attributes JSONB NOT NULL DEFAULT '{}'
|
|
59
|
+
)
|
|
60
|
+
`);
|
|
61
|
+
});
|
|
62
|
+
test('should support loadManyCursor', async () => {
|
|
63
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
64
|
+
await runInInjectionContext(injector, async () => {
|
|
65
|
+
const repository = injectRepository(AdvancedEntity);
|
|
66
|
+
const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
67
|
+
const e2 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
|
|
68
|
+
const cursor = repository.loadManyCursor([e1.id, e2.id]);
|
|
69
|
+
const results = await toArrayAsync(cursor);
|
|
70
|
+
expect(results).toHaveLength(2);
|
|
71
|
+
expect(results.map((r) => r.name).sort()).toEqual(['E1', 'E2']);
|
|
72
|
+
});
|
|
73
|
+
});
|
|
74
|
+
test('should support loadAllCursor', async () => {
|
|
75
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
76
|
+
await runInInjectionContext(injector, async () => {
|
|
77
|
+
const repository = injectRepository(AdvancedEntity);
|
|
78
|
+
await repository.insertMany([
|
|
79
|
+
Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }),
|
|
80
|
+
Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }),
|
|
81
|
+
]);
|
|
82
|
+
const cursor = repository.loadAllCursor();
|
|
83
|
+
const results = await toArrayAsync(cursor);
|
|
84
|
+
expect(results).toHaveLength(2);
|
|
85
|
+
});
|
|
86
|
+
});
|
|
87
|
+
test('should support countByQuery with various filters', async () => {
|
|
88
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
89
|
+
await runInInjectionContext(injector, async () => {
|
|
90
|
+
const repository = injectRepository(AdvancedEntity);
|
|
91
|
+
await repository.insertMany([
|
|
92
|
+
Object.assign(new AdvancedEntity(), { name: 'A', value: 10 }),
|
|
93
|
+
Object.assign(new AdvancedEntity(), { name: 'B', value: 20 }),
|
|
94
|
+
Object.assign(new AdvancedEntity(), { name: 'C', value: 30 }),
|
|
95
|
+
]);
|
|
96
|
+
expect(await repository.countByQuery({ value: { $gt: 15 } })).toBe(2);
|
|
97
|
+
expect(await repository.countByQuery({ value: { $lt: 25 } })).toBe(2);
|
|
98
|
+
expect(await repository.countByQuery({ name: 'A' })).toBe(1);
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
test('should support has and hasByQuery', async () => {
|
|
102
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
103
|
+
await runInInjectionContext(injector, async () => {
|
|
104
|
+
const repository = injectRepository(AdvancedEntity);
|
|
105
|
+
const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
106
|
+
expect(await repository.has(e1.id)).toBe(true);
|
|
107
|
+
expect(await repository.has('00000000-0000-0000-0000-000000000000')).toBe(false);
|
|
108
|
+
expect(await repository.hasByQuery({ name: 'E1' })).toBe(true);
|
|
109
|
+
expect(await repository.hasByQuery({ name: 'E2' })).toBe(false);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
test('should support tryUpdate', async () => {
|
|
113
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
114
|
+
await runInInjectionContext(injector, async () => {
|
|
115
|
+
const repository = injectRepository(AdvancedEntity);
|
|
116
|
+
const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
117
|
+
const updated = await repository.tryUpdate(e1.id, { name: 'E1-Updated' });
|
|
118
|
+
expect(updated.name).toBe('E1-Updated');
|
|
119
|
+
const missing = await repository.tryUpdate('00000000-0000-0000-0000-000000000000', { name: 'X' });
|
|
120
|
+
expect(missing).toBeUndefined();
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
test('should support tryUpdateByQuery', async () => {
|
|
124
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
125
|
+
await runInInjectionContext(injector, async () => {
|
|
126
|
+
const repository = injectRepository(AdvancedEntity);
|
|
127
|
+
await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
128
|
+
const updated = await repository.tryUpdateByQuery({ name: 'E1' }, { value: 100 });
|
|
129
|
+
expect(updated.value).toBe(100);
|
|
130
|
+
const missing = await repository.tryUpdateByQuery({ name: 'Missing' }, { value: 0 });
|
|
131
|
+
expect(missing).toBeUndefined();
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
test('should support insertIfNotExists', async () => {
|
|
135
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
136
|
+
await runInInjectionContext(injector, async () => {
|
|
137
|
+
const repository = injectRepository(AdvancedEntity);
|
|
138
|
+
const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
139
|
+
// Conflict on ID
|
|
140
|
+
const conflict = await repository.insertIfNotExists('id', Object.assign(new AdvancedEntity(), { id: e1.id, name: 'Conflict', value: 2 }));
|
|
141
|
+
expect(conflict).toBeUndefined();
|
|
142
|
+
// No conflict
|
|
143
|
+
const success = await repository.insertIfNotExists('id', Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
|
|
144
|
+
expect(success).toBeDefined();
|
|
145
|
+
expect(success.name).toBe('E2');
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
test('should support pagination (limit/offset)', async () => {
|
|
149
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
150
|
+
await runInInjectionContext(injector, async () => {
|
|
151
|
+
const repository = injectRepository(AdvancedEntity);
|
|
152
|
+
await repository.insertMany([
|
|
153
|
+
Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }),
|
|
154
|
+
Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }),
|
|
155
|
+
Object.assign(new AdvancedEntity(), { name: 'E3', value: 3 }),
|
|
156
|
+
]);
|
|
157
|
+
const p1 = await repository.loadAll({ limit: 2, order: { value: 'asc' } });
|
|
158
|
+
expect(p1).toHaveLength(2);
|
|
159
|
+
expect(p1[0].name).toBe('E1');
|
|
160
|
+
expect(p1[1].name).toBe('E2');
|
|
161
|
+
const p2 = await repository.loadAll({ limit: 2, offset: 2, order: { value: 'asc' } });
|
|
162
|
+
expect(p2).toHaveLength(1);
|
|
163
|
+
expect(p2[0].name).toBe('E3');
|
|
164
|
+
});
|
|
165
|
+
});
|
|
166
|
+
test('should support complex ordering', async () => {
|
|
167
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
168
|
+
await runInInjectionContext(injector, async () => {
|
|
169
|
+
const repository = injectRepository(AdvancedEntity);
|
|
170
|
+
await repository.insertMany([
|
|
171
|
+
Object.assign(new AdvancedEntity(), { name: 'B', value: 10 }),
|
|
172
|
+
Object.assign(new AdvancedEntity(), { name: 'A', value: 20 }),
|
|
173
|
+
Object.assign(new AdvancedEntity(), { name: 'A', value: 10 }),
|
|
174
|
+
]);
|
|
175
|
+
const results = await repository.loadAll({
|
|
176
|
+
order: [
|
|
177
|
+
['name', 'asc'],
|
|
178
|
+
['value', 'desc'],
|
|
179
|
+
],
|
|
180
|
+
});
|
|
181
|
+
expect(results[0].name).toBe('A');
|
|
182
|
+
expect(results[0].value).toBe(20);
|
|
183
|
+
expect(results[1].name).toBe('A');
|
|
184
|
+
expect(results[1].value).toBe(10);
|
|
185
|
+
expect(results[2].name).toBe('B');
|
|
186
|
+
});
|
|
187
|
+
});
|
|
188
|
+
test('should return empty array for empty loadMany', async () => {
|
|
189
|
+
await runInInjectionContext(injector, async () => {
|
|
190
|
+
const repository = injectRepository(AdvancedEntity);
|
|
191
|
+
const results = await repository.loadMany([]);
|
|
192
|
+
expect(results).toEqual([]);
|
|
193
|
+
});
|
|
194
|
+
});
|
|
195
|
+
test('should fail on duplicate ID insert', async () => {
|
|
196
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
197
|
+
await runInInjectionContext(injector, async () => {
|
|
198
|
+
const repository = injectRepository(AdvancedEntity);
|
|
199
|
+
const id = '00000000-0000-0000-0000-000000000001';
|
|
200
|
+
await repository.insert(Object.assign(new AdvancedEntity(), { id, name: 'E1', value: 1 }));
|
|
201
|
+
await expect(repository.insert(Object.assign(new AdvancedEntity(), { id, name: 'E2', value: 2 }))).rejects.toThrow();
|
|
202
|
+
});
|
|
203
|
+
});
|
|
204
|
+
test('should support explicit transaction usage', async () => {
|
|
205
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
206
|
+
await runInInjectionContext(injector, async () => {
|
|
207
|
+
const repository = injectRepository(AdvancedEntity);
|
|
208
|
+
await repository.transaction(async (tx) => {
|
|
209
|
+
const txRepo = repository.withTransaction(tx);
|
|
210
|
+
await txRepo.insert(Object.assign(new AdvancedEntity(), { name: 'TxEntity', value: 100 }));
|
|
211
|
+
// Should be visible in tx
|
|
212
|
+
expect(await txRepo.count()).toBe(1);
|
|
213
|
+
});
|
|
214
|
+
// Should be committed
|
|
215
|
+
expect(await repository.count()).toBe(1);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
test('should exclude soft-deleted entities by default', async () => {
|
|
219
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('advanced_entities')} CASCADE`);
|
|
220
|
+
await runInInjectionContext(injector, async () => {
|
|
221
|
+
const repository = injectRepository(AdvancedEntity);
|
|
222
|
+
const e1 = await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E1', value: 1 }));
|
|
223
|
+
await repository.insert(Object.assign(new AdvancedEntity(), { name: 'E2', value: 2 }));
|
|
224
|
+
await repository.delete(e1.id);
|
|
225
|
+
const all = await repository.loadAll();
|
|
226
|
+
expect(all).toHaveLength(1);
|
|
227
|
+
expect(all[0].name).toBe('E2');
|
|
228
|
+
const withDeleted = await repository.loadManyByQuery({}, { withDeleted: true });
|
|
229
|
+
expect(withDeleted).toHaveLength(2);
|
|
230
|
+
});
|
|
231
|
+
});
|
|
232
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
11
|
+
import { StringProperty } from '../../schema/index.js';
|
|
12
|
+
import { sql } from 'drizzle-orm';
|
|
13
|
+
import { beforeAll, describe, expect, test } from 'vitest';
|
|
14
|
+
import { Table } from '../decorators.js';
|
|
15
|
+
import { Entity } from '../entity.js';
|
|
16
|
+
import { configureOrm, Database } from '../server/index.js';
|
|
17
|
+
import { injectRepository } from '../server/repository.js';
|
|
18
|
+
describe('ORM Repository Attributes (Integration)', () => {
|
|
19
|
+
let injector;
|
|
20
|
+
let db;
|
|
21
|
+
const schema = 'test_orm_attributes';
|
|
22
|
+
let AttributeEntity = class AttributeEntity extends Entity {
|
|
23
|
+
name;
|
|
24
|
+
};
|
|
25
|
+
__decorate([
|
|
26
|
+
StringProperty(),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], AttributeEntity.prototype, "name", void 0);
|
|
29
|
+
AttributeEntity = __decorate([
|
|
30
|
+
Table('attribute_entities', { schema })
|
|
31
|
+
], AttributeEntity);
|
|
32
|
+
beforeAll(async () => {
|
|
33
|
+
injector = new Injector('Test');
|
|
34
|
+
configureOrm({
|
|
35
|
+
repositoryConfig: { schema },
|
|
36
|
+
connection: {
|
|
37
|
+
host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
|
|
38
|
+
},
|
|
39
|
+
});
|
|
40
|
+
db = injector.resolve(Database);
|
|
41
|
+
await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
|
|
42
|
+
await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
|
|
43
|
+
await db.execute(sql `
|
|
44
|
+
CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} (
|
|
45
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
46
|
+
name TEXT NOT NULL,
|
|
47
|
+
revision INTEGER NOT NULL,
|
|
48
|
+
revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
49
|
+
create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
50
|
+
delete_timestamp TIMESTAMP WITH TIME ZONE,
|
|
51
|
+
attributes JSONB NOT NULL DEFAULT '{}'
|
|
52
|
+
)
|
|
53
|
+
`);
|
|
54
|
+
});
|
|
55
|
+
test('should support partial attribute updates', async () => {
|
|
56
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
|
|
57
|
+
await runInInjectionContext(injector, async () => {
|
|
58
|
+
const repository = injectRepository(AttributeEntity);
|
|
59
|
+
const e1 = await repository.insert(Object.assign(new AttributeEntity(), {
|
|
60
|
+
name: 'E1',
|
|
61
|
+
metadata: { attributes: { a: 1, b: 2 } },
|
|
62
|
+
}));
|
|
63
|
+
expect(e1.metadata.attributes).toEqual({ a: 1, b: 2 });
|
|
64
|
+
const updated = await repository.update(e1.id, {
|
|
65
|
+
metadata: { attributes: { b: 3, c: 4 } },
|
|
66
|
+
});
|
|
67
|
+
expect(updated.metadata.attributes).toEqual({ a: 1, b: 3, c: 4 });
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
test('should update attributes during soft delete', async () => {
|
|
71
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
|
|
72
|
+
await runInInjectionContext(injector, async () => {
|
|
73
|
+
const repository = injectRepository(AttributeEntity);
|
|
74
|
+
const e1 = await repository.insert(Object.assign(new AttributeEntity(), {
|
|
75
|
+
name: 'E1',
|
|
76
|
+
metadata: { attributes: { initial: true } },
|
|
77
|
+
}));
|
|
78
|
+
const deleted = await repository.delete(e1.id, undefined, { attributes: { deleted: true } });
|
|
79
|
+
expect(deleted.metadata.attributes).toEqual({ initial: true, deleted: true });
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
test('should support querying by raw SQL on attributes', async () => {
|
|
83
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('attribute_entities')} CASCADE`);
|
|
84
|
+
await runInInjectionContext(injector, async () => {
|
|
85
|
+
const repository = injectRepository(AttributeEntity);
|
|
86
|
+
await repository.insert(Object.assign(new AttributeEntity(), {
|
|
87
|
+
name: 'E1',
|
|
88
|
+
metadata: { attributes: { key: 'value1' } },
|
|
89
|
+
}));
|
|
90
|
+
await repository.insert(Object.assign(new AttributeEntity(), {
|
|
91
|
+
name: 'E2',
|
|
92
|
+
metadata: { attributes: { key: 'value2' } },
|
|
93
|
+
}));
|
|
94
|
+
const results = await repository.loadManyByQuery(sql `attributes->>'key' = 'value1'`);
|
|
95
|
+
expect(results).toHaveLength(1);
|
|
96
|
+
expect(results[0].name).toBe('E1');
|
|
97
|
+
});
|
|
98
|
+
});
|
|
99
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
2
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
3
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
4
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
5
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6
|
+
};
|
|
7
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
8
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
9
|
+
};
|
|
10
|
+
import { sql } from 'drizzle-orm';
|
|
11
|
+
import { beforeAll, describe, expect, test } from 'vitest';
|
|
12
|
+
import { Injector, runInInjectionContext } from '../../injector/index.js';
|
|
13
|
+
import { Integer, StringProperty } from '../../schema/index.js';
|
|
14
|
+
import { GeneratedTsVector, Table } from '../decorators.js';
|
|
15
|
+
import { Entity } from '../entity.js';
|
|
16
|
+
import { configureOrm, Database } from '../server/index.js';
|
|
17
|
+
import { injectRepository } from '../server/repository.js';
|
|
18
|
+
describe('ORM Repository Comprehensive Options & Edge Cases', () => {
|
|
19
|
+
let injector;
|
|
20
|
+
let db;
|
|
21
|
+
const schema = 'test_orm_comprehensive';
|
|
22
|
+
let ComprehensiveEntity = class ComprehensiveEntity extends Entity {
|
|
23
|
+
name;
|
|
24
|
+
category;
|
|
25
|
+
value;
|
|
26
|
+
searchVector;
|
|
27
|
+
};
|
|
28
|
+
__decorate([
|
|
29
|
+
StringProperty(),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], ComprehensiveEntity.prototype, "name", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
StringProperty(),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], ComprehensiveEntity.prototype, "category", void 0);
|
|
36
|
+
__decorate([
|
|
37
|
+
Integer(),
|
|
38
|
+
__metadata("design:type", Number)
|
|
39
|
+
], ComprehensiveEntity.prototype, "value", void 0);
|
|
40
|
+
__decorate([
|
|
41
|
+
StringProperty(),
|
|
42
|
+
GeneratedTsVector({ sources: ['name', 'category'] }),
|
|
43
|
+
__metadata("design:type", Object)
|
|
44
|
+
], ComprehensiveEntity.prototype, "searchVector", void 0);
|
|
45
|
+
ComprehensiveEntity = __decorate([
|
|
46
|
+
Table('comprehensive_entities', { schema })
|
|
47
|
+
], ComprehensiveEntity);
|
|
48
|
+
beforeAll(async () => {
|
|
49
|
+
injector = new Injector('Test');
|
|
50
|
+
configureOrm({
|
|
51
|
+
repositoryConfig: { schema },
|
|
52
|
+
connection: {
|
|
53
|
+
host: '127.0.0.1', port: 5432, user: 'tstdl', password: 'wf7rq6glrk5jykne', database: 'tstdl',
|
|
54
|
+
},
|
|
55
|
+
});
|
|
56
|
+
db = injector.resolve(Database);
|
|
57
|
+
await db.execute(sql `CREATE SCHEMA IF NOT EXISTS ${sql.identifier(schema)}`);
|
|
58
|
+
await db.execute(sql `DROP TABLE IF EXISTS ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
59
|
+
await db.execute(sql `
|
|
60
|
+
CREATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} (
|
|
61
|
+
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
|
62
|
+
name TEXT NOT NULL,
|
|
63
|
+
category TEXT NOT NULL,
|
|
64
|
+
value INTEGER NOT NULL,
|
|
65
|
+
search_vector tsvector GENERATED ALWAYS AS (to_tsvector('simple', name || ' ' || category)) STORED,
|
|
66
|
+
revision INTEGER NOT NULL,
|
|
67
|
+
revision_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
68
|
+
create_timestamp TIMESTAMP WITH TIME ZONE NOT NULL,
|
|
69
|
+
delete_timestamp TIMESTAMP WITH TIME ZONE,
|
|
70
|
+
attributes JSONB NOT NULL DEFAULT '{}'
|
|
71
|
+
)
|
|
72
|
+
`);
|
|
73
|
+
await db.execute(sql `CREATE INDEX idx_comprehensive_search ON ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} USING GIN (search_vector)`);
|
|
74
|
+
});
|
|
75
|
+
test('should support DISTINCT ON loading', async () => {
|
|
76
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
77
|
+
await runInInjectionContext(injector, async () => {
|
|
78
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
79
|
+
await repository.insertMany([
|
|
80
|
+
Object.assign(new ComprehensiveEntity(), { name: 'A1', category: 'Cat1', value: 10 }),
|
|
81
|
+
Object.assign(new ComprehensiveEntity(), { name: 'A2', category: 'Cat1', value: 20 }),
|
|
82
|
+
Object.assign(new ComprehensiveEntity(), { name: 'B1', category: 'Cat2', value: 30 }),
|
|
83
|
+
]);
|
|
84
|
+
// Load distinct by category, order by category and value desc to get the one with highest value per category
|
|
85
|
+
const results = await repository.loadManyByQuery({}, {
|
|
86
|
+
distinct: ['category'],
|
|
87
|
+
order: [['category', 'asc'], ['value', 'desc']],
|
|
88
|
+
});
|
|
89
|
+
expect(results).toHaveLength(2);
|
|
90
|
+
expect(results.find((r) => r.category === 'Cat1').name).toBe('A2'); // Value 20 > 10
|
|
91
|
+
expect(results.find((r) => r.category === 'Cat2').name).toBe('B1');
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
test('should support search with filter and distinct', async () => {
|
|
95
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
96
|
+
await runInInjectionContext(injector, async () => {
|
|
97
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
98
|
+
await repository.insertMany([
|
|
99
|
+
Object.assign(new ComprehensiveEntity(), { name: 'Apple One', category: 'Fruit', value: 5 }),
|
|
100
|
+
Object.assign(new ComprehensiveEntity(), { name: 'Apple Two', category: 'Fruit', value: 15 }),
|
|
101
|
+
Object.assign(new ComprehensiveEntity(), { name: 'Apple Three', category: 'Tech', value: 25 }),
|
|
102
|
+
]);
|
|
103
|
+
// Search "Apple", filter value > 10, distinct category
|
|
104
|
+
const results = await repository.search({
|
|
105
|
+
query: { $tsvector: { fields: ['searchVector'], query: 'Apple' } },
|
|
106
|
+
filter: { value: { $gt: 10 } },
|
|
107
|
+
distinct: ['category'],
|
|
108
|
+
order: [['category', 'asc'], ['value', 'desc']], // Distinct on requires matching order prefix
|
|
109
|
+
});
|
|
110
|
+
expect(results).toHaveLength(2);
|
|
111
|
+
const categories = results.map((r) => r.entity.category).sort();
|
|
112
|
+
expect(categories).toEqual(['Fruit', 'Tech']);
|
|
113
|
+
const fruit = results.find((r) => r.entity.category === 'Fruit');
|
|
114
|
+
expect(fruit.entity.name).toBe('Apple Two'); // Value 15 > 10, Apple One filtered out or lower
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
test('should handle soft deletes correctly in updates', async () => {
|
|
118
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
119
|
+
await runInInjectionContext(injector, async () => {
|
|
120
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
121
|
+
const entity = await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'To Delete', category: 'Test', value: 1 }));
|
|
122
|
+
await repository.delete(entity.id);
|
|
123
|
+
// Try update without withDeleted (should fail/return undefined)
|
|
124
|
+
const tryUpdateResult = await repository.tryUpdate(entity.id, { name: 'Resurrected?' });
|
|
125
|
+
expect(tryUpdateResult).toBeUndefined();
|
|
126
|
+
// Ensure DB is unchanged
|
|
127
|
+
const row = await repository.tryLoad(entity.id, { withDeleted: true });
|
|
128
|
+
expect(row.name).toBe('To Delete');
|
|
129
|
+
// Update WITH withDeleted (should work)
|
|
130
|
+
const updateResult = await repository.update(entity.id, { name: 'Zombie' }, { withDeleted: true });
|
|
131
|
+
expect(updateResult.name).toBe('Zombie');
|
|
132
|
+
expect(updateResult.metadata.deleteTimestamp).not.toBeNull(); // Still deleted
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
test('should handle count with and without deleted', async () => {
|
|
136
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
137
|
+
await runInInjectionContext(injector, async () => {
|
|
138
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
139
|
+
await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'Alive', category: 'A', value: 1 }));
|
|
140
|
+
const dead = await repository.insert(Object.assign(new ComprehensiveEntity(), { name: 'Dead', category: 'A', value: 2 }));
|
|
141
|
+
await repository.delete(dead.id);
|
|
142
|
+
expect(await repository.count()).toBe(1);
|
|
143
|
+
expect(await repository.count({ withDeleted: true })).toBe(2);
|
|
144
|
+
expect(await repository.countByQuery({ category: 'A' })).toBe(1);
|
|
145
|
+
expect(await repository.countByQuery({ category: 'A' }, { withDeleted: true })).toBe(2);
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
test('should fail gracefully (return undefined) for missing entities in tryLoad operations', async () => {
|
|
149
|
+
await runInInjectionContext(injector, async () => {
|
|
150
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
151
|
+
expect(await repository.tryLoad('00000000-0000-0000-0000-000000000000')).toBeUndefined();
|
|
152
|
+
expect(await repository.tryLoadByQuery({ name: 'NonExistent' })).toBeUndefined();
|
|
153
|
+
});
|
|
154
|
+
});
|
|
155
|
+
test('should return empty array for empty search results', async () => {
|
|
156
|
+
await runInInjectionContext(injector, async () => {
|
|
157
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
158
|
+
const results = await repository.search({ query: { $tsvector: { fields: ['name'], query: 'NonExistentTerm' } } });
|
|
159
|
+
expect(results).toEqual([]);
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
test('should support complex ordering with multiple fields', async () => {
|
|
163
|
+
await db.execute(sql `TRUNCATE TABLE ${sql.identifier(schema)}.${sql.identifier('comprehensive_entities')} CASCADE`);
|
|
164
|
+
await runInInjectionContext(injector, async () => {
|
|
165
|
+
const repository = injectRepository(ComprehensiveEntity);
|
|
166
|
+
await repository.insertMany([
|
|
167
|
+
Object.assign(new ComprehensiveEntity(), { name: 'A', category: 'X', value: 1 }),
|
|
168
|
+
Object.assign(new ComprehensiveEntity(), { name: 'B', category: 'X', value: 2 }),
|
|
169
|
+
Object.assign(new ComprehensiveEntity(), { name: 'A', category: 'Y', value: 3 }),
|
|
170
|
+
]);
|
|
171
|
+
// Order by Name ASC, then Value DESC
|
|
172
|
+
const results = await repository.loadAll({
|
|
173
|
+
order: [['name', 'asc'], ['value', 'desc']],
|
|
174
|
+
});
|
|
175
|
+
expect(results).toHaveLength(3);
|
|
176
|
+
expect(results[0].name).toBe('A');
|
|
177
|
+
expect(results[0].value).toBe(3); // Y (3) comes before X (1) because both are A? No, wait.
|
|
178
|
+
// Name A (Y, 3) and Name A (X, 1).
|
|
179
|
+
// Order: Name ASC. A, A, B.
|
|
180
|
+
// Tie-break Name: Value DESC. 3, 1.
|
|
181
|
+
// So A(3) then A(1).
|
|
182
|
+
expect(results[1].name).toBe('A');
|
|
183
|
+
expect(results[1].value).toBe(1);
|
|
184
|
+
expect(results[2].name).toBe('B');
|
|
185
|
+
});
|
|
186
|
+
});
|
|
187
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|