@roomkit/gateway 1.1.3 → 1.3.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.
Files changed (48) hide show
  1. package/README.md +182 -47
  2. package/dist/src/config/config.module.d.ts +3 -0
  3. package/dist/src/config/config.module.d.ts.map +1 -1
  4. package/dist/src/config/config.module.js +16 -6
  5. package/dist/src/config/config.module.js.map +1 -1
  6. package/dist/src/config/config.service.d.ts +17 -1
  7. package/dist/src/config/config.service.d.ts.map +1 -1
  8. package/dist/src/config/config.service.js +17 -16
  9. package/dist/src/config/config.service.js.map +1 -1
  10. package/dist/src/config/gateway.config.d.ts +2 -0
  11. package/dist/src/config/gateway.config.d.ts.map +1 -1
  12. package/dist/src/config/gateway.config.js +1 -0
  13. package/dist/src/config/gateway.config.js.map +1 -1
  14. package/dist/src/gateway.module.d.ts +2 -0
  15. package/dist/src/gateway.module.d.ts.map +1 -1
  16. package/dist/src/gateway.module.js +1 -1
  17. package/dist/src/gateway.module.js.map +1 -1
  18. package/dist/src/index.d.ts.map +1 -1
  19. package/dist/src/index.js +21 -16
  20. package/dist/src/index.js.map +1 -1
  21. package/dist/src/ws/message-router.service.d.ts.map +1 -1
  22. package/dist/src/ws/message-router.service.js +6 -6
  23. package/dist/src/ws/message-router.service.js.map +1 -1
  24. package/package.json +25 -21
  25. package/dist/test/connection/connection.service.spec.d.ts +0 -2
  26. package/dist/test/connection/connection.service.spec.d.ts.map +0 -1
  27. package/dist/test/connection/connection.service.spec.js +0 -204
  28. package/dist/test/connection/connection.service.spec.js.map +0 -1
  29. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts +0 -2
  30. package/dist/test/e2e/gateway-worker.e2e.spec.d.ts.map +0 -1
  31. package/dist/test/e2e/gateway-worker.e2e.spec.js +0 -412
  32. package/dist/test/e2e/gateway-worker.e2e.spec.js.map +0 -1
  33. package/dist/test/integration/admin-api.spec.d.ts +0 -2
  34. package/dist/test/integration/admin-api.spec.d.ts.map +0 -1
  35. package/dist/test/integration/admin-api.spec.js +0 -218
  36. package/dist/test/integration/admin-api.spec.js.map +0 -1
  37. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts +0 -2
  38. package/dist/test/ratelimit/rate-limiter.service.spec.d.ts.map +0 -1
  39. package/dist/test/ratelimit/rate-limiter.service.spec.js +0 -139
  40. package/dist/test/ratelimit/rate-limiter.service.spec.js.map +0 -1
  41. package/dist/test/setup.d.ts +0 -2
  42. package/dist/test/setup.d.ts.map +0 -1
  43. package/dist/test/setup.js +0 -56
  44. package/dist/test/setup.js.map +0 -1
  45. package/dist/test/ws/message-router.service.spec.d.ts +0 -2
  46. package/dist/test/ws/message-router.service.spec.d.ts.map +0 -1
  47. package/dist/test/ws/message-router.service.spec.js +0 -403
  48. package/dist/test/ws/message-router.service.spec.js.map +0 -1
@@ -1 +0,0 @@
1
- {"version":3,"file":"admin-api.spec.js","sourceRoot":"","sources":["../../../test/integration/admin-api.spec.ts"],"names":[],"mappings":";;;;;AAAA,8BAA8B;AAC9B,6CAAsD;AAEtD,0DAAgC;AAChC,uEAAmE;AACnE,iEAA6D;AAC7D,oEAAgE;AAChE,gFAA4E;AAC5E,iEAA6D;AAC7D,mFAA8E;AAE9E;;;GAGG;AACH,QAAQ,CAAC,iCAAiC,EAAE,GAAG,EAAE;IAC/C,IAAI,GAAqB,CAAC;IAE1B,MAAM,iBAAiB,GAAG;QACxB,SAAS,EAAE,0BAA0B;QACrC,IAAI,EAAE,KAAK;QACX,SAAS,EAAE,KAAK;QAChB,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW;QAChD,SAAS,EAAE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,EAAE,EAAE,CAAC;QACzD,aAAa,EAAE,OAAO,CAAC,GAAG,CAAC,cAAc;QACzC,iBAAiB,EAAE,KAAK;QACxB,gBAAgB,EAAE,KAAK;QACvB,cAAc,EAAE,KAAK;QACrB,mBAAmB,EAAE,GAAG;QACxB,6BAA6B,EAAE,EAAE;QACjC,iBAAiB,EAAE,GAAG;QACtB,sBAAsB,EAAE,KAAK;KAC9B,CAAC;IAEF,MAAM,qBAAqB,GAAG;QAC5B,kBAAkB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QAChD,qBAAqB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACnD,iBAAiB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,GAAG,EAAE,CAAC;QACvD,aAAa,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;QAC9C,qBAAqB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;QACtD,uBAAuB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,IAAI,CAAC;QACxD,WAAW,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,CAAC;QAC1C,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,CAAC;QAC/D,gBAAgB,EAAE,IAAI,CAAC,EAAE,EAAE;QAC3B,IAAI,EAAE,IAAI,CAAC,EAAE,EAAE;KAChB,CAAC;IAEF,MAAM,gBAAgB,GAAG;QACvB,MAAM,EAAE;YACN,MAAM,EAAE,OAAO;SAChB;KACF,CAAC;IAEF,MAAM,sBAAsB,GAAG;QAC7B,wBAAwB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACtD,2BAA2B,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACzD,2BAA2B,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YACrD,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,GAAG;SACX,CAAC;QACF,wBAAwB,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YAClD,kBAAkB,EAAE,CAAC;YACrB,aAAa,EAAE,CAAC;SACjB,CAAC;QACF,8BAA8B,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YACxD,KAAK,EAAE,CAAC;YACR,MAAM,EAAE,IAAI;YACZ,KAAK,EAAE,KAAK;SACb,CAAC;QACF,0BAA0B,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC;QACxD,SAAS,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YACnC,mBAAmB,EAAE,GAAG;YACxB,6BAA6B,EAAE,EAAE;YACjC,iBAAiB,EAAE,GAAG;YACtB,sBAAsB,EAAE,KAAK;SAC9B,CAAC;QACF,QAAQ,EAAE,IAAI,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC;YAClC,cAAc,EAAE;gBACd,OAAO,EAAE,CAAC;gBACV,KAAK,EAAE,GAAG;gBACV,MAAM,EAAE,IAAI;aACb;YACD,iBAAiB,EAAE;gBACjB,OAAO,EAAE,CAAC;gBACV,KAAK,EAAE,KAAK;gBACZ,MAAM,EAAE,IAAI;aACb;YACD,kBAAkB,EAAE,CAAC;SACtB,CAAC;KACH,CAAC;IAEF,SAAS,CAAC,KAAK,IAAI,EAAE;QACnB,MAAM,aAAa,GAAkB,MAAM,cAAI,CAAC,mBAAmB,CAAC;YAClE,WAAW,EAAE,CAAC,kCAAe,CAAC;YAC9B,SAAS,EAAE;gBACT,4BAAY;gBACZ,EAAE,OAAO,EAAE,8BAAa,EAAE,QAAQ,EAAE,iBAAiB,EAAE;gBACvD,EAAE,OAAO,EAAE,sCAAiB,EAAE,QAAQ,EAAE,qBAAqB,EAAE;gBAC/D,EAAE,OAAO,EAAE,4BAAY,EAAE,QAAQ,EAAE,gBAAgB,EAAE;gBACrD,EAAE,OAAO,EAAE,yCAAkB,EAAE,QAAQ,EAAE,sBAAsB,EAAE;aAClE;SACF,CAAC,CAAC,OAAO,EAAE,CAAC;QAEb,GAAG,GAAG,aAAa,CAAC,qBAAqB,EAAE,CAAC;QAC5C,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,KAAK,IAAI,EAAE;QAClB,MAAM,GAAG,CAAC,KAAK,EAAE,CAAC;IACpB,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,eAAe,CAAC;iBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;YACrD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;QACpD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,mBAAmB,EAAE,GAAG,EAAE;QACjC,EAAE,CAAC,6BAA6B,EAAE,KAAK,IAAI,EAAE;YAC3C,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,eAAe,CAAC;iBACpB,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC1D,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,cAAc,CAAC,eAAe,CAAC,CAAC;YAClE,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,wBAAwB,EAAE,GAAG,EAAE;QACtC,EAAE,CAAC,gDAAgD,EAAE,KAAK,IAAI,EAAE;YAC9D,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,oBAAoB,CAAC;iBACzB,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAChD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,+CAA+C,EAAE,KAAK,IAAI,EAAE;YAC7D,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAC/B,GAAG,CAAC,oCAAoC,CAAC;iBACzC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,qCAAqC,EAAE,KAAK,IAAI,EAAE;YACnD,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,wBAAwB,CAAC;iBAC7B,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,gBAAgB,CAAC,CAAC;YACvD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,mBAAmB,CAAC,CAAC;YAC1D,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,oBAAoB,CAAC,CAAC;YAC3D,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,yBAAyB,EAAE,GAAG,EAAE;QACvC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,qBAAqB,CAAC;iBAC1B,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;YAC/C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;YAC7C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,CAAC,CAAC;QAC/C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,kBAAkB,EAAE,GAAG,EAAE;QAChC,EAAE,CAAC,0CAA0C,EAAE,KAAK,IAAI,EAAE;YACxD,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,cAAc,CAAC;iBACnB,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;YACjD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC;YAC9C,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;QAC1C,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;QACpC,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAC/B,GAAG,CAAC,gCAAgC,CAAC;iBACrC,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,4BAA4B,EAAE,GAAG,EAAE;QAC1C,EAAE,CAAC,yCAAyC,EAAE,KAAK,IAAI,EAAE;YACvD,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAC/B,IAAI,CAAC,qCAAqC,CAAC;iBAC3C,IAAI,CAAC,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC;iBACxB,MAAM,CAAC,GAAG,CAAC,CAAC;QACjB,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,uBAAuB,EAAE,GAAG,EAAE;QACrC,EAAE,CAAC,6DAA6D,EAAE,KAAK,IAAI,EAAE;YAC3E,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,IAAI,CAAC,kBAAkB,CAAC;iBACxB,IAAI,CAAC,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,CAAC;iBACnD,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACtD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;YAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;QACnD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;QAClC,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;YAC1C,MAAM,QAAQ,GAAG,MAAM,IAAA,mBAAO,EAAC,GAAG,CAAC,aAAa,EAAE,CAAC;iBAChD,GAAG,CAAC,gBAAgB,CAAC;iBACrB,MAAM,CAAC,GAAG,CAAC,CAAC;YAEf,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,SAAS,CAAC,CAAC;YAChD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,aAAa,CAAC,CAAC;YACpD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,WAAW,CAAC,CAAC;YAClD,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QACjD,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=rate-limiter.service.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.service.spec.d.ts","sourceRoot":"","sources":["../../../test/ratelimit/rate-limiter.service.spec.ts"],"names":[],"mappings":""}
@@ -1,139 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /// <reference types="jest" />
4
- const rate_limiter_service_1 = require("../../src/ratelimit/rate-limiter.service");
5
- describe('RateLimiterService', () => {
6
- let service;
7
- let configService;
8
- beforeEach(() => {
9
- configService = {
10
- connectionRateLimit: 10,
11
- messageRateLimitPerConnection: 5,
12
- messageBurstLimit: 10,
13
- globalMessageRateLimit: 100,
14
- };
15
- service = new rate_limiter_service_1.RateLimiterService(configService);
16
- });
17
- afterEach(() => {
18
- jest.clearAllMocks();
19
- });
20
- describe('checkConnectionRate', () => {
21
- it('should allow connections under the limit', () => {
22
- for (let i = 0; i < 5; i++) {
23
- expect(service.checkConnectionRate()).toBe(true);
24
- }
25
- });
26
- it('should reject connections over the limit', () => {
27
- // 使用所有配额
28
- for (let i = 0; i < 10; i++) {
29
- service.checkConnectionRate();
30
- }
31
- // 下一个连接应该被拒绝
32
- expect(service.checkConnectionRate()).toBe(false);
33
- });
34
- it('should reset after window expires', async () => {
35
- // 使用所有配额
36
- for (let i = 0; i < 10; i++) {
37
- service.checkConnectionRate();
38
- }
39
- expect(service.checkConnectionRate()).toBe(false);
40
- // 等待窗口过期(1秒+)
41
- await new Promise((resolve) => setTimeout(resolve, 1100));
42
- // 现在应该允许
43
- expect(service.checkConnectionRate()).toBe(true);
44
- });
45
- });
46
- describe('checkMessageRate', () => {
47
- const connectionId = 'conn-1';
48
- it('should allow messages under the limit', () => {
49
- for (let i = 0; i < 5; i++) {
50
- const result = service.checkMessageRate(connectionId);
51
- expect(result.allowed).toBe(true);
52
- }
53
- });
54
- it('should reject messages over per-connection limit', () => {
55
- // 使用所有配额
56
- for (let i = 0; i < 5; i++) {
57
- service.checkMessageRate(connectionId);
58
- }
59
- // 下一个消息应该被拒绝
60
- const result = service.checkMessageRate(connectionId);
61
- expect(result.allowed).toBe(false);
62
- expect(result.retryAfter).toBeDefined();
63
- });
64
- it('should track connections separately', () => {
65
- // conn-1 使用完配额
66
- for (let i = 0; i < 5; i++) {
67
- service.checkMessageRate('conn-1');
68
- }
69
- // conn-2 应该仍然可用
70
- const result = service.checkMessageRate('conn-2');
71
- expect(result.allowed).toBe(true);
72
- });
73
- it('should enforce burst limit', () => {
74
- // 创建一个较大突发限制的服务
75
- const burstConfig = {
76
- ...configService,
77
- messageRateLimitPerConnection: 100,
78
- messageBurstLimit: 5,
79
- };
80
- const burstService = new rate_limiter_service_1.RateLimiterService(burstConfig);
81
- // 快速发送消息直到突发限制
82
- for (let i = 0; i < 5; i++) {
83
- const result = burstService.checkMessageRate(connectionId);
84
- expect(result.allowed).toBe(true);
85
- }
86
- // 下一个消息应该因突发限制被拒绝
87
- const result = burstService.checkMessageRate(connectionId);
88
- expect(result.allowed).toBe(false);
89
- });
90
- });
91
- describe('removeConnection', () => {
92
- it('should clean up connection data', () => {
93
- const connectionId = 'conn-1';
94
- // 发送一些消息
95
- service.checkMessageRate(connectionId);
96
- // 移除连接
97
- service.removeConnection(connectionId);
98
- // 应该不会报错
99
- expect(() => service.removeConnection(connectionId)).not.toThrow();
100
- });
101
- });
102
- describe('getStats', () => {
103
- it('should return current statistics', () => {
104
- // 创建一些活动
105
- service.checkConnectionRate();
106
- service.checkConnectionRate();
107
- service.checkMessageRate('conn-1');
108
- service.checkMessageRate('conn-2');
109
- const stats = service.getStats();
110
- expect(stats.connectionRate).toBe(2);
111
- expect(stats.globalMessageRate).toBe(2);
112
- expect(stats.trackedConnections).toBe(2);
113
- });
114
- it('should return zero stats initially', () => {
115
- const stats = service.getStats();
116
- expect(stats.connectionRate).toBe(0);
117
- expect(stats.globalMessageRate).toBe(0);
118
- expect(stats.trackedConnections).toBe(0);
119
- });
120
- });
121
- describe('sliding window behavior', () => {
122
- it('should slide window and reset old counts', async () => {
123
- // 发送一些连接
124
- for (let i = 0; i < 5; i++) {
125
- service.checkConnectionRate();
126
- }
127
- const statsBefore = service.getStats();
128
- expect(statsBefore.connectionRate).toBe(5);
129
- // 等待窗口滑动
130
- await new Promise((resolve) => setTimeout(resolve, 1100));
131
- // 再检查一次以触发滑动
132
- service.checkConnectionRate();
133
- const statsAfter = service.getStats();
134
- // 旧的计数应该被清除,只剩下最新的1个
135
- expect(statsAfter.connectionRate).toBeLessThanOrEqual(2);
136
- });
137
- });
138
- });
139
- //# sourceMappingURL=rate-limiter.service.spec.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"rate-limiter.service.spec.js","sourceRoot":"","sources":["../../../test/ratelimit/rate-limiter.service.spec.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=setup.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.d.ts","sourceRoot":"","sources":["../../test/setup.ts"],"names":[],"mappings":""}
@@ -1,56 +0,0 @@
1
- "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
- Object.defineProperty(exports, "__esModule", { value: true });
36
- /// <reference types="jest" />
37
- const dotenv = __importStar(require("dotenv"));
38
- const path = __importStar(require("path"));
39
- /**
40
- * Jest 测试环境设置
41
- */
42
- // 加载根目录的 .env 文件
43
- dotenv.config({ path: path.resolve(__dirname, '../../../.env') });
44
- // 设置测试超时时间
45
- jest.setTimeout(10000);
46
- // 模拟环境变量(优先使用已有的环境变量)
47
- process.env.NODE_ENV = 'test';
48
- process.env.GATEWAY_ID = process.env.GATEWAY_ID || 'gateway-test';
49
- process.env.REDIS_HOST = process.env.REDIS_HOST || 'localhost';
50
- process.env.REDIS_PORT = process.env.REDIS_PORT || '6379';
51
- // 全局清理
52
- afterAll(async () => {
53
- // 确保所有异步操作完成
54
- await new Promise((resolve) => setTimeout(resolve, 100));
55
- });
56
- //# sourceMappingURL=setup.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"setup.js","sourceRoot":"","sources":["../../test/setup.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA,8BAA8B;AAC9B,+CAAiC;AACjC,2CAA6B;AAE7B;;GAEG;AAEH,iBAAiB;AACjB,MAAM,CAAC,MAAM,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,eAAe,CAAC,EAAE,CAAC,CAAC;AAElE,WAAW;AACX,IAAI,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC;AAEvB,sBAAsB;AACtB,OAAO,CAAC,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC;AAC9B,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,cAAc,CAAC;AAClE,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,WAAW,CAAC;AAC/D,OAAO,CAAC,GAAG,CAAC,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,UAAU,IAAI,MAAM,CAAC;AAE1D,OAAO;AACP,QAAQ,CAAC,KAAK,IAAI,EAAE;IAClB,aAAa;IACb,MAAM,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;AAC3D,CAAC,CAAC,CAAC"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=message-router.service.spec.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"message-router.service.spec.d.ts","sourceRoot":"","sources":["../../../test/ws/message-router.service.spec.ts"],"names":[],"mappings":""}
@@ -1,403 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- /// <reference types="jest" />
4
- const message_router_service_1 = require("../../src/ws/message-router.service");
5
- const core_1 = require("@roomkit/core");
6
- describe('MessageRouterService', () => {
7
- let service;
8
- let configService;
9
- let redisService;
10
- let connectionService;
11
- const mockGatewayId = 'gateway-test-001';
12
- const mockWorkerId = 'worker-test-001';
13
- beforeEach(() => {
14
- // 创建模拟的依赖服务
15
- configService = {
16
- gatewayId: mockGatewayId,
17
- };
18
- redisService = {
19
- client: {
20
- get: jest.fn(),
21
- setnx: jest.fn(),
22
- smembers: jest.fn(),
23
- srandmember: jest.fn(),
24
- hgetall: jest.fn(),
25
- set: jest.fn(),
26
- del: jest.fn(),
27
- },
28
- publish: jest.fn().mockResolvedValue(undefined),
29
- evalScript: jest.fn().mockResolvedValue(null),
30
- };
31
- connectionService = {
32
- touch: jest.fn(),
33
- send: jest.fn().mockReturnValue(true),
34
- sendByType: jest.fn().mockReturnValue(true),
35
- updateStatus: jest.fn(),
36
- setRoom: jest.fn(),
37
- getConnection: jest.fn(),
38
- };
39
- const sessionService = {
40
- createOrUpdateSession: jest.fn(),
41
- getSessionByUserId: jest.fn(),
42
- getSessionByToken: jest.fn(),
43
- updateConnectionInfo: jest.fn(),
44
- addRoomToSession: jest.fn(),
45
- removeRoomFromSession: jest.fn(),
46
- };
47
- service = new message_router_service_1.MessageRouterService(configService, redisService, connectionService, sessionService);
48
- });
49
- afterEach(() => {
50
- jest.clearAllMocks();
51
- });
52
- describe('handleMessage - Heartbeat', () => {
53
- it('should respond to heartbeat directly without forwarding to worker', async () => {
54
- const connection = {
55
- id: 'conn-1',
56
- status: 'identified',
57
- lastActivity: Date.now(),
58
- createdAt: Date.now(),
59
- rooms: new Set(),
60
- };
61
- const message = {
62
- msgType: core_1.MessageType.MSG_HEARTBEAT,
63
- payload: { timestamp: 1234567890 },
64
- };
65
- await service.handleMessage(connection, message);
66
- // 应该更新活动时间
67
- expect(connectionService.touch).toHaveBeenCalledWith(connection.id);
68
- // 应该直接响应心跳
69
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_HEARTBEAT_ACK, expect.objectContaining({
70
- timestamp: 1234567890,
71
- serverTime: expect.any(Number),
72
- }));
73
- // 不应发布到Redis
74
- expect(redisService.publish).not.toHaveBeenCalled();
75
- });
76
- });
77
- describe('handleMessage - Auth', () => {
78
- it('should reject auth request if connection is not in connected state', async () => {
79
- const connection = {
80
- id: 'conn-1',
81
- status: 'identified', // 已认证状态
82
- lastActivity: Date.now(),
83
- createdAt: Date.now(),
84
- rooms: new Set(),
85
- };
86
- const message = {
87
- msgType: core_1.MessageType.MSG_AUTH,
88
- payload: { token: 'test-token' },
89
- };
90
- await service.handleMessage(connection, message);
91
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
92
- success: false,
93
- error: 'INVALID_STATE',
94
- }));
95
- });
96
- it('should reject auth request with invalid payload', async () => {
97
- const connection = {
98
- id: 'conn-1',
99
- status: 'connected',
100
- lastActivity: Date.now(),
101
- createdAt: Date.now(),
102
- rooms: new Set(),
103
- };
104
- const message = {
105
- msgType: core_1.MessageType.MSG_AUTH,
106
- payload: {}, // 缺少必需的 token 字段
107
- };
108
- await service.handleMessage(connection, message);
109
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
110
- success: false,
111
- error: 'INVALID_PAYLOAD',
112
- }));
113
- });
114
- it('should forward valid auth request to worker', async () => {
115
- const connection = {
116
- id: 'conn-1',
117
- status: 'connected',
118
- lastActivity: Date.now(),
119
- createdAt: Date.now(),
120
- rooms: new Set(),
121
- };
122
- // 模拟有可用的 Worker
123
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
124
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
125
- const message = {
126
- msgType: core_1.MessageType.MSG_AUTH,
127
- payload: { token: 'valid-token' },
128
- };
129
- await service.handleMessage(connection, message);
130
- // 应该更新状态为 authenticating
131
- expect(connectionService.updateStatus).toHaveBeenCalledWith(connection.id, 'authenticating');
132
- // 应该发布消息到 Worker
133
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
134
- type: 'auth',
135
- gatewayId: mockGatewayId,
136
- connectionId: connection.id,
137
- msgType: core_1.MessageType.MSG_AUTH,
138
- }));
139
- });
140
- it('should reject auth when no worker available', async () => {
141
- const connection = {
142
- id: 'conn-1',
143
- status: 'connected',
144
- lastActivity: Date.now(),
145
- createdAt: Date.now(),
146
- rooms: new Set(),
147
- };
148
- // 模拟没有可用的 Worker
149
- redisService.client.smembers.mockResolvedValue([]);
150
- const message = {
151
- msgType: core_1.MessageType.MSG_AUTH,
152
- payload: { token: 'valid-token' },
153
- };
154
- await service.handleMessage(connection, message);
155
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_AUTH_FAIL, expect.objectContaining({
156
- success: false,
157
- error: 'NO_WORKER_AVAILABLE',
158
- }));
159
- });
160
- });
161
- describe('handleMessage - Unauthenticated access', () => {
162
- it('should reject non-auth messages from unauthenticated connection', async () => {
163
- const connection = {
164
- id: 'conn-1',
165
- status: 'connected', // 未认证
166
- lastActivity: Date.now(),
167
- createdAt: Date.now(),
168
- rooms: new Set(),
169
- };
170
- const message = {
171
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
172
- payload: { gameType: 'echo' },
173
- };
174
- await service.handleMessage(connection, message);
175
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
176
- error: 'NOT_AUTHENTICATED',
177
- }));
178
- });
179
- });
180
- describe('handleMessage - Create Room', () => {
181
- it('should reject create room with invalid payload', async () => {
182
- const connection = {
183
- id: 'conn-1',
184
- userId: 'user-1',
185
- status: 'identified',
186
- lastActivity: Date.now(),
187
- createdAt: Date.now(),
188
- rooms: new Set(),
189
- };
190
- const message = {
191
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
192
- payload: {}, // 缺少 gameType
193
- };
194
- await service.handleMessage(connection, message);
195
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
196
- success: false,
197
- error: 'INVALID_PAYLOAD',
198
- }));
199
- });
200
- it('should reject create room when no worker available', async () => {
201
- const connection = {
202
- id: 'conn-1',
203
- userId: 'user-1',
204
- status: 'identified',
205
- lastActivity: Date.now(),
206
- createdAt: Date.now(),
207
- rooms: new Set(),
208
- };
209
- // 模拟没有可用的 Worker
210
- redisService.client.smembers.mockResolvedValue([]);
211
- const message = {
212
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
213
- payload: { gameType: 'echo', maxPlayers: 10 },
214
- };
215
- await service.handleMessage(connection, message);
216
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
217
- success: false,
218
- error: 'NO_WORKER_AVAILABLE',
219
- }));
220
- });
221
- it('should reject duplicate room creation', async () => {
222
- const connection = {
223
- id: 'conn-1',
224
- userId: 'user-1',
225
- status: 'identified',
226
- lastActivity: Date.now(),
227
- createdAt: Date.now(),
228
- rooms: new Set(),
229
- };
230
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
231
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
232
- // 模拟房间已存在(setnx 返回 0)
233
- redisService.client.setnx.mockResolvedValue(0);
234
- const message = {
235
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
236
- payload: { roomId: 'existing-room', gameType: 'echo', maxPlayers: 10 },
237
- };
238
- await service.handleMessage(connection, message);
239
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_CREATE_FAIL, expect.objectContaining({
240
- success: false,
241
- error: 'ROOM_ALREADY_EXISTS',
242
- }));
243
- });
244
- it('should forward valid create room request to worker', async () => {
245
- const connection = {
246
- id: 'conn-1',
247
- userId: 'user-1',
248
- status: 'identified',
249
- lastActivity: Date.now(),
250
- createdAt: Date.now(),
251
- rooms: new Set(),
252
- };
253
- redisService.client.smembers.mockResolvedValue([mockWorkerId]);
254
- redisService.client.srandmember.mockResolvedValue(mockWorkerId);
255
- // 模拟房间不存在(setnx 返回 1)
256
- redisService.client.setnx.mockResolvedValue(1);
257
- const message = {
258
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
259
- payload: { gameType: 'echo', maxPlayers: 10, config: {} },
260
- };
261
- await service.handleMessage(connection, message);
262
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
263
- type: 'client-message',
264
- msgType: core_1.MessageType.MSG_ROOM_CREATE,
265
- userId: 'user-1',
266
- }));
267
- });
268
- });
269
- describe('handleMessage - Join Room', () => {
270
- it('should reject join room when room not found', async () => {
271
- const connection = {
272
- id: 'conn-1',
273
- userId: 'user-1',
274
- status: 'identified',
275
- lastActivity: Date.now(),
276
- createdAt: Date.now(),
277
- rooms: new Set(),
278
- };
279
- // 模拟房间不存在
280
- redisService.client.get.mockResolvedValue(null);
281
- const message = {
282
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
283
- payload: { roomId: 'non-existent-room' },
284
- };
285
- await service.handleMessage(connection, message);
286
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ROOM_JOIN_FAIL, expect.objectContaining({
287
- success: false,
288
- error: 'ROOM_NOT_FOUND',
289
- }));
290
- });
291
- it('should forward valid join room request to worker', async () => {
292
- const connection = {
293
- id: 'conn-1',
294
- userId: 'user-1',
295
- status: 'identified',
296
- lastActivity: Date.now(),
297
- createdAt: Date.now(),
298
- rooms: new Set(),
299
- };
300
- // 模拟房间存在
301
- redisService.client.get.mockResolvedValue(mockWorkerId);
302
- const message = {
303
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
304
- payload: { roomId: 'room-123' },
305
- };
306
- await service.handleMessage(connection, message);
307
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
308
- type: 'client-message',
309
- msgType: core_1.MessageType.MSG_ROOM_JOIN,
310
- roomId: 'room-123',
311
- }));
312
- });
313
- });
314
- describe('handleMessage - Leave Room', () => {
315
- it('should reject leave room when not in room', async () => {
316
- const connection = {
317
- id: 'conn-1',
318
- userId: 'user-1',
319
- status: 'identified',
320
- currentRoom: undefined, // 不在房间中
321
- lastActivity: Date.now(),
322
- createdAt: Date.now(),
323
- rooms: new Set(),
324
- };
325
- const message = {
326
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
327
- payload: {},
328
- };
329
- await service.handleMessage(connection, message);
330
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
331
- success: false,
332
- error: 'NOT_IN_ROOM',
333
- }));
334
- });
335
- it('should forward leave room request to worker', async () => {
336
- const connection = {
337
- id: 'conn-1',
338
- userId: 'user-1',
339
- status: 'in-room',
340
- currentRoom: 'room-123',
341
- lastActivity: Date.now(),
342
- createdAt: Date.now(),
343
- rooms: new Set(),
344
- };
345
- redisService.client.get.mockResolvedValue(mockWorkerId);
346
- const message = {
347
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
348
- payload: {},
349
- };
350
- await service.handleMessage(connection, message);
351
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
352
- type: 'client-message',
353
- msgType: core_1.MessageType.MSG_ROOM_LEAVE,
354
- roomId: 'room-123',
355
- }));
356
- });
357
- });
358
- describe('handleMessage - Game Logic', () => {
359
- it('should reject game message when not in room', async () => {
360
- const connection = {
361
- id: 'conn-1',
362
- userId: 'user-1',
363
- status: 'identified',
364
- currentRoom: undefined,
365
- lastActivity: Date.now(),
366
- createdAt: Date.now(),
367
- rooms: new Set(),
368
- };
369
- const message = {
370
- msgType: 'chat',
371
- payload: { message: 'Hello!' },
372
- };
373
- await service.handleMessage(connection, message);
374
- expect(connectionService.sendByType).toHaveBeenCalledWith(connection.id, core_1.MessageType.MSG_ERROR, expect.objectContaining({
375
- error: 'NOT_IN_ROOM',
376
- }));
377
- });
378
- it('should forward game message to worker when in room', async () => {
379
- const connection = {
380
- id: 'conn-1',
381
- userId: 'user-1',
382
- status: 'in-room',
383
- currentRoom: 'room-123',
384
- lastActivity: Date.now(),
385
- createdAt: Date.now(),
386
- rooms: new Set(),
387
- };
388
- redisService.client.get.mockResolvedValue(mockWorkerId);
389
- const message = {
390
- msgType: 'chat',
391
- payload: { message: 'Hello!' },
392
- };
393
- await service.handleMessage(connection, message);
394
- expect(redisService.publish).toHaveBeenCalledWith(expect.stringContaining('worker'), expect.objectContaining({
395
- type: 'client-message',
396
- msgType: 'chat',
397
- userId: 'user-1',
398
- roomId: 'room-123',
399
- }));
400
- });
401
- });
402
- });
403
- //# sourceMappingURL=message-router.service.spec.js.map