@eratu/common 1.0.17 → 1.0.19

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.
Files changed (117) hide show
  1. package/build/__tests__/enum/roles.test.d.ts +1 -0
  2. package/build/__tests__/enum/roles.test.js +31 -0
  3. package/build/__tests__/errors/bad-request-error.test.d.ts +1 -0
  4. package/build/__tests__/errors/bad-request-error.test.js +30 -0
  5. package/build/__tests__/errors/database-connection-error.test.d.ts +1 -0
  6. package/build/__tests__/errors/database-connection-error.test.js +28 -0
  7. package/build/__tests__/errors/forbidden-error.test.d.ts +1 -0
  8. package/build/__tests__/errors/forbidden-error.test.js +39 -0
  9. package/build/__tests__/errors/not-authorized-error.test.d.ts +1 -0
  10. package/build/__tests__/errors/not-authorized-error.test.js +39 -0
  11. package/build/__tests__/errors/not-found-error.test.d.ts +1 -0
  12. package/build/__tests__/errors/not-found-error.test.js +39 -0
  13. package/build/__tests__/errors/request-validation-error.test.d.ts +1 -0
  14. package/build/__tests__/errors/request-validation-error.test.js +101 -0
  15. package/build/__tests__/events/base-listener.test.d.ts +1 -0
  16. package/build/__tests__/events/base-listener.test.js +222 -0
  17. package/build/__tests__/events/base-publisher.test.d.ts +1 -0
  18. package/build/__tests__/events/base-publisher.test.js +85 -0
  19. package/build/__tests__/events/streams.test.d.ts +1 -0
  20. package/build/__tests__/events/streams.test.js +24 -0
  21. package/build/__tests__/events/subjects.test.d.ts +1 -0
  22. package/build/__tests__/events/subjects.test.js +79 -0
  23. package/build/__tests__/middlewares/allow-roles.test.d.ts +1 -0
  24. package/build/__tests__/middlewares/allow-roles.test.js +112 -0
  25. package/build/__tests__/middlewares/current-user.test.d.ts +1 -0
  26. package/build/__tests__/middlewares/current-user.test.js +261 -0
  27. package/build/__tests__/middlewares/error-handler.test.d.ts +1 -0
  28. package/build/__tests__/middlewares/error-handler.test.js +102 -0
  29. package/build/__tests__/middlewares/require-auth.test.d.ts +1 -0
  30. package/build/__tests__/middlewares/require-auth.test.js +57 -0
  31. package/build/__tests__/middlewares/validation.test.d.ts +1 -0
  32. package/build/__tests__/middlewares/validation.test.js +198 -0
  33. package/build/__tests__/nats-wrapper.test.d.ts +1 -0
  34. package/build/__tests__/nats-wrapper.test.js +158 -0
  35. package/build/__tests__/redis-wrapper.test.d.ts +1 -0
  36. package/build/__tests__/redis-wrapper.test.js +158 -0
  37. package/build/enum/roles.d.ts +6 -0
  38. package/build/enum/roles.js +10 -0
  39. package/build/errors/bad-request-error.d.ts +9 -0
  40. package/build/errors/bad-request-error.js +16 -0
  41. package/build/errors/custom-error.d.ts +8 -0
  42. package/build/errors/custom-error.js +10 -0
  43. package/build/errors/database-connection-error.d.ts +9 -0
  44. package/build/errors/database-connection-error.js +16 -0
  45. package/build/errors/forbidden-error.d.ts +9 -0
  46. package/build/errors/forbidden-error.js +16 -0
  47. package/build/errors/not-authorized-error.d.ts +8 -0
  48. package/build/errors/not-authorized-error.js +15 -0
  49. package/build/errors/not-found-error.d.ts +9 -0
  50. package/build/errors/not-found-error.js +16 -0
  51. package/build/errors/request-validation-error.d.ts +14 -0
  52. package/build/errors/request-validation-error.js +22 -0
  53. package/build/events/event-types/auth/admin-deleted-event.d.ts +11 -0
  54. package/build/events/event-types/auth/admin-deleted-event.js +2 -0
  55. package/build/events/event-types/auth/admin-signed-up-event.d.ts +13 -0
  56. package/build/events/event-types/auth/admin-signed-up-event.js +2 -0
  57. package/build/events/event-types/auth/admin-updated-event.d.ts +14 -0
  58. package/build/events/event-types/auth/admin-updated-event.js +2 -0
  59. package/build/events/event-types/auth/author-deleted-event.d.ts +11 -0
  60. package/build/events/event-types/auth/author-deleted-event.js +2 -0
  61. package/build/events/event-types/auth/author-signed-in-event.d.ts +12 -0
  62. package/build/events/event-types/auth/author-signed-in-event.js +2 -0
  63. package/build/events/event-types/auth/author-signed-out-event.d.ts +11 -0
  64. package/build/events/event-types/auth/author-signed-out-event.js +2 -0
  65. package/build/events/event-types/auth/author-signed-up-event.d.ts +13 -0
  66. package/build/events/event-types/auth/author-signed-up-event.js +2 -0
  67. package/build/events/event-types/auth/author-updated-event.d.ts +14 -0
  68. package/build/events/event-types/auth/author-updated-event.js +2 -0
  69. package/build/events/event-types/auth/author-verified-event.d.ts +14 -0
  70. package/build/events/event-types/auth/author-verified-event.js +2 -0
  71. package/build/events/event-types/auth/reader-deleted-event.d.ts +11 -0
  72. package/build/events/event-types/auth/reader-deleted-event.js +2 -0
  73. package/build/events/event-types/auth/reader-signed-in-event.d.ts +13 -0
  74. package/build/events/event-types/auth/reader-signed-in-event.js +2 -0
  75. package/build/events/event-types/auth/reader-signed-out-event.d.ts +11 -0
  76. package/build/events/event-types/auth/reader-signed-out-event.js +2 -0
  77. package/build/events/event-types/auth/reader-signed-up-event.d.ts +13 -0
  78. package/build/events/event-types/auth/reader-signed-up-event.js +2 -0
  79. package/build/events/event-types/auth/reader-updated-event.d.ts +14 -0
  80. package/build/events/event-types/auth/reader-updated-event.js +2 -0
  81. package/build/events/event-types/auth/reader-verified-event.d.ts +14 -0
  82. package/build/events/event-types/auth/reader-verified-event.js +2 -0
  83. package/build/events/event-types/books/book-created-event.d.ts +11 -0
  84. package/build/events/event-types/books/book-created-event.js +2 -0
  85. package/build/events/event-types/books/book-deleted-event.d.ts +10 -0
  86. package/build/events/event-types/books/book-deleted-event.js +2 -0
  87. package/build/events/event-types/books/book-updated-event.d.ts +11 -0
  88. package/build/events/event-types/books/book-updated-event.js +2 -0
  89. package/build/events/event-types/orders/order-created-event.d.ts +12 -0
  90. package/build/events/event-types/orders/order-created-event.js +2 -0
  91. package/build/events/listeners/base-listener.d.ts +23 -0
  92. package/build/events/listeners/base-listener.js +96 -0
  93. package/build/events/publishers/base-publisher.d.ts +14 -0
  94. package/build/events/publishers/base-publisher.js +27 -0
  95. package/build/events/streams.d.ts +7 -0
  96. package/build/events/streams.js +11 -0
  97. package/build/events/subjects.d.ts +25 -0
  98. package/build/events/subjects.js +35 -0
  99. package/build/index.d.ts +38 -0
  100. package/build/index.js +60 -0
  101. package/build/middlewares/allow-roles.d.ts +3 -0
  102. package/build/middlewares/allow-roles.js +18 -0
  103. package/build/middlewares/current-user.d.ts +40 -0
  104. package/build/middlewares/current-user.js +86 -0
  105. package/build/middlewares/error-handler.d.ts +2 -0
  106. package/build/middlewares/error-handler.js +13 -0
  107. package/build/middlewares/require-auth.d.ts +2 -0
  108. package/build/middlewares/require-auth.js +11 -0
  109. package/build/middlewares/validation.d.ts +3 -0
  110. package/build/middlewares/validation.js +57 -0
  111. package/build/nats-wrapper.d.ts +17 -0
  112. package/build/nats-wrapper.js +80 -0
  113. package/build/redis-wrapper.d.ts +20 -0
  114. package/build/redis-wrapper.js +77 -0
  115. package/build/test/setup.d.ts +8 -0
  116. package/build/test/setup.js +45 -0
  117. package/package.json +1 -1
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,31 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const roles_1 = require("../../enum/roles");
4
+ describe('UserRoles Enum', () => {
5
+ it('should have superAdmin with value 1', () => {
6
+ expect(roles_1.UserRoles.superAdmin).toBe(1);
7
+ });
8
+ it('should have admin with value 2', () => {
9
+ expect(roles_1.UserRoles.admin).toBe(2);
10
+ });
11
+ it('should have author with value 3', () => {
12
+ expect(roles_1.UserRoles.author).toBe(3);
13
+ });
14
+ it('should have reader with value 4', () => {
15
+ expect(roles_1.UserRoles.reader).toBe(4);
16
+ });
17
+ it('should have exactly 4 roles', () => {
18
+ const roles = Object.keys(roles_1.UserRoles).filter((key) => isNaN(Number(key)));
19
+ expect(roles.length).toBe(4);
20
+ expect(roles).toContain('superAdmin');
21
+ expect(roles).toContain('admin');
22
+ expect(roles).toContain('author');
23
+ expect(roles).toContain('reader');
24
+ });
25
+ it('should support reverse mapping', () => {
26
+ expect(roles_1.UserRoles[1]).toBe('superAdmin');
27
+ expect(roles_1.UserRoles[2]).toBe('admin');
28
+ expect(roles_1.UserRoles[3]).toBe('author');
29
+ expect(roles_1.UserRoles[4]).toBe('reader');
30
+ });
31
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,30 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const bad_request_error_1 = require("../../errors/bad-request-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('BadRequestError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new bad_request_error_1.BadRequestError('Test error');
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 400', () => {
12
+ const error = new bad_request_error_1.BadRequestError('Test error');
13
+ expect(error.statusCode).toBe(400);
14
+ });
15
+ it('should store the message', () => {
16
+ const message = 'Invalid email format';
17
+ const error = new bad_request_error_1.BadRequestError(message);
18
+ expect(error.message).toBe(message);
19
+ });
20
+ it('should serialize errors correctly', () => {
21
+ const message = 'Invalid input';
22
+ const error = new bad_request_error_1.BadRequestError(message);
23
+ const serialized = error.serializeErrors();
24
+ expect(serialized).toEqual([{ message }]);
25
+ });
26
+ it('should have correct prototype chain', () => {
27
+ const error = new bad_request_error_1.BadRequestError('Test');
28
+ expect(Object.getPrototypeOf(error)).toBe(bad_request_error_1.BadRequestError.prototype);
29
+ });
30
+ });
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const database_connection_error_1 = require("../../errors/database-connection-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('DatabaseConnectionError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new database_connection_error_1.DatabaseConnectionError();
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 500', () => {
12
+ const error = new database_connection_error_1.DatabaseConnectionError();
13
+ expect(error.statusCode).toBe(500);
14
+ });
15
+ it('should have reason property', () => {
16
+ const error = new database_connection_error_1.DatabaseConnectionError();
17
+ expect(error.reason).toBe('Error connecting to database');
18
+ });
19
+ it('should serialize errors correctly', () => {
20
+ const error = new database_connection_error_1.DatabaseConnectionError();
21
+ const serialized = error.serializeErrors();
22
+ expect(serialized).toEqual([{ message: 'Error connecting to database' }]);
23
+ });
24
+ it('should have correct prototype chain', () => {
25
+ const error = new database_connection_error_1.DatabaseConnectionError();
26
+ expect(Object.getPrototypeOf(error)).toBe(database_connection_error_1.DatabaseConnectionError.prototype);
27
+ });
28
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const forbidden_error_1 = require("../../errors/forbidden-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('ForbiddenError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new forbidden_error_1.ForbiddenError();
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 403', () => {
12
+ const error = new forbidden_error_1.ForbiddenError();
13
+ expect(error.statusCode).toBe(403);
14
+ });
15
+ it('should have default message "Forbidden"', () => {
16
+ const error = new forbidden_error_1.ForbiddenError();
17
+ expect(error.message).toBe('Forbidden');
18
+ });
19
+ it('should accept custom message', () => {
20
+ const customMessage = 'Access denied for this resource';
21
+ const error = new forbidden_error_1.ForbiddenError(customMessage);
22
+ expect(error.message).toBe(customMessage);
23
+ });
24
+ it('should serialize errors correctly with default message', () => {
25
+ const error = new forbidden_error_1.ForbiddenError();
26
+ const serialized = error.serializeErrors();
27
+ expect(serialized).toEqual([{ message: 'Forbidden' }]);
28
+ });
29
+ it('should serialize errors correctly with custom message', () => {
30
+ const customMessage = 'Insufficient permissions';
31
+ const error = new forbidden_error_1.ForbiddenError(customMessage);
32
+ const serialized = error.serializeErrors();
33
+ expect(serialized).toEqual([{ message: customMessage }]);
34
+ });
35
+ it('should have correct prototype chain', () => {
36
+ const error = new forbidden_error_1.ForbiddenError();
37
+ expect(Object.getPrototypeOf(error)).toBe(forbidden_error_1.ForbiddenError.prototype);
38
+ });
39
+ });
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const not_authorized_error_1 = require("../../errors/not-authorized-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('NotAuthorizedError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new not_authorized_error_1.NotAuthorizedError();
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 401', () => {
12
+ const error = new not_authorized_error_1.NotAuthorizedError();
13
+ expect(error.statusCode).toBe(401);
14
+ });
15
+ it('should have default message "Not Authorized"', () => {
16
+ const error = new not_authorized_error_1.NotAuthorizedError();
17
+ expect(error.message).toBe('Not Authorized');
18
+ });
19
+ it('should accept custom message', () => {
20
+ const customMessage = 'Session expired';
21
+ const error = new not_authorized_error_1.NotAuthorizedError(customMessage);
22
+ expect(error.message).toBe(customMessage);
23
+ });
24
+ it('should serialize errors correctly with default message', () => {
25
+ const error = new not_authorized_error_1.NotAuthorizedError();
26
+ const serialized = error.serializeErrors();
27
+ expect(serialized).toEqual([{ message: 'Not Authorized' }]);
28
+ });
29
+ it('should serialize errors correctly with custom message', () => {
30
+ const customMessage = 'Token expired';
31
+ const error = new not_authorized_error_1.NotAuthorizedError(customMessage);
32
+ const serialized = error.serializeErrors();
33
+ expect(serialized).toEqual([{ message: customMessage }]);
34
+ });
35
+ it('should have correct prototype chain', () => {
36
+ const error = new not_authorized_error_1.NotAuthorizedError();
37
+ expect(Object.getPrototypeOf(error)).toBe(not_authorized_error_1.NotAuthorizedError.prototype);
38
+ });
39
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const not_found_error_1 = require("../../errors/not-found-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('NotFoundError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new not_found_error_1.NotFoundError();
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 404', () => {
12
+ const error = new not_found_error_1.NotFoundError();
13
+ expect(error.statusCode).toBe(404);
14
+ });
15
+ it('should have default message "Not Found"', () => {
16
+ const error = new not_found_error_1.NotFoundError();
17
+ expect(error.message).toBe('Not Found');
18
+ });
19
+ it('should accept custom message', () => {
20
+ const customMessage = 'User not found';
21
+ const error = new not_found_error_1.NotFoundError(customMessage);
22
+ expect(error.message).toBe(customMessage);
23
+ });
24
+ it('should serialize errors correctly with default message', () => {
25
+ const error = new not_found_error_1.NotFoundError();
26
+ const serialized = error.serializeErrors();
27
+ expect(serialized).toEqual([{ message: 'Not Found' }]);
28
+ });
29
+ it('should serialize errors correctly with custom message', () => {
30
+ const customMessage = 'Book not found';
31
+ const error = new not_found_error_1.NotFoundError(customMessage);
32
+ const serialized = error.serializeErrors();
33
+ expect(serialized).toEqual([{ message: customMessage }]);
34
+ });
35
+ it('should have correct prototype chain', () => {
36
+ const error = new not_found_error_1.NotFoundError();
37
+ expect(Object.getPrototypeOf(error)).toBe(not_found_error_1.NotFoundError.prototype);
38
+ });
39
+ });
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const request_validation_error_1 = require("../../errors/request-validation-error");
4
+ const custom_error_1 = require("../../errors/custom-error");
5
+ describe('RequestValidationError', () => {
6
+ it('should extend CustomError', () => {
7
+ const error = new request_validation_error_1.RequestValidationError([]);
8
+ expect(error).toBeInstanceOf(custom_error_1.CustomError);
9
+ expect(error).toBeInstanceOf(Error);
10
+ });
11
+ it('should have statusCode of 400', () => {
12
+ const error = new request_validation_error_1.RequestValidationError([]);
13
+ expect(error.statusCode).toBe(400);
14
+ });
15
+ it('should have message "Invalid request parameters"', () => {
16
+ const error = new request_validation_error_1.RequestValidationError([]);
17
+ expect(error.message).toBe('Invalid request parameters');
18
+ });
19
+ it('should serialize field errors correctly', () => {
20
+ const validationErrors = [
21
+ {
22
+ type: 'field',
23
+ msg: 'Email is required',
24
+ path: 'email',
25
+ location: 'body',
26
+ value: '',
27
+ },
28
+ {
29
+ type: 'field',
30
+ msg: 'Password must be at least 8 characters',
31
+ path: 'password',
32
+ location: 'body',
33
+ value: '123',
34
+ },
35
+ ];
36
+ const error = new request_validation_error_1.RequestValidationError(validationErrors);
37
+ const serialized = error.serializeErrors();
38
+ expect(serialized).toEqual([
39
+ { message: 'Email is required', field: 'email' },
40
+ { message: 'Password must be at least 8 characters', field: 'password' },
41
+ ]);
42
+ });
43
+ it('should serialize non-field errors correctly', () => {
44
+ const validationErrors = [
45
+ {
46
+ type: 'alternative',
47
+ msg: 'At least one field is required',
48
+ nestedErrors: [],
49
+ },
50
+ ];
51
+ const error = new request_validation_error_1.RequestValidationError(validationErrors);
52
+ const serialized = error.serializeErrors();
53
+ expect(serialized).toEqual([
54
+ { message: 'At least one field is required' },
55
+ ]);
56
+ });
57
+ it('should serialize mixed errors correctly', () => {
58
+ const validationErrors = [
59
+ {
60
+ type: 'field',
61
+ msg: 'Invalid email',
62
+ path: 'email',
63
+ location: 'body',
64
+ value: 'invalid',
65
+ },
66
+ {
67
+ type: 'alternative_grouped',
68
+ msg: 'Alternative error',
69
+ nestedErrors: [],
70
+ },
71
+ ];
72
+ const error = new request_validation_error_1.RequestValidationError(validationErrors);
73
+ const serialized = error.serializeErrors();
74
+ expect(serialized).toEqual([
75
+ { message: 'Invalid email', field: 'email' },
76
+ { message: 'Alternative error' },
77
+ ]);
78
+ });
79
+ it('should handle empty errors array', () => {
80
+ const error = new request_validation_error_1.RequestValidationError([]);
81
+ const serialized = error.serializeErrors();
82
+ expect(serialized).toEqual([]);
83
+ });
84
+ it('should have correct prototype chain', () => {
85
+ const error = new request_validation_error_1.RequestValidationError([]);
86
+ expect(Object.getPrototypeOf(error)).toBe(request_validation_error_1.RequestValidationError.prototype);
87
+ });
88
+ it('should store errors array', () => {
89
+ const validationErrors = [
90
+ {
91
+ type: 'field',
92
+ msg: 'Test error',
93
+ path: 'test',
94
+ location: 'body',
95
+ value: '',
96
+ },
97
+ ];
98
+ const error = new request_validation_error_1.RequestValidationError(validationErrors);
99
+ expect(error.errors).toEqual(validationErrors);
100
+ });
101
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,222 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const base_listener_1 = require("../../events/listeners/base-listener");
13
+ const subjects_1 = require("../../events/subjects");
14
+ const streams_1 = require("../../events/streams");
15
+ const nats_1 = require("nats");
16
+ class TestListener extends base_listener_1.Listener {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.subject = subjects_1.Subjects.AuthorSignedUp;
20
+ this.stream = streams_1.Streams.AuthServiceStream;
21
+ this.durableName = 'test-durable';
22
+ this.onMessage = jest.fn();
23
+ }
24
+ }
25
+ describe('Listener', () => {
26
+ let mockJs;
27
+ let mockJsm;
28
+ let listener;
29
+ beforeEach(() => {
30
+ jest.clearAllMocks();
31
+ mockJsm = {
32
+ streams: {
33
+ info: jest.fn().mockResolvedValue({}),
34
+ },
35
+ consumers: {
36
+ info: jest.fn().mockResolvedValue({}),
37
+ add: jest.fn().mockResolvedValue({}),
38
+ },
39
+ };
40
+ mockJs = {
41
+ consumers: {
42
+ get: jest.fn(),
43
+ },
44
+ };
45
+ listener = new TestListener(mockJs, mockJsm);
46
+ });
47
+ describe('constructor', () => {
48
+ it('should initialize with JetStreamClient and JetStreamManager', () => {
49
+ expect(listener).toBeDefined();
50
+ });
51
+ });
52
+ describe('waitForStream', () => {
53
+ it('should succeed when stream exists', () => __awaiter(void 0, void 0, void 0, function* () {
54
+ const mockConsumer = {
55
+ consume: jest.fn().mockResolvedValue({
56
+ [Symbol.asyncIterator]: () => ({
57
+ next: jest.fn().mockResolvedValue({ done: true }),
58
+ }),
59
+ }),
60
+ };
61
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
62
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
63
+ // We need to start listen and then stop it to test waitForStream
64
+ const listenPromise = listener.listen();
65
+ // Let it resolve
66
+ yield Promise.resolve();
67
+ yield Promise.resolve();
68
+ expect(mockJsm.streams.info).toHaveBeenCalledWith(streams_1.Streams.AuthServiceStream);
69
+ expect(consoleSpy).toHaveBeenCalledWith(`✅ Stream "${streams_1.Streams.AuthServiceStream}" is ready`);
70
+ consoleSpy.mockRestore();
71
+ }));
72
+ it('should retry when stream does not exist initially', () => __awaiter(void 0, void 0, void 0, function* () {
73
+ jest.useFakeTimers();
74
+ mockJsm.streams.info = jest.fn()
75
+ .mockRejectedValueOnce(new Error('Stream not found'))
76
+ .mockResolvedValueOnce({});
77
+ const mockConsumer = {
78
+ consume: jest.fn().mockResolvedValue({
79
+ [Symbol.asyncIterator]: () => ({
80
+ next: jest.fn().mockResolvedValue({ done: true }),
81
+ }),
82
+ }),
83
+ };
84
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
85
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
86
+ const listenPromise = listener.listen();
87
+ // First attempt fails, advance time for retry
88
+ yield jest.advanceTimersByTimeAsync(2000);
89
+ yield Promise.resolve();
90
+ expect(consoleSpy).toHaveBeenCalledWith(`⏳ Waiting for stream "${streams_1.Streams.AuthServiceStream}"...`);
91
+ expect(mockJsm.streams.info).toHaveBeenCalledTimes(2);
92
+ consoleSpy.mockRestore();
93
+ jest.useRealTimers();
94
+ }));
95
+ it('should throw error after max retry attempts', () => __awaiter(void 0, void 0, void 0, function* () {
96
+ jest.useFakeTimers();
97
+ mockJsm.streams.info = jest.fn().mockRejectedValue(new Error('Stream not found'));
98
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
99
+ const consoleErrorSpy = jest.spyOn(console, 'error').mockImplementation();
100
+ const listenPromise = listener.listen().catch(e => e);
101
+ // Advance through all retries (6 attempts, 2s each)
102
+ for (let i = 0; i < 6; i++) {
103
+ yield jest.advanceTimersByTimeAsync(2000);
104
+ yield Promise.resolve();
105
+ }
106
+ const error = yield listenPromise;
107
+ expect(error).toBeInstanceOf(Error);
108
+ expect(error.message).toBe(`Stream "${streams_1.Streams.AuthServiceStream}" is not ready`);
109
+ expect(consoleErrorSpy).toHaveBeenCalledWith(`❌ Stream "${streams_1.Streams.AuthServiceStream}" is not ready after 5 attempts`);
110
+ consoleSpy.mockRestore();
111
+ consoleErrorSpy.mockRestore();
112
+ jest.useRealTimers();
113
+ }));
114
+ });
115
+ describe('createConsumerIfNotExists', () => {
116
+ it('should not create consumer if it already exists', () => __awaiter(void 0, void 0, void 0, function* () {
117
+ const mockConsumer = {
118
+ consume: jest.fn().mockResolvedValue({
119
+ [Symbol.asyncIterator]: () => ({
120
+ next: jest.fn().mockResolvedValue({ done: true }),
121
+ }),
122
+ }),
123
+ };
124
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
125
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
126
+ yield listener.listen();
127
+ expect(mockJsm.consumers.info).toHaveBeenCalledWith(streams_1.Streams.AuthServiceStream, 'test-durable');
128
+ expect(mockJsm.consumers.add).not.toHaveBeenCalled();
129
+ consoleSpy.mockRestore();
130
+ }));
131
+ it('should create consumer if it does not exist', () => __awaiter(void 0, void 0, void 0, function* () {
132
+ mockJsm.consumers.info = jest.fn().mockRejectedValueOnce(new Error('Consumer not found'));
133
+ const mockConsumer = {
134
+ consume: jest.fn().mockResolvedValue({
135
+ [Symbol.asyncIterator]: () => ({
136
+ next: jest.fn().mockResolvedValue({ done: true }),
137
+ }),
138
+ }),
139
+ };
140
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
141
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
142
+ yield listener.listen();
143
+ expect(mockJsm.consumers.add).toHaveBeenCalledWith(streams_1.Streams.AuthServiceStream, {
144
+ durable_name: 'test-durable',
145
+ ack_policy: 'explicit',
146
+ ack_wait: 5 * 1000 * 1000000,
147
+ filter_subject: subjects_1.Subjects.AuthorSignedUp,
148
+ });
149
+ consoleSpy.mockRestore();
150
+ }));
151
+ });
152
+ describe('listen', () => {
153
+ it('should process messages through onMessage', () => __awaiter(void 0, void 0, void 0, function* () {
154
+ const testData = { id: 'test-id', email: 'test@test.com' };
155
+ const codec = (0, nats_1.JSONCodec)();
156
+ const encodedData = codec.encode(testData);
157
+ const mockMsg = {
158
+ data: encodedData,
159
+ ack: jest.fn(),
160
+ };
161
+ let messageCount = 0;
162
+ const mockConsumer = {
163
+ consume: jest.fn().mockResolvedValue({
164
+ [Symbol.asyncIterator]: () => ({
165
+ next: jest.fn().mockImplementation(() => {
166
+ messageCount++;
167
+ if (messageCount === 1) {
168
+ return Promise.resolve({ value: mockMsg, done: false });
169
+ }
170
+ return Promise.resolve({ done: true });
171
+ }),
172
+ }),
173
+ }),
174
+ };
175
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
176
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
177
+ yield listener.listen();
178
+ expect(listener.onMessage).toHaveBeenCalledWith(testData, mockMsg);
179
+ consoleSpy.mockRestore();
180
+ }));
181
+ });
182
+ describe('parseMessage', () => {
183
+ it('should correctly decode JSON messages', () => __awaiter(void 0, void 0, void 0, function* () {
184
+ const testData = { id: 'parse-test', email: 'parse@test.com' };
185
+ const codec = (0, nats_1.JSONCodec)();
186
+ const encodedData = codec.encode(testData);
187
+ const mockMsg = {
188
+ data: encodedData,
189
+ };
190
+ let messageCount = 0;
191
+ const mockConsumer = {
192
+ consume: jest.fn().mockResolvedValue({
193
+ [Symbol.asyncIterator]: () => ({
194
+ next: jest.fn().mockImplementation(() => {
195
+ messageCount++;
196
+ if (messageCount === 1) {
197
+ return Promise.resolve({ value: mockMsg, done: false });
198
+ }
199
+ return Promise.resolve({ done: true });
200
+ }),
201
+ }),
202
+ }),
203
+ };
204
+ mockJs.consumers.get.mockResolvedValue(mockConsumer);
205
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
206
+ yield listener.listen();
207
+ expect(listener.onMessage).toHaveBeenCalledWith(testData, expect.anything());
208
+ consoleSpy.mockRestore();
209
+ }));
210
+ });
211
+ describe('subject, stream, and durableName', () => {
212
+ it('should have correct subject', () => {
213
+ expect(listener.subject).toBe(subjects_1.Subjects.AuthorSignedUp);
214
+ });
215
+ it('should have correct stream', () => {
216
+ expect(listener.stream).toBe(streams_1.Streams.AuthServiceStream);
217
+ });
218
+ it('should have correct durableName', () => {
219
+ expect(listener.durableName).toBe('test-durable');
220
+ });
221
+ });
222
+ });
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,85 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ const base_publisher_1 = require("../../events/publishers/base-publisher");
13
+ const subjects_1 = require("../../events/subjects");
14
+ const streams_1 = require("../../events/streams");
15
+ const nats_1 = require("nats");
16
+ class TestPublisher extends base_publisher_1.Publisher {
17
+ constructor() {
18
+ super(...arguments);
19
+ this.subject = subjects_1.Subjects.AuthorSignedUp;
20
+ this.stream = streams_1.Streams.AuthServiceStream;
21
+ }
22
+ }
23
+ describe('Publisher', () => {
24
+ let mockJsClient;
25
+ let publisher;
26
+ beforeEach(() => {
27
+ mockJsClient = {
28
+ publish: jest.fn().mockResolvedValue({}),
29
+ };
30
+ publisher = new TestPublisher(mockJsClient);
31
+ });
32
+ describe('constructor', () => {
33
+ it('should initialize with JetStreamClient', () => {
34
+ expect(publisher).toBeDefined();
35
+ });
36
+ });
37
+ describe('publish', () => {
38
+ it('should encode and publish message to the correct subject', () => __awaiter(void 0, void 0, void 0, function* () {
39
+ const testData = { id: 'test-id', email: 'test@test.com' };
40
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
41
+ yield publisher.publish(testData);
42
+ expect(mockJsClient.publish).toHaveBeenCalledTimes(1);
43
+ expect(mockJsClient.publish).toHaveBeenCalledWith(subjects_1.Subjects.AuthorSignedUp, expect.any(Uint8Array));
44
+ expect(consoleSpy).toHaveBeenCalledWith(`Message published to ${subjects_1.Subjects.AuthorSignedUp}:`, testData);
45
+ consoleSpy.mockRestore();
46
+ }));
47
+ it('should properly encode the data using JSONCodec', () => __awaiter(void 0, void 0, void 0, function* () {
48
+ const testData = { id: 'test-id', email: 'test@test.com' };
49
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
50
+ yield publisher.publish(testData);
51
+ // Verify the published message can be decoded back to original data
52
+ const publishCall = mockJsClient.publish.mock.calls[0];
53
+ const encodedData = publishCall[1];
54
+ const codec = (0, nats_1.JSONCodec)();
55
+ const decodedData = codec.decode(encodedData);
56
+ expect(decodedData).toEqual(testData);
57
+ consoleSpy.mockRestore();
58
+ }));
59
+ it('should handle complex data objects', () => __awaiter(void 0, void 0, void 0, function* () {
60
+ const complexData = {
61
+ id: 'complex-id',
62
+ email: 'complex@test.com',
63
+ };
64
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
65
+ yield publisher.publish(complexData);
66
+ expect(mockJsClient.publish).toHaveBeenCalledTimes(1);
67
+ consoleSpy.mockRestore();
68
+ }));
69
+ it('should propagate errors from JetStreamClient', () => __awaiter(void 0, void 0, void 0, function* () {
70
+ mockJsClient.publish.mockRejectedValueOnce(new Error('Publish failed'));
71
+ const consoleSpy = jest.spyOn(console, 'log').mockImplementation();
72
+ const testData = { id: 'test-id', email: 'test@test.com' };
73
+ yield expect(publisher.publish(testData)).rejects.toThrow('Publish failed');
74
+ consoleSpy.mockRestore();
75
+ }));
76
+ });
77
+ describe('subject and stream', () => {
78
+ it('should have correct subject', () => {
79
+ expect(publisher.subject).toBe(subjects_1.Subjects.AuthorSignedUp);
80
+ });
81
+ it('should have correct stream', () => {
82
+ expect(publisher.stream).toBe(streams_1.Streams.AuthServiceStream);
83
+ });
84
+ });
85
+ });
@@ -0,0 +1 @@
1
+ export {};