mongodb-dynamic-api 4.8.0 → 4.10.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/CHANGELOG.md +21 -0
- package/README.md +1 -0
- package/package.json +2 -1
- package/src/dynamic-api.module.d.ts +1 -1
- package/src/dynamic-api.module.js +14 -1
- package/src/dynamic-api.module.js.map +1 -1
- package/src/helpers/route-description.helper.js +2 -0
- package/src/helpers/route-description.helper.js.map +1 -1
- package/src/index.d.ts +2 -0
- package/src/index.js +2 -0
- package/src/index.js.map +1 -1
- package/src/interfaces/dynamic-api-custom-route.interface.d.ts +34 -0
- package/src/interfaces/dynamic-api-custom-route.interface.js +3 -0
- package/src/interfaces/dynamic-api-custom-route.interface.js.map +1 -0
- package/src/interfaces/dynamic-api-options.interface.d.ts +2 -0
- package/src/interfaces/dynamic-api-options.interface.js.map +1 -1
- package/src/interfaces/dynamic-api-presence.interface.d.ts +22 -0
- package/src/interfaces/dynamic-api-presence.interface.js +6 -0
- package/src/interfaces/dynamic-api-presence.interface.js.map +1 -0
- package/src/interfaces/dynamic-api-route-type.type.d.ts +1 -1
- package/src/interfaces/index.d.ts +2 -0
- package/src/interfaces/index.js +2 -0
- package/src/interfaces/index.js.map +1 -1
- package/src/modules/presence/adapters/in-memory-presence.adapter.d.ts +10 -0
- package/src/modules/presence/adapters/in-memory-presence.adapter.js +64 -0
- package/src/modules/presence/adapters/in-memory-presence.adapter.js.map +1 -0
- package/src/modules/presence/adapters/redis-presence.adapter.d.ts +17 -0
- package/src/modules/presence/adapters/redis-presence.adapter.js +80 -0
- package/src/modules/presence/adapters/redis-presence.adapter.js.map +1 -0
- package/src/modules/presence/index.d.ts +5 -0
- package/src/modules/presence/index.js +22 -0
- package/src/modules/presence/index.js.map +1 -0
- package/src/modules/presence/presence.controller.d.ts +6 -0
- package/src/modules/presence/presence.controller.js +55 -0
- package/src/modules/presence/presence.controller.js.map +1 -0
- package/src/modules/presence/presence.gateway.d.ts +13 -0
- package/src/modules/presence/presence.gateway.js +79 -0
- package/src/modules/presence/presence.gateway.js.map +1 -0
- package/src/modules/presence/presence.module.d.ts +5 -0
- package/src/modules/presence/presence.module.js +50 -0
- package/src/modules/presence/presence.module.js.map +1 -0
- package/src/routes/custom/create-custom-route-controller.d.ts +7 -0
- package/src/routes/custom/create-custom-route-controller.js +133 -0
- package/src/routes/custom/create-custom-route-controller.js.map +1 -0
- package/src/routes/custom/create-custom-route-gateway.d.ts +6 -0
- package/src/routes/custom/create-custom-route-gateway.js +98 -0
- package/src/routes/custom/create-custom-route-gateway.js.map +1 -0
- package/src/routes/custom/index.d.ts +2 -0
- package/src/routes/custom/index.js +19 -0
- package/src/routes/custom/index.js.map +1 -0
- package/src/routes/index.d.ts +1 -0
- package/src/routes/index.js +1 -0
- package/src/routes/index.js.map +1 -1
- package/src/version.json +1 -1
- package/test/for-feature/custom-routes.e2e-spec.d.ts +1 -0
- package/test/for-feature/custom-routes.e2e-spec.js +275 -0
- package/test/for-feature/custom-routes.e2e-spec.js.map +1 -0
- package/test/for-feature/presence.e2e-spec.d.ts +1 -0
- package/test/for-feature/presence.e2e-spec.js +169 -0
- package/test/for-feature/presence.e2e-spec.js.map +1 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const common_1 = require("@nestjs/common");
|
|
13
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const mongoose_1 = require("@nestjs/mongoose");
|
|
15
|
+
const class_validator_1 = require("class-validator");
|
|
16
|
+
const mongoose_2 = require("mongoose");
|
|
17
|
+
const src_1 = require("../../src");
|
|
18
|
+
const e2e_setup_1 = require("../e2e.setup");
|
|
19
|
+
require("dotenv/config");
|
|
20
|
+
const utils_1 = require("../utils");
|
|
21
|
+
const shared_1 = require("../shared");
|
|
22
|
+
let Conversation = class Conversation extends src_1.BaseEntity {
|
|
23
|
+
};
|
|
24
|
+
__decorate([
|
|
25
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
26
|
+
__metadata("design:type", String)
|
|
27
|
+
], Conversation.prototype, "encryptedContent", void 0);
|
|
28
|
+
__decorate([
|
|
29
|
+
(0, mongoose_1.Prop)({ type: String }),
|
|
30
|
+
__metadata("design:type", String)
|
|
31
|
+
], Conversation.prototype, "wrappedKey", void 0);
|
|
32
|
+
__decorate([
|
|
33
|
+
(0, mongoose_1.Prop)({ type: String }),
|
|
34
|
+
__metadata("design:type", String)
|
|
35
|
+
], Conversation.prototype, "ownerId", void 0);
|
|
36
|
+
Conversation = __decorate([
|
|
37
|
+
(0, src_1.DynamicAPISchemaOptions)({}),
|
|
38
|
+
(0, mongoose_1.Schema)({ collection: 'conversations-custom-routes' })
|
|
39
|
+
], Conversation);
|
|
40
|
+
class UpdateWrappedKeyBody {
|
|
41
|
+
}
|
|
42
|
+
__decorate([
|
|
43
|
+
(0, swagger_1.ApiProperty)(),
|
|
44
|
+
(0, class_validator_1.IsNotEmpty)(),
|
|
45
|
+
(0, class_validator_1.IsString)(),
|
|
46
|
+
__metadata("design:type", String)
|
|
47
|
+
], UpdateWrappedKeyBody.prototype, "wrappedKey", void 0);
|
|
48
|
+
class ConversationPresenter {
|
|
49
|
+
static fromEntity(entity) {
|
|
50
|
+
return { id: entity._id.toString(), wrappedKey: entity.wrappedKey };
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
__decorate([
|
|
54
|
+
(0, swagger_1.ApiProperty)(),
|
|
55
|
+
__metadata("design:type", String)
|
|
56
|
+
], ConversationPresenter.prototype, "id", void 0);
|
|
57
|
+
__decorate([
|
|
58
|
+
(0, swagger_1.ApiProperty)(),
|
|
59
|
+
__metadata("design:type", String)
|
|
60
|
+
], ConversationPresenter.prototype, "wrappedKey", void 0);
|
|
61
|
+
let RejectAllGuard = class RejectAllGuard {
|
|
62
|
+
canActivate(_ctx) {
|
|
63
|
+
throw new common_1.ForbiddenException('Blocked by custom guard');
|
|
64
|
+
}
|
|
65
|
+
};
|
|
66
|
+
RejectAllGuard = __decorate([
|
|
67
|
+
(0, common_1.Injectable)()
|
|
68
|
+
], RejectAllGuard);
|
|
69
|
+
describe('DynamicApiModule forFeature - customRoutes (e2e)', () => {
|
|
70
|
+
beforeEach(() => {
|
|
71
|
+
src_1.DynamicApiModule.state['resetState']();
|
|
72
|
+
e2e_setup_1.handleSocketException.mockClear();
|
|
73
|
+
});
|
|
74
|
+
afterEach(async () => {
|
|
75
|
+
await (0, e2e_setup_1.closeTestingApp)(mongoose_2.default.connections);
|
|
76
|
+
});
|
|
77
|
+
async function seedConversation(ownerId = 'user-1') {
|
|
78
|
+
const model = await (0, utils_1.getModelFromEntity)(Conversation);
|
|
79
|
+
const doc = await model.create({
|
|
80
|
+
encryptedContent: 'encrypted-data',
|
|
81
|
+
wrappedKey: 'old-key',
|
|
82
|
+
ownerId,
|
|
83
|
+
});
|
|
84
|
+
return doc.toObject();
|
|
85
|
+
}
|
|
86
|
+
async function setupApp(customRouteConfig, seed) {
|
|
87
|
+
await (0, shared_1.initApp)({
|
|
88
|
+
entity: Conversation,
|
|
89
|
+
controllerOptions: {
|
|
90
|
+
path: 'conversations',
|
|
91
|
+
isPublic: true,
|
|
92
|
+
},
|
|
93
|
+
routes: [],
|
|
94
|
+
customRoutes: [customRouteConfig],
|
|
95
|
+
}, {}, seed);
|
|
96
|
+
}
|
|
97
|
+
describe('PATCH /conversations/:id/e2ee-wrapped-keys — happy path', () => {
|
|
98
|
+
it('calls handler with model, params, body and returns updated doc', async () => {
|
|
99
|
+
let seededId;
|
|
100
|
+
await setupApp({
|
|
101
|
+
path: ':id/e2ee-wrapped-keys',
|
|
102
|
+
method: 'PATCH',
|
|
103
|
+
isPublic: true,
|
|
104
|
+
description: 'Update E2EE wrapped key',
|
|
105
|
+
dTOs: { body: UpdateWrappedKeyBody },
|
|
106
|
+
handler: async ({ model, params, body }) => {
|
|
107
|
+
const typed = body;
|
|
108
|
+
return model.findByIdAndUpdate(params.id, { $set: { wrappedKey: typed.wrappedKey } }, { new: true, lean: true });
|
|
109
|
+
},
|
|
110
|
+
}, async (_conn) => {
|
|
111
|
+
const doc = await seedConversation();
|
|
112
|
+
seededId = doc._id.toString();
|
|
113
|
+
});
|
|
114
|
+
const { status, body } = await e2e_setup_1.server
|
|
115
|
+
.patch(`/conversations/${seededId}/e2ee-wrapped-keys`, { wrappedKey: 'new-key-abc' });
|
|
116
|
+
expect(status).toBe(200);
|
|
117
|
+
expect(body.wrappedKey).toBe('new-key-abc');
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('PATCH /conversations/:id/e2ee-wrapped-keys — presenter.fromEntity', () => {
|
|
121
|
+
it('maps result through presenter.fromEntity when present', async () => {
|
|
122
|
+
let seededId;
|
|
123
|
+
await setupApp({
|
|
124
|
+
path: ':id/e2ee-wrapped-keys',
|
|
125
|
+
method: 'PATCH',
|
|
126
|
+
isPublic: true,
|
|
127
|
+
dTOs: {
|
|
128
|
+
body: UpdateWrappedKeyBody,
|
|
129
|
+
presenter: ConversationPresenter,
|
|
130
|
+
},
|
|
131
|
+
handler: async ({ model, params, body }) => {
|
|
132
|
+
const typed = body;
|
|
133
|
+
return model.findByIdAndUpdate(params.id, { $set: { wrappedKey: typed.wrappedKey } }, { new: true, lean: true });
|
|
134
|
+
},
|
|
135
|
+
}, async (_conn) => {
|
|
136
|
+
const doc = await seedConversation();
|
|
137
|
+
seededId = doc._id.toString();
|
|
138
|
+
});
|
|
139
|
+
const { status, body } = await e2e_setup_1.server
|
|
140
|
+
.patch(`/conversations/${seededId}/e2ee-wrapped-keys`, { wrappedKey: 'mapped-key' });
|
|
141
|
+
expect(status).toBe(200);
|
|
142
|
+
expect(body).toHaveProperty('id');
|
|
143
|
+
expect(body).toHaveProperty('wrappedKey', 'mapped-key');
|
|
144
|
+
expect(body).not.toHaveProperty('encryptedContent');
|
|
145
|
+
});
|
|
146
|
+
});
|
|
147
|
+
describe('PATCH /conversations/:id/e2ee-wrapped-keys — custom guard rejects', () => {
|
|
148
|
+
it('returns 403 when a custom guard throws ForbiddenException', async () => {
|
|
149
|
+
let seededId;
|
|
150
|
+
await setupApp({
|
|
151
|
+
path: ':id/e2ee-wrapped-keys',
|
|
152
|
+
method: 'PATCH',
|
|
153
|
+
isPublic: true,
|
|
154
|
+
guards: [RejectAllGuard],
|
|
155
|
+
handler: async () => ({ done: true }),
|
|
156
|
+
}, async (_conn) => {
|
|
157
|
+
const doc = await seedConversation();
|
|
158
|
+
seededId = doc._id.toString();
|
|
159
|
+
});
|
|
160
|
+
const { status } = await e2e_setup_1.server
|
|
161
|
+
.patch(`/conversations/${seededId}/e2ee-wrapped-keys`, { wrappedKey: 'any-key' });
|
|
162
|
+
expect(status).toBe(403);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
describe('PATCH /conversations/:id/e2ee-wrapped-keys — abilityPredicate fails', () => {
|
|
166
|
+
it('returns 403 when abilityPredicate returns false for the document', async () => {
|
|
167
|
+
let seededId;
|
|
168
|
+
await setupApp({
|
|
169
|
+
path: ':id/e2ee-wrapped-keys',
|
|
170
|
+
method: 'PATCH',
|
|
171
|
+
isPublic: true,
|
|
172
|
+
abilityPredicate: (entity) => entity.ownerId === 'allowed-user',
|
|
173
|
+
handler: async () => ({ done: true }),
|
|
174
|
+
}, async (_conn) => {
|
|
175
|
+
const doc = await seedConversation('user-1');
|
|
176
|
+
seededId = doc._id.toString();
|
|
177
|
+
});
|
|
178
|
+
const { status } = await e2e_setup_1.server
|
|
179
|
+
.patch(`/conversations/${seededId}/e2ee-wrapped-keys`, { wrappedKey: 'any-key' });
|
|
180
|
+
expect(status).toBe(403);
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
describe('GET /conversations/metadata — GET custom route', () => {
|
|
184
|
+
it('returns handler result for GET method', async () => {
|
|
185
|
+
await setupApp({
|
|
186
|
+
path: 'metadata',
|
|
187
|
+
method: 'GET',
|
|
188
|
+
isPublic: true,
|
|
189
|
+
handler: async () => ({ version: '1.0', algorithm: 'AES-256' }),
|
|
190
|
+
});
|
|
191
|
+
const { status, body } = await e2e_setup_1.server.get('/conversations/metadata');
|
|
192
|
+
expect(status).toBe(200);
|
|
193
|
+
expect(body).toEqual({ version: '1.0', algorithm: 'AES-256' });
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
describe('POST /conversations/bulk-wrap — POST custom route', () => {
|
|
197
|
+
it('returns handler result for POST method', async () => {
|
|
198
|
+
await setupApp({
|
|
199
|
+
path: 'bulk-wrap',
|
|
200
|
+
method: 'POST',
|
|
201
|
+
isPublic: true,
|
|
202
|
+
handler: async ({ body }) => {
|
|
203
|
+
const typed = body;
|
|
204
|
+
return { wrapped: typed.keys.length };
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
const { status, body } = await e2e_setup_1.server
|
|
208
|
+
.post('/conversations/bulk-wrap', { keys: ['k1', 'k2', 'k3'] });
|
|
209
|
+
expect(status).toBe(201);
|
|
210
|
+
expect(body).toEqual({ wrapped: 3 });
|
|
211
|
+
});
|
|
212
|
+
});
|
|
213
|
+
describe('WebSocket customRoute — happy path', () => {
|
|
214
|
+
it('receives handler result via WS event', async () => {
|
|
215
|
+
await (0, shared_1.initApp)({
|
|
216
|
+
entity: Conversation,
|
|
217
|
+
controllerOptions: { path: 'conversations', isPublic: true },
|
|
218
|
+
routes: [],
|
|
219
|
+
customRoutes: [
|
|
220
|
+
{
|
|
221
|
+
path: 'metadata',
|
|
222
|
+
method: 'GET',
|
|
223
|
+
isPublic: true,
|
|
224
|
+
webSocket: true,
|
|
225
|
+
handler: async () => ({ version: '2.0', algo: 'AES-256' }),
|
|
226
|
+
},
|
|
227
|
+
],
|
|
228
|
+
}, {}, undefined, async (app) => { app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app)); });
|
|
229
|
+
const result = await e2e_setup_1.server.emit('custom-metadata-conversation', undefined);
|
|
230
|
+
expect(result).toEqual({ version: '2.0', algo: 'AES-256' });
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
describe('WebSocket customRoute — custom eventName', () => {
|
|
234
|
+
it('subscribes to the custom event name', async () => {
|
|
235
|
+
await (0, shared_1.initApp)({
|
|
236
|
+
entity: Conversation,
|
|
237
|
+
controllerOptions: { path: 'conversations', isPublic: true },
|
|
238
|
+
routes: [],
|
|
239
|
+
customRoutes: [
|
|
240
|
+
{
|
|
241
|
+
path: 'ping',
|
|
242
|
+
method: 'GET',
|
|
243
|
+
isPublic: true,
|
|
244
|
+
webSocket: true,
|
|
245
|
+
eventName: 'my-custom-ping',
|
|
246
|
+
handler: async () => ({ pong: true }),
|
|
247
|
+
},
|
|
248
|
+
],
|
|
249
|
+
}, {}, undefined, async (app) => { app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app)); });
|
|
250
|
+
const result = await e2e_setup_1.server.emit('my-custom-ping', undefined);
|
|
251
|
+
expect(result).toEqual({ pong: true });
|
|
252
|
+
});
|
|
253
|
+
});
|
|
254
|
+
describe('WebSocket customRoute — Unauthorized', () => {
|
|
255
|
+
it('emits WsException when no access token is provided', async () => {
|
|
256
|
+
await (0, shared_1.initApp)({
|
|
257
|
+
entity: Conversation,
|
|
258
|
+
controllerOptions: { path: 'conversations' },
|
|
259
|
+
routes: [],
|
|
260
|
+
customRoutes: [
|
|
261
|
+
{
|
|
262
|
+
path: 'secret',
|
|
263
|
+
method: 'GET',
|
|
264
|
+
isPublic: false,
|
|
265
|
+
webSocket: true,
|
|
266
|
+
handler: async () => ({ ok: true }),
|
|
267
|
+
},
|
|
268
|
+
],
|
|
269
|
+
}, {}, undefined, async (app) => { app.useWebSocketAdapter(new e2e_setup_1.TestSocketAdapter(app)); });
|
|
270
|
+
await e2e_setup_1.server.emit('custom-secret-conversation', undefined);
|
|
271
|
+
expect(e2e_setup_1.handleSocketException).toHaveBeenCalledWith(expect.objectContaining({ message: 'Unauthorized' }));
|
|
272
|
+
});
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
//# sourceMappingURL=custom-routes.e2e-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"custom-routes.e2e-spec.js","sourceRoot":"","sources":["../../../libs/dynamic-api/test/for-feature/custom-routes.e2e-spec.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import 'dotenv/config';
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
const mongoose_1 = require("@nestjs/mongoose");
|
|
13
|
+
const testing_1 = require("@nestjs/testing");
|
|
14
|
+
const socket_io_client_1 = require("socket.io-client");
|
|
15
|
+
const mongoose_2 = require("mongoose");
|
|
16
|
+
require("dotenv/config");
|
|
17
|
+
const src_1 = require("../../src");
|
|
18
|
+
const e2e_setup_1 = require("../e2e.setup");
|
|
19
|
+
let PresenceUserEntity = class PresenceUserEntity extends src_1.BaseEntity {
|
|
20
|
+
};
|
|
21
|
+
__decorate([
|
|
22
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
23
|
+
__metadata("design:type", String)
|
|
24
|
+
], PresenceUserEntity.prototype, "email", void 0);
|
|
25
|
+
__decorate([
|
|
26
|
+
(0, mongoose_1.Prop)({ type: String, required: true }),
|
|
27
|
+
__metadata("design:type", String)
|
|
28
|
+
], PresenceUserEntity.prototype, "password", void 0);
|
|
29
|
+
PresenceUserEntity = __decorate([
|
|
30
|
+
(0, mongoose_1.Schema)({ collection: 'presence-users' })
|
|
31
|
+
], PresenceUserEntity);
|
|
32
|
+
const DEFAULT_TIMEOUT_MS = 6000;
|
|
33
|
+
async function connectSocket(baseUrl, accessToken) {
|
|
34
|
+
return new Promise((resolve, reject) => {
|
|
35
|
+
const socket = (0, socket_io_client_1.io)(baseUrl, {
|
|
36
|
+
query: accessToken ? { accessToken } : {},
|
|
37
|
+
reconnection: false,
|
|
38
|
+
});
|
|
39
|
+
const timeout = setTimeout(() => {
|
|
40
|
+
socket.close();
|
|
41
|
+
reject(new Error('Socket connection timed out'));
|
|
42
|
+
}, DEFAULT_TIMEOUT_MS);
|
|
43
|
+
socket.once('connect', () => {
|
|
44
|
+
clearTimeout(timeout);
|
|
45
|
+
resolve(socket);
|
|
46
|
+
});
|
|
47
|
+
socket.once('connect_error', (err) => {
|
|
48
|
+
clearTimeout(timeout);
|
|
49
|
+
reject(err);
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
function waitForEvent(socket, event, timeoutMs = DEFAULT_TIMEOUT_MS) {
|
|
54
|
+
return new Promise((resolve, reject) => {
|
|
55
|
+
const timeout = setTimeout(() => reject(new Error(`Timeout waiting for "${event}" after ${timeoutMs}ms`)), timeoutMs);
|
|
56
|
+
socket.once(event, (data) => {
|
|
57
|
+
clearTimeout(timeout);
|
|
58
|
+
resolve(data);
|
|
59
|
+
});
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
const delay = (ms) => new Promise((r) => setTimeout(r, ms));
|
|
63
|
+
describe('DynamicApiPresenceModule (e2e)', () => {
|
|
64
|
+
jest.setTimeout(20000);
|
|
65
|
+
let accessToken;
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
src_1.DynamicApiModule.state['resetState']();
|
|
68
|
+
});
|
|
69
|
+
afterEach(async () => {
|
|
70
|
+
await (0, e2e_setup_1.closeTestingApp)(mongoose_2.default.connections);
|
|
71
|
+
});
|
|
72
|
+
async function initPresenceApp(enableController = false) {
|
|
73
|
+
const uri = process.env.MONGO_DB_URL;
|
|
74
|
+
const moduleRef = await testing_1.Test.createTestingModule({
|
|
75
|
+
imports: [
|
|
76
|
+
src_1.DynamicApiModule.forRoot(uri, {
|
|
77
|
+
useAuth: {
|
|
78
|
+
userEntity: PresenceUserEntity,
|
|
79
|
+
jwt: { secret: 'presence-e2e-secret', expiresIn: '1h' },
|
|
80
|
+
login: {},
|
|
81
|
+
webSocket: true,
|
|
82
|
+
},
|
|
83
|
+
}),
|
|
84
|
+
src_1.DynamicApiPresenceModule.register({
|
|
85
|
+
adapter: 'memory',
|
|
86
|
+
enableController,
|
|
87
|
+
}),
|
|
88
|
+
],
|
|
89
|
+
}).compile();
|
|
90
|
+
return (0, e2e_setup_1.createTestingApp)(moduleRef, undefined, async (app) => {
|
|
91
|
+
(0, src_1.enableDynamicAPIWebSockets)(app);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
async function registerAndLogin(email = 'presence@test.co', password = 'P@ssw0rd!') {
|
|
95
|
+
await e2e_setup_1.server.post('/auth/register', { email, password });
|
|
96
|
+
const { body } = await e2e_setup_1.server.post('/auth/login', { email, password });
|
|
97
|
+
return body.accessToken;
|
|
98
|
+
}
|
|
99
|
+
it('should emit user:online when an authenticated socket connects', async () => {
|
|
100
|
+
await initPresenceApp();
|
|
101
|
+
accessToken = await registerAndLogin();
|
|
102
|
+
const observer = await connectSocket(global.appBaseUrl);
|
|
103
|
+
const onlinePromise = waitForEvent(observer, 'user:online');
|
|
104
|
+
const authenticated = await connectSocket(global.appBaseUrl, accessToken);
|
|
105
|
+
const payload = await onlinePromise;
|
|
106
|
+
expect(payload).toMatchObject({ userId: expect.any(String) });
|
|
107
|
+
authenticated.disconnect();
|
|
108
|
+
observer.disconnect();
|
|
109
|
+
});
|
|
110
|
+
it('should emit user:offline when the last authenticated socket disconnects', async () => {
|
|
111
|
+
await initPresenceApp();
|
|
112
|
+
accessToken = await registerAndLogin();
|
|
113
|
+
const observer = await connectSocket(global.appBaseUrl);
|
|
114
|
+
const onlinePromise = waitForEvent(observer, 'user:online', 5000);
|
|
115
|
+
const authenticated = await connectSocket(global.appBaseUrl, accessToken);
|
|
116
|
+
await onlinePromise;
|
|
117
|
+
const offlinePromise = waitForEvent(observer, 'user:offline', 5000);
|
|
118
|
+
authenticated.disconnect();
|
|
119
|
+
const payload = await offlinePromise;
|
|
120
|
+
expect(payload).toMatchObject({ userId: expect.any(String) });
|
|
121
|
+
observer.disconnect();
|
|
122
|
+
});
|
|
123
|
+
it('should NOT emit user:offline while the user has another active socket (multi-tab)', async () => {
|
|
124
|
+
await initPresenceApp();
|
|
125
|
+
accessToken = await registerAndLogin();
|
|
126
|
+
const observer = await connectSocket(global.appBaseUrl);
|
|
127
|
+
const tab1 = await connectSocket(global.appBaseUrl, accessToken);
|
|
128
|
+
const tab2 = await connectSocket(global.appBaseUrl, accessToken);
|
|
129
|
+
await delay(300);
|
|
130
|
+
let offlineReceived = false;
|
|
131
|
+
observer.on('user:offline', () => { offlineReceived = true; });
|
|
132
|
+
tab1.disconnect();
|
|
133
|
+
await delay(500);
|
|
134
|
+
expect(offlineReceived).toBe(false);
|
|
135
|
+
tab2.disconnect();
|
|
136
|
+
observer.disconnect();
|
|
137
|
+
});
|
|
138
|
+
it('should NOT emit user:online for anonymous (unauthenticated) connections', async () => {
|
|
139
|
+
await initPresenceApp();
|
|
140
|
+
const observer = await connectSocket(global.appBaseUrl);
|
|
141
|
+
let onlineReceived = false;
|
|
142
|
+
observer.on('user:online', () => { onlineReceived = true; });
|
|
143
|
+
const anon = await connectSocket(global.appBaseUrl);
|
|
144
|
+
await delay(300);
|
|
145
|
+
expect(onlineReceived).toBe(false);
|
|
146
|
+
anon.disconnect();
|
|
147
|
+
observer.disconnect();
|
|
148
|
+
});
|
|
149
|
+
it('GET /presence should return online user IDs when enableController is true', async () => {
|
|
150
|
+
await initPresenceApp(true);
|
|
151
|
+
accessToken = await registerAndLogin();
|
|
152
|
+
const observer = await connectSocket(global.appBaseUrl);
|
|
153
|
+
const onlinePromise = waitForEvent(observer, 'user:online', 5000);
|
|
154
|
+
const authenticated = await connectSocket(global.appBaseUrl, accessToken);
|
|
155
|
+
await onlinePromise;
|
|
156
|
+
const res = await e2e_setup_1.server.get('/presence');
|
|
157
|
+
const onlineIds = res.body?.onlineUserIds ?? [];
|
|
158
|
+
expect(onlineIds.length).toBeGreaterThan(0);
|
|
159
|
+
authenticated.disconnect();
|
|
160
|
+
observer.disconnect();
|
|
161
|
+
});
|
|
162
|
+
it('GET /presence?room=nonexistent should return empty array', async () => {
|
|
163
|
+
await initPresenceApp(true);
|
|
164
|
+
const res = await e2e_setup_1.server.get('/presence', { query: { room: 'nonexistent-room' } });
|
|
165
|
+
const onlineIds = res.body?.onlineUserIds ?? [];
|
|
166
|
+
expect(onlineIds).toEqual([]);
|
|
167
|
+
});
|
|
168
|
+
});
|
|
169
|
+
//# sourceMappingURL=presence.e2e-spec.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"presence.e2e-spec.js","sourceRoot":"","sources":["../../../libs/dynamic-api/test/for-feature/presence.e2e-spec.ts"],"names":[],"mappings":"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"}
|