@webex/contact-center 3.11.0 → 3.12.0-llmrefactor.2

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 (267) hide show
  1. package/.sdd/manifest.json +882 -0
  2. package/AGENTS.md +94 -0
  3. package/ai-docs/ARCHITECTURE.md +168 -0
  4. package/ai-docs/CONTRACTS.md +46 -0
  5. package/ai-docs/GETTING_STARTED.md +168 -0
  6. package/ai-docs/GLOSSARY.md +43 -0
  7. package/ai-docs/README.md +138 -0
  8. package/ai-docs/REVIEW_CHECKLIST.md +41 -0
  9. package/ai-docs/RULES.md +444 -0
  10. package/ai-docs/SECURITY.md +52 -0
  11. package/ai-docs/SERVICE_STATE.md +48 -0
  12. package/ai-docs/SPEC_INDEX.md +65 -0
  13. package/ai-docs/adr/0001-spec-source-policy.md +55 -0
  14. package/ai-docs/adr/README.md +8 -0
  15. package/ai-docs/adr/_adr-template.md +31 -0
  16. package/ai-docs/contact-center-spec.md +341 -0
  17. package/ai-docs/features/generated-spec-conformance-fidelity-remediation/spec/feature-spec.md +117 -0
  18. package/ai-docs/features/residual-warning-coverage-completion/spec/feature-spec.md +203 -0
  19. package/ai-docs/features/validator-code-fidelity-drift-fix/spec/feature-spec.md +315 -0
  20. package/ai-docs/patterns/event-driven-patterns.md +485 -0
  21. package/ai-docs/patterns/testing-patterns.md +480 -0
  22. package/ai-docs/patterns/typescript-patterns.md +365 -0
  23. package/ai-docs/templates/README.md +102 -0
  24. package/ai-docs/templates/documentation/create-agents-md.md +240 -0
  25. package/ai-docs/templates/documentation/create-architecture-md.md +295 -0
  26. package/ai-docs/templates/existing-service/bug-fix.md +254 -0
  27. package/ai-docs/templates/existing-service/feature-enhancement.md +450 -0
  28. package/ai-docs/templates/new-method/00-master.md +80 -0
  29. package/ai-docs/templates/new-method/01-requirements.md +232 -0
  30. package/ai-docs/templates/new-method/02-implementation.md +295 -0
  31. package/ai-docs/templates/new-method/03-tests.md +201 -0
  32. package/ai-docs/templates/new-method/04-validation.md +141 -0
  33. package/ai-docs/templates/new-service/00-master.md +109 -0
  34. package/ai-docs/templates/new-service/01-pre-questions.md +159 -0
  35. package/ai-docs/templates/new-service/02-code-generation.md +346 -0
  36. package/ai-docs/templates/new-service/03-integration.md +178 -0
  37. package/ai-docs/templates/new-service/04-test-generation.md +205 -0
  38. package/ai-docs/templates/new-service/05-validation.md +145 -0
  39. package/dist/cc.js +379 -50
  40. package/dist/cc.js.map +1 -1
  41. package/dist/config.js +6 -0
  42. package/dist/config.js.map +1 -1
  43. package/dist/constants.js +22 -2
  44. package/dist/constants.js.map +1 -1
  45. package/dist/index.js +27 -5
  46. package/dist/index.js.map +1 -1
  47. package/dist/metrics/behavioral-events.js +114 -0
  48. package/dist/metrics/behavioral-events.js.map +1 -1
  49. package/dist/metrics/constants.js +32 -3
  50. package/dist/metrics/constants.js.map +1 -1
  51. package/dist/services/ApiAiAssistant.js +318 -0
  52. package/dist/services/ApiAiAssistant.js.map +1 -0
  53. package/dist/services/UserPreference.js +427 -0
  54. package/dist/services/UserPreference.js.map +1 -0
  55. package/dist/services/agent/types.js.map +1 -1
  56. package/dist/services/config/Util.js +8 -4
  57. package/dist/services/config/Util.js.map +1 -1
  58. package/dist/services/config/constants.js +35 -2
  59. package/dist/services/config/constants.js.map +1 -1
  60. package/dist/services/config/index.js +41 -2
  61. package/dist/services/config/index.js.map +1 -1
  62. package/dist/services/config/types.js +66 -8
  63. package/dist/services/config/types.js.map +1 -1
  64. package/dist/services/constants.js +27 -1
  65. package/dist/services/constants.js.map +1 -1
  66. package/dist/services/core/Err.js.map +1 -1
  67. package/dist/services/core/Utils.js +122 -25
  68. package/dist/services/core/Utils.js.map +1 -1
  69. package/dist/services/core/aqm-reqs.js +92 -17
  70. package/dist/services/core/aqm-reqs.js.map +1 -1
  71. package/dist/services/core/websocket/WebSocketManager.js +22 -6
  72. package/dist/services/core/websocket/WebSocketManager.js.map +1 -1
  73. package/dist/services/core/websocket/connection-service.js +3 -1
  74. package/dist/services/core/websocket/connection-service.js.map +1 -1
  75. package/dist/services/core/websocket/types.js.map +1 -1
  76. package/dist/services/index.js +6 -0
  77. package/dist/services/index.js.map +1 -1
  78. package/dist/services/task/Task.js +688 -0
  79. package/dist/services/task/Task.js.map +1 -0
  80. package/dist/services/task/TaskFactory.js +45 -0
  81. package/dist/services/task/TaskFactory.js.map +1 -0
  82. package/dist/services/task/TaskManager.js +751 -457
  83. package/dist/services/task/TaskManager.js.map +1 -1
  84. package/dist/services/task/TaskUtils.js +220 -23
  85. package/dist/services/task/TaskUtils.js.map +1 -1
  86. package/dist/services/task/constants.js +23 -2
  87. package/dist/services/task/constants.js.map +1 -1
  88. package/dist/services/task/dialer.js +129 -0
  89. package/dist/services/task/dialer.js.map +1 -1
  90. package/dist/services/task/digital/Digital.js +77 -0
  91. package/dist/services/task/digital/Digital.js.map +1 -0
  92. package/dist/services/task/state-machine/TaskStateMachine.js +873 -0
  93. package/dist/services/task/state-machine/TaskStateMachine.js.map +1 -0
  94. package/dist/services/task/state-machine/actions.js +567 -0
  95. package/dist/services/task/state-machine/actions.js.map +1 -0
  96. package/dist/services/task/state-machine/constants.js +161 -0
  97. package/dist/services/task/state-machine/constants.js.map +1 -0
  98. package/dist/services/task/state-machine/guards.js +382 -0
  99. package/dist/services/task/state-machine/guards.js.map +1 -0
  100. package/dist/services/task/state-machine/index.js +53 -0
  101. package/dist/services/task/state-machine/index.js.map +1 -0
  102. package/dist/services/task/state-machine/types.js +54 -0
  103. package/dist/services/task/state-machine/types.js.map +1 -0
  104. package/dist/services/task/state-machine/uiControlsComputer.js +603 -0
  105. package/dist/services/task/state-machine/uiControlsComputer.js.map +1 -0
  106. package/dist/services/task/taskDataNormalizer.js +99 -0
  107. package/dist/services/task/taskDataNormalizer.js.map +1 -0
  108. package/dist/services/task/types.js +227 -4
  109. package/dist/services/task/types.js.map +1 -1
  110. package/dist/services/task/voice/Voice.js +1044 -0
  111. package/dist/services/task/voice/Voice.js.map +1 -0
  112. package/dist/services/task/voice/WebRTC.js +149 -0
  113. package/dist/services/task/voice/WebRTC.js.map +1 -0
  114. package/dist/types/cc.d.ts +894 -0
  115. package/dist/types/config.d.ts +72 -0
  116. package/dist/types/constants.d.ts +66 -0
  117. package/dist/types/index.d.ts +199 -0
  118. package/dist/types/logger-proxy.d.ts +71 -0
  119. package/dist/types/metrics/MetricsManager.d.ts +223 -0
  120. package/dist/types/metrics/behavioral-events.d.ts +29 -0
  121. package/dist/types/metrics/constants.d.ts +181 -0
  122. package/dist/types/services/AddressBook.d.ts +74 -0
  123. package/dist/types/services/ApiAiAssistant.d.ts +49 -0
  124. package/dist/types/services/EntryPoint.d.ts +67 -0
  125. package/dist/types/services/Queue.d.ts +76 -0
  126. package/dist/types/services/UserPreference.d.ts +118 -0
  127. package/dist/types/services/WebCallingService.d.ts +1 -0
  128. package/dist/types/services/agent/index.d.ts +46 -0
  129. package/dist/types/services/agent/types.d.ts +413 -0
  130. package/dist/types/services/config/Util.d.ts +20 -0
  131. package/dist/types/services/config/constants.d.ts +270 -0
  132. package/dist/types/services/config/index.d.ts +177 -0
  133. package/dist/types/services/config/types.d.ts +1368 -0
  134. package/dist/types/services/constants.d.ts +110 -0
  135. package/dist/types/services/core/Err.d.ts +125 -0
  136. package/dist/types/services/core/GlobalTypes.d.ts +58 -0
  137. package/dist/types/services/core/Utils.d.ts +121 -0
  138. package/dist/types/services/core/WebexRequest.d.ts +22 -0
  139. package/dist/types/services/core/aqm-reqs.d.ts +65 -0
  140. package/dist/types/services/core/constants.d.ts +99 -0
  141. package/dist/types/services/core/types.d.ts +47 -0
  142. package/dist/types/services/core/websocket/WebSocketManager.d.ts +36 -0
  143. package/dist/types/services/core/websocket/connection-service.d.ts +27 -0
  144. package/dist/types/services/core/websocket/keepalive.worker.d.ts +2 -0
  145. package/dist/types/services/core/websocket/types.d.ts +37 -0
  146. package/dist/types/services/index.d.ts +54 -0
  147. package/dist/types/services/task/AutoWrapup.d.ts +40 -0
  148. package/dist/types/services/task/Task.d.ts +157 -0
  149. package/dist/types/services/task/TaskFactory.d.ts +12 -0
  150. package/dist/types/services/task/TaskManager.d.ts +1 -0
  151. package/dist/types/services/task/TaskUtils.d.ts +138 -0
  152. package/dist/types/services/task/constants.d.ts +91 -0
  153. package/dist/types/services/task/contact.d.ts +69 -0
  154. package/dist/types/services/task/dialer.d.ts +73 -0
  155. package/dist/types/services/task/digital/Digital.d.ts +22 -0
  156. package/dist/types/services/task/state-machine/TaskStateMachine.d.ts +1194 -0
  157. package/dist/types/services/task/state-machine/actions.d.ts +10 -0
  158. package/dist/types/services/task/state-machine/constants.d.ts +107 -0
  159. package/dist/types/services/task/state-machine/guards.d.ts +102 -0
  160. package/dist/types/services/task/state-machine/index.d.ts +13 -0
  161. package/dist/types/services/task/state-machine/types.d.ts +269 -0
  162. package/dist/types/services/task/state-machine/uiControlsComputer.d.ts +9 -0
  163. package/dist/types/services/task/taskDataNormalizer.d.ts +10 -0
  164. package/dist/types/services/task/types.d.ts +1856 -0
  165. package/dist/types/services/task/voice/Voice.d.ts +184 -0
  166. package/dist/types/services/task/voice/WebRTC.d.ts +53 -0
  167. package/dist/types/types.d.ts +778 -0
  168. package/dist/types/utils/PageCache.d.ts +173 -0
  169. package/dist/types/webex-config.d.ts +53 -0
  170. package/dist/types/webex.d.ts +8 -0
  171. package/dist/types.js +130 -1
  172. package/dist/types.js.map +1 -1
  173. package/dist/webex.js +14 -2
  174. package/dist/webex.js.map +1 -1
  175. package/package.json +16 -12
  176. package/src/cc.ts +477 -51
  177. package/src/config.ts +6 -0
  178. package/src/constants.ts +21 -1
  179. package/src/index.ts +24 -5
  180. package/src/metrics/ai-docs/AGENTS.md +350 -0
  181. package/src/metrics/ai-docs/ARCHITECTURE.md +338 -0
  182. package/src/metrics/ai-docs/metrics-spec.md +854 -0
  183. package/src/metrics/behavioral-events.ts +120 -0
  184. package/src/metrics/constants.ts +37 -3
  185. package/src/services/ApiAiAssistant.ts +412 -0
  186. package/src/services/UserPreference.ts +509 -0
  187. package/src/services/agent/ai-docs/AGENTS.md +240 -0
  188. package/src/services/agent/ai-docs/ARCHITECTURE.md +304 -0
  189. package/src/services/agent/ai-docs/agent-spec.md +504 -0
  190. package/src/services/agent/types.ts +1 -1
  191. package/src/services/ai-docs/AGENTS.md +386 -0
  192. package/src/services/ai-docs/services-spec.md +492 -0
  193. package/src/services/config/Util.ts +10 -2
  194. package/src/services/config/ai-docs/AGENTS.md +255 -0
  195. package/src/services/config/ai-docs/ARCHITECTURE.md +426 -0
  196. package/src/services/config/ai-docs/config-spec.md +669 -0
  197. package/src/services/config/constants.ts +37 -1
  198. package/src/services/config/index.ts +45 -1
  199. package/src/services/config/types.ts +241 -11
  200. package/src/services/constants.ts +29 -0
  201. package/src/services/core/Err.ts +3 -0
  202. package/src/services/core/Utils.ts +143 -30
  203. package/src/services/core/ai-docs/AGENTS.md +381 -0
  204. package/src/services/core/ai-docs/ARCHITECTURE.md +698 -0
  205. package/src/services/core/ai-docs/core-spec.md +783 -0
  206. package/src/services/core/aqm-reqs.ts +100 -22
  207. package/src/services/core/websocket/WebSocketManager.ts +23 -6
  208. package/src/services/core/websocket/connection-service.ts +5 -1
  209. package/src/services/core/websocket/types.ts +1 -1
  210. package/src/services/index.ts +4 -0
  211. package/src/services/task/Task.ts +837 -0
  212. package/src/services/task/TaskFactory.ts +55 -0
  213. package/src/services/task/TaskManager.ts +793 -521
  214. package/src/services/task/TaskUtils.ts +314 -24
  215. package/src/services/task/ai-docs/AGENTS.md +457 -0
  216. package/src/services/task/ai-docs/ARCHITECTURE.md +594 -0
  217. package/src/services/task/ai-docs/task-spec.md +1319 -0
  218. package/src/services/task/constants.ts +23 -0
  219. package/src/services/task/dialer.ts +136 -1
  220. package/src/services/task/digital/Digital.ts +95 -0
  221. package/src/services/task/state-machine/TaskStateMachine.ts +1166 -0
  222. package/src/services/task/state-machine/actions.ts +738 -0
  223. package/src/services/task/state-machine/ai-docs/AGENTS.md +458 -0
  224. package/src/services/task/state-machine/ai-docs/ARCHITECTURE.md +1137 -0
  225. package/src/services/task/state-machine/ai-docs/task-state-machine-spec.md +2177 -0
  226. package/src/services/task/state-machine/constants.ts +172 -0
  227. package/src/services/task/state-machine/guards.ts +445 -0
  228. package/src/services/task/state-machine/index.ts +28 -0
  229. package/src/services/task/state-machine/types.ts +243 -0
  230. package/src/services/task/state-machine/uiControlsComputer.ts +961 -0
  231. package/src/services/task/taskDataNormalizer.ts +137 -0
  232. package/src/services/task/types.ts +734 -71
  233. package/src/services/task/voice/Voice.ts +1270 -0
  234. package/src/services/task/voice/WebRTC.ts +187 -0
  235. package/src/types.ts +205 -2
  236. package/src/utils/AGENTS.md +278 -0
  237. package/src/utils/ai-docs/utils-spec.md +381 -0
  238. package/src/webex.js +2 -0
  239. package/test/unit/spec/cc.ts +503 -43
  240. package/test/unit/spec/logger-proxy.ts +70 -0
  241. package/test/unit/spec/services/ApiAiAssistant.ts +273 -0
  242. package/test/unit/spec/services/UserPreference.ts +401 -0
  243. package/test/unit/spec/services/WebCallingService.ts +7 -1
  244. package/test/unit/spec/services/config/index.ts +85 -29
  245. package/test/unit/spec/services/core/Utils.ts +481 -2
  246. package/test/unit/spec/services/core/websocket/WebSocketManager.ts +137 -41
  247. package/test/unit/spec/services/core/websocket/connection-service.ts +3 -1
  248. package/test/unit/spec/services/task/AutoWrapup.ts +63 -0
  249. package/test/unit/spec/services/task/Task.ts +477 -0
  250. package/test/unit/spec/services/task/TaskFactory.ts +62 -0
  251. package/test/unit/spec/services/task/TaskManager.ts +1001 -1003
  252. package/test/unit/spec/services/task/TaskUtils.ts +235 -0
  253. package/test/unit/spec/services/task/dialer.ts +372 -96
  254. package/test/unit/spec/services/task/digital/Digital.ts +105 -0
  255. package/test/unit/spec/services/task/state-machine/TaskStateMachine.ts +2651 -0
  256. package/test/unit/spec/services/task/state-machine/guards.ts +637 -0
  257. package/test/unit/spec/services/task/state-machine/types.ts +18 -0
  258. package/test/unit/spec/services/task/state-machine/uiControlsComputer.ts +2663 -0
  259. package/test/unit/spec/services/task/taskTestUtils.ts +87 -0
  260. package/test/unit/spec/services/task/voice/Voice.ts +649 -0
  261. package/test/unit/spec/services/task/voice/WebRTC.ts +235 -0
  262. package/umd/contact-center.min.js +2 -2
  263. package/umd/contact-center.min.js.map +1 -1
  264. package/dist/services/task/index.js +0 -1525
  265. package/dist/services/task/index.js.map +0 -1
  266. package/src/services/task/index.ts +0 -1801
  267. package/test/unit/spec/services/task/index.ts +0 -2184
@@ -0,0 +1,401 @@
1
+ import {expect} from '@jest/globals';
2
+ import UserPreference from '../../../../src/services/UserPreference';
3
+ import {HTTP_METHODS, WebexSDK, IHttpResponse} from '../../../../src/types';
4
+ import {METRIC_EVENT_NAMES} from '../../../../src/metrics/constants';
5
+ import WebexRequest from '../../../../src/services/core/WebexRequest';
6
+ import MetricsManager from '../../../../src/metrics/MetricsManager';
7
+ import LoggerProxy from '../../../../src/logger-proxy';
8
+
9
+ jest.mock('../../../../src/metrics/MetricsManager');
10
+ jest.mock('../../../../src/logger-proxy');
11
+
12
+ describe('UserPreference', () => {
13
+ let userPreferenceAPI: UserPreference;
14
+ let mockWebex: WebexSDK;
15
+ let mockMetricsManager: jest.Mocked<MetricsManager>;
16
+
17
+ const mockGetUserId = jest.fn().mockReturnValue('test-user-id');
18
+
19
+ beforeEach(() => {
20
+ jest.clearAllMocks();
21
+ (WebexRequest as any).instance = undefined;
22
+
23
+ mockWebex = {
24
+ credentials: {
25
+ getOrgId: jest.fn().mockReturnValue('test-org-id'),
26
+ },
27
+ request: jest.fn(),
28
+ internal: {
29
+ newMetrics: {
30
+ submitBehavioralEvent: jest.fn(),
31
+ submitOperationalEvent: jest.fn(),
32
+ submitBusinessEvent: jest.fn(),
33
+ },
34
+ },
35
+ ready: true,
36
+ once: jest.fn(),
37
+ } as unknown as WebexSDK;
38
+
39
+ mockMetricsManager = {
40
+ trackEvent: jest.fn(),
41
+ timeEvent: jest.fn(),
42
+ } as unknown as jest.Mocked<MetricsManager>;
43
+ (MetricsManager.getInstance as jest.Mock).mockReturnValue(mockMetricsManager);
44
+
45
+ userPreferenceAPI = new UserPreference(mockWebex, mockGetUserId);
46
+ });
47
+
48
+ describe('constructor', () => {
49
+ it('should initialize with all required dependencies', () => {
50
+ expect(WebexRequest.getInstance({webex: mockWebex})).toBeDefined();
51
+ expect(MetricsManager.getInstance).toHaveBeenCalledWith({webex: mockWebex});
52
+ });
53
+ });
54
+
55
+ describe('getUserPreference', () => {
56
+ const mockPreference = {
57
+ id: 'pref-123',
58
+ organizationId: 'test-org-id',
59
+ userId: 'test-user-id',
60
+ preferences: {e911Reminder: true},
61
+ createdTime: 1234567890,
62
+ lastUpdatedTime: 1234567890,
63
+ };
64
+
65
+ const mockResponse: IHttpResponse = {
66
+ statusCode: 200,
67
+ method: 'GET',
68
+ url: '/organization/test-org-id/user-preference/test-user-id',
69
+ headers: {} as any,
70
+ body: mockPreference,
71
+ };
72
+
73
+ beforeEach(() => {
74
+ jest.spyOn(Date, 'now').mockReturnValue(1000);
75
+ });
76
+
77
+ afterEach(() => {
78
+ jest.restoreAllMocks();
79
+ });
80
+
81
+ it('should fetch user preferences successfully with default userId', async () => {
82
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
83
+
84
+ const result = await userPreferenceAPI.getUserPreference();
85
+
86
+ expect(mockWebex.request).toHaveBeenCalledWith({
87
+ service: 'wcc-api-gateway',
88
+ resource: 'organization/test-org-id/user-preference/test-user-id',
89
+ method: HTTP_METHODS.GET,
90
+ body: undefined,
91
+ });
92
+ expect(result).toEqual(mockPreference);
93
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
94
+ METRIC_EVENT_NAMES.USER_PREFERENCE_GET_SUCCESS
95
+ );
96
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
97
+ METRIC_EVENT_NAMES.USER_PREFERENCE_GET_SUCCESS,
98
+ expect.objectContaining({
99
+ orgId: 'test-org-id',
100
+ userId: 'test-user-id',
101
+ statusCode: 200,
102
+ }),
103
+ ['behavioral', 'operational']
104
+ );
105
+ });
106
+
107
+ it('should fetch user preferences for a specific userId', async () => {
108
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
109
+
110
+ const result = await userPreferenceAPI.getUserPreference({userId: 'specific-user-id'});
111
+
112
+ expect(mockWebex.request).toHaveBeenCalledWith({
113
+ service: 'wcc-api-gateway',
114
+ resource: 'organization/test-org-id/user-preference/specific-user-id',
115
+ method: HTTP_METHODS.GET,
116
+ body: undefined,
117
+ });
118
+ expect(result).toEqual(mockPreference);
119
+ });
120
+
121
+ it('should fetch user preferences with pagination parameters', async () => {
122
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
123
+
124
+ const result = await userPreferenceAPI.getUserPreference({page: 1, pageSize: 50});
125
+
126
+ expect(mockWebex.request).toHaveBeenCalledWith({
127
+ service: 'wcc-api-gateway',
128
+ resource: 'organization/test-org-id/user-preference/test-user-id?page=1&pageSize=50',
129
+ method: HTTP_METHODS.GET,
130
+ body: undefined,
131
+ });
132
+ expect(result).toEqual(mockPreference);
133
+ });
134
+
135
+ it('should throw error when userId is not available', async () => {
136
+ mockGetUserId.mockReturnValueOnce('');
137
+
138
+ await expect(userPreferenceAPI.getUserPreference()).rejects.toThrow(
139
+ 'UserPreference: userId is not available.'
140
+ );
141
+
142
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
143
+ });
144
+
145
+ it('should handle API errors', async () => {
146
+ const mockError = new Error('API call failed');
147
+ (mockWebex.request as jest.Mock).mockRejectedValue(mockError);
148
+
149
+ await expect(userPreferenceAPI.getUserPreference()).rejects.toThrow('API call failed');
150
+
151
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
152
+ METRIC_EVENT_NAMES.USER_PREFERENCE_GET_FAILED,
153
+ expect.objectContaining({
154
+ error: 'API call failed',
155
+ }),
156
+ ['behavioral', 'operational']
157
+ );
158
+ });
159
+ });
160
+
161
+ describe('createUserPreference', () => {
162
+ const mockCreateRequest = {
163
+ userId: 'test-user-id',
164
+ desktopPreference: '{}',
165
+ };
166
+
167
+ const mockCreatedPreference = {
168
+ id: 'pref-123',
169
+ organizationId: 'test-org-id',
170
+ userId: 'test-user-id',
171
+ preferences: {e911Reminder: true},
172
+ createdTime: 1234567890,
173
+ lastUpdatedTime: 1234567890,
174
+ };
175
+
176
+ const mockResponse: IHttpResponse = {
177
+ statusCode: 201,
178
+ method: 'POST',
179
+ url: '/organization/test-org-id/user-preference',
180
+ headers: {} as any,
181
+ body: mockCreatedPreference,
182
+ };
183
+
184
+ beforeEach(() => {
185
+ jest.spyOn(Date, 'now').mockReturnValue(1000);
186
+ });
187
+
188
+ afterEach(() => {
189
+ jest.restoreAllMocks();
190
+ });
191
+
192
+ it('should create user preferences successfully', async () => {
193
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
194
+
195
+ const result = await userPreferenceAPI.createUserPreference(mockCreateRequest);
196
+
197
+ expect(mockWebex.request).toHaveBeenCalledWith({
198
+ service: 'wcc-api-gateway',
199
+ resource: 'organization/test-org-id/user-preference',
200
+ method: HTTP_METHODS.POST,
201
+ body: mockCreateRequest,
202
+ });
203
+ expect(result).toEqual(mockCreatedPreference);
204
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
205
+ METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_SUCCESS
206
+ );
207
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
208
+ METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_SUCCESS,
209
+ expect.objectContaining({
210
+ orgId: 'test-org-id',
211
+ userId: 'test-user-id',
212
+ statusCode: 201,
213
+ }),
214
+ ['behavioral', 'operational']
215
+ );
216
+ });
217
+
218
+ it('should throw error when userId is missing in request', async () => {
219
+ const invalidRequest = {
220
+ userId: '',
221
+ desktopPreference: '{}',
222
+ };
223
+
224
+ await expect(userPreferenceAPI.createUserPreference(invalidRequest)).rejects.toThrow(
225
+ 'UserPreference: userId is required to create user preferences.'
226
+ );
227
+
228
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
229
+ });
230
+
231
+ it('should handle API errors', async () => {
232
+ const mockError = new Error('API call failed');
233
+ (mockWebex.request as jest.Mock).mockRejectedValue(mockError);
234
+
235
+ await expect(userPreferenceAPI.createUserPreference(mockCreateRequest)).rejects.toThrow(
236
+ 'API call failed'
237
+ );
238
+
239
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
240
+ METRIC_EVENT_NAMES.USER_PREFERENCE_CREATE_FAILED,
241
+ expect.objectContaining({
242
+ error: 'API call failed',
243
+ }),
244
+ ['behavioral', 'operational']
245
+ );
246
+ });
247
+ });
248
+
249
+ describe('updateUserPreference', () => {
250
+ const mockUpdateRequest = {
251
+ desktopPreference: '{}',
252
+ };
253
+
254
+ const mockUpdatedPreference = {
255
+ id: 'pref-123',
256
+ organizationId: 'test-org-id',
257
+ userId: 'test-user-id',
258
+ preferences: {e911Reminder: false},
259
+ createdTime: 1234567890,
260
+ lastUpdatedTime: 1234567899,
261
+ };
262
+
263
+ const mockResponse: IHttpResponse = {
264
+ statusCode: 200,
265
+ method: 'PUT',
266
+ url: '/organization/test-org-id/user-preference/test-user-id',
267
+ headers: {} as any,
268
+ body: mockUpdatedPreference,
269
+ };
270
+
271
+ beforeEach(() => {
272
+ jest.spyOn(Date, 'now').mockReturnValue(1000);
273
+ });
274
+
275
+ afterEach(() => {
276
+ jest.restoreAllMocks();
277
+ });
278
+
279
+ it('should update user preferences successfully', async () => {
280
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
281
+
282
+ const result = await userPreferenceAPI.updateUserPreference(
283
+ 'test-user-id',
284
+ mockUpdateRequest
285
+ );
286
+
287
+ expect(mockWebex.request).toHaveBeenCalledWith({
288
+ service: 'wcc-api-gateway',
289
+ resource: 'organization/test-org-id/user-preference/test-user-id',
290
+ method: HTTP_METHODS.PUT,
291
+ body: mockUpdateRequest,
292
+ });
293
+ expect(result).toEqual(mockUpdatedPreference);
294
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
295
+ METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_SUCCESS
296
+ );
297
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
298
+ METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_SUCCESS,
299
+ expect.objectContaining({
300
+ orgId: 'test-org-id',
301
+ userId: 'test-user-id',
302
+ statusCode: 200,
303
+ }),
304
+ ['behavioral', 'operational']
305
+ );
306
+ });
307
+
308
+ it('should throw error when userId is missing', async () => {
309
+ await expect(userPreferenceAPI.updateUserPreference('', mockUpdateRequest)).rejects.toThrow(
310
+ 'UserPreference: userId is required to update user preferences.'
311
+ );
312
+
313
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
314
+ });
315
+
316
+ it('should handle API errors', async () => {
317
+ const mockError = new Error('API call failed');
318
+ (mockWebex.request as jest.Mock).mockRejectedValue(mockError);
319
+
320
+ await expect(
321
+ userPreferenceAPI.updateUserPreference('test-user-id', mockUpdateRequest)
322
+ ).rejects.toThrow('API call failed');
323
+
324
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
325
+ METRIC_EVENT_NAMES.USER_PREFERENCE_UPDATE_FAILED,
326
+ expect.objectContaining({
327
+ error: 'API call failed',
328
+ }),
329
+ ['behavioral', 'operational']
330
+ );
331
+ });
332
+ });
333
+
334
+ describe('deleteUserPreference', () => {
335
+ const mockResponse: IHttpResponse = {
336
+ statusCode: 204,
337
+ method: 'DELETE',
338
+ url: '/organization/test-org-id/user-preference/test-user-id',
339
+ headers: {} as any,
340
+ body: null,
341
+ };
342
+
343
+ beforeEach(() => {
344
+ jest.spyOn(Date, 'now').mockReturnValue(1000);
345
+ });
346
+
347
+ afterEach(() => {
348
+ jest.restoreAllMocks();
349
+ });
350
+
351
+ it('should delete user preferences successfully', async () => {
352
+ (mockWebex.request as jest.Mock).mockResolvedValue(mockResponse);
353
+
354
+ await userPreferenceAPI.deleteUserPreference('test-user-id');
355
+
356
+ expect(mockWebex.request).toHaveBeenCalledWith({
357
+ service: 'wcc-api-gateway',
358
+ resource: 'organization/test-org-id/user-preference/test-user-id',
359
+ method: HTTP_METHODS.DELETE,
360
+ body: undefined,
361
+ });
362
+ expect(mockMetricsManager.timeEvent).toHaveBeenCalledWith(
363
+ METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_SUCCESS
364
+ );
365
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
366
+ METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_SUCCESS,
367
+ expect.objectContaining({
368
+ orgId: 'test-org-id',
369
+ userId: 'test-user-id',
370
+ statusCode: 204,
371
+ }),
372
+ ['behavioral', 'operational']
373
+ );
374
+ });
375
+
376
+ it('should throw error when userId is missing', async () => {
377
+ await expect(userPreferenceAPI.deleteUserPreference('')).rejects.toThrow(
378
+ 'UserPreference: userId is required to delete user preferences.'
379
+ );
380
+
381
+ expect(mockMetricsManager.trackEvent).not.toHaveBeenCalled();
382
+ });
383
+
384
+ it('should handle API errors', async () => {
385
+ const mockError = new Error('API call failed');
386
+ (mockWebex.request as jest.Mock).mockRejectedValue(mockError);
387
+
388
+ await expect(userPreferenceAPI.deleteUserPreference('test-user-id')).rejects.toThrow(
389
+ 'API call failed'
390
+ );
391
+
392
+ expect(mockMetricsManager.trackEvent).toHaveBeenCalledWith(
393
+ METRIC_EVENT_NAMES.USER_PREFERENCE_DELETE_FAILED,
394
+ expect.objectContaining({
395
+ error: 'API call failed',
396
+ }),
397
+ ['behavioral', 'operational']
398
+ );
399
+ });
400
+ });
401
+ });
@@ -200,11 +200,17 @@ describe('WebCallingService', () => {
200
200
 
201
201
  it('should reject if registration times out', async () => {
202
202
  line = callingClient.getLines().line1 as ILine;
203
+ jest.spyOn(global, 'setTimeout').mockImplementation(((handler: TimerHandler) => {
204
+ if (typeof handler === 'function') {
205
+ handler();
206
+ }
207
+ return 0 as unknown as NodeJS.Timeout;
208
+ }) as typeof setTimeout);
203
209
 
204
210
  const promise = webRTCCalling.registerWebCallingLine();
205
211
 
206
212
  await expect(promise).rejects.toThrow('WebCallingService Registration timed out');
207
- }, 20003); // Increased timeout to 20 seconds
213
+ });
208
214
 
209
215
  it('should handle incoming calls', async () => {
210
216
  line = callingClient.getLines().line1 as ILine;
@@ -66,7 +66,6 @@ describe('AgentConfigService', () => {
66
66
  method: 'GET',
67
67
  });
68
68
  expect(result).toEqual(mockResponse.body);
69
-
70
69
  expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching user data using CI', {
71
70
  module: CONFIG_FILE_NAME,
72
71
  method: 'getUserUsingCI',
@@ -124,7 +123,6 @@ describe('AgentConfigService', () => {
124
123
  method: 'GET',
125
124
  });
126
125
  expect(result).toEqual(mockResponse.body);
127
-
128
126
  expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching desktop profile', {
129
127
  module: CONFIG_FILE_NAME,
130
128
  method: 'getDesktopProfileById',
@@ -183,7 +181,6 @@ describe('AgentConfigService', () => {
183
181
  method: 'GET',
184
182
  });
185
183
  expect(result).toEqual(mockResponse.body);
186
-
187
184
  expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching list of teams', {
188
185
  module: CONFIG_FILE_NAME,
189
186
  method: 'getListOfTeams',
@@ -263,11 +260,10 @@ describe('AgentConfigService', () => {
263
260
 
264
261
  expect(mockWebexRequest.request).toHaveBeenCalledWith({
265
262
  service: mockWccAPIURL,
266
- resource: `organization/${mockOrgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}&filter=id=in=(${filter})&attributes=${attributes}`,
263
+ resource: `organization/${mockOrgId}/v2/auxiliary-code?page=${page}&pageSize=${pageSize}&filter=id=in=(${filter})&attributes=${attributes}&desktopProfileFilter=true`,
267
264
  method: 'GET',
268
265
  });
269
266
  expect(result).toEqual(mockResponse.body);
270
-
271
267
  expect(LoggerProxy.info).toHaveBeenCalledWith('Fetching list of aux codes', {
272
268
  module: CONFIG_FILE_NAME,
273
269
  method: 'getListOfAuxCodes',
@@ -490,6 +486,49 @@ describe('AgentConfigService', () => {
490
486
  });
491
487
  });
492
488
 
489
+ describe('getAIFeatureFlags', () => {
490
+ it('should return AI feature flags successfully', async () => {
491
+ const mockResponse = {
492
+ statusCode: 200,
493
+ body: {
494
+ data: [{realtimeTranscripts: {enable: true}}],
495
+ },
496
+ };
497
+ mockWebexRequest.request.mockResolvedValue(mockResponse);
498
+
499
+ const result = await agentConfigService.getAIFeatureFlags(mockOrgId);
500
+ expect(result).toEqual(mockResponse.body);
501
+ expect(LoggerProxy.log).toHaveBeenCalledWith('getAIFeatureFlags api success.', {
502
+ module: CONFIG_FILE_NAME,
503
+ method: 'getAIFeatureFlags',
504
+ });
505
+ });
506
+
507
+ it('should throw an error if API call returns non-200 status code', async () => {
508
+ const mockError = {statusCode: 500};
509
+ mockWebexRequest.request.mockResolvedValue(mockError);
510
+
511
+ await expect(agentConfigService.getAIFeatureFlags(mockOrgId)).rejects.toThrow(
512
+ 'API call failed with 500'
513
+ );
514
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
515
+ 'getAIFeatureFlags API call failed with Error: API call failed with 500',
516
+ {module: CONFIG_FILE_NAME, method: 'getAIFeatureFlags'}
517
+ );
518
+ });
519
+
520
+ it('should handle network errors gracefully', async () => {
521
+ const networkError = new Error('Network Error');
522
+ mockWebexRequest.request.mockRejectedValue(networkError);
523
+
524
+ await expect(agentConfigService.getAIFeatureFlags(mockOrgId)).rejects.toThrow('Network Error');
525
+ expect(LoggerProxy.error).toHaveBeenCalledWith(
526
+ 'getAIFeatureFlags API call failed with Error: Network Error',
527
+ {module: CONFIG_FILE_NAME, method: 'getAIFeatureFlags'}
528
+ );
529
+ });
530
+ });
531
+
493
532
  describe(`getDialPlanData`, () => {
494
533
  it('should return dial plan data successfully', async () => {
495
534
  const mockResponse = {statusCode: 200, body: {data: {}}}; // Adjust data accordingly
@@ -667,6 +706,32 @@ describe('AgentConfigService', () => {
667
706
  });
668
707
 
669
708
  describe('getAgentConfig', () => {
709
+ const mockTeamData = [
710
+ {id: 'team1', name: 'Support Team'},
711
+ {id: 'team2', name: 'Sales Team'},
712
+ ];
713
+
714
+ const mockOrgInfo = {
715
+ tenantId: 'tenant123',
716
+ timezone: 'GMT',
717
+ environment: 'produs1',
718
+ };
719
+
720
+ const mockSiteInfo = {
721
+ id: 'c6a5451f-5ba7-49a1-aee8-fbef70c19ece',
722
+ name: 'Site-1',
723
+ multimediaProfileId: 'c5888e6f-5661-4871-9936-cbcec7658d41',
724
+ };
725
+
726
+ const mockURLMapping = [
727
+ {key: 'ACQUEON_API_URL', url: 'https://api.example.com'},
728
+ {key: 'ACQUEON_CONSOLE_URL', url: 'https://console.example.com'},
729
+ ];
730
+
731
+ const mockAIFeatureFlags = {
732
+ data: [{realtimeTranscripts: {enable: true}}],
733
+ };
734
+
670
735
  beforeEach(() => {
671
736
  jest.clearAllMocks();
672
737
  });
@@ -681,7 +746,7 @@ describe('AgentConfigService', () => {
681
746
  agentProfileId: 'profile123',
682
747
  siteId: 'site789',
683
748
  dbId: 'db123',
684
- defaultDialledNumber: '1234567890',
749
+ deafultDialledNumber: '1234567890',
685
750
  id: 'user001',
686
751
  teamIds: ['team1', 'team2'],
687
752
  };
@@ -718,6 +783,7 @@ describe('AgentConfigService', () => {
718
783
  const mockOrgInfo = {
719
784
  tenantId: 'tenant123',
720
785
  timezone: 'GMT',
786
+ environment: 'produs1',
721
787
  };
722
788
 
723
789
  const mockOrgSettings = {
@@ -726,12 +792,6 @@ describe('AgentConfigService', () => {
726
792
  maskSensitiveData: false,
727
793
  };
728
794
 
729
- const mockSiteInfo = {
730
- id: 'c6a5451f-5ba7-49a1-aee8-fbef70c19ece',
731
- name: 'Site-1',
732
- multimediaProfileId: 'c5888e6f-5661-4871-9936-cbcec7658d41',
733
- };
734
-
735
795
  const mockTenantData = {
736
796
  timeoutDesktopInactivityEnabled: false,
737
797
  timeoutDesktopInactivityMins: 15,
@@ -745,15 +805,13 @@ describe('AgentConfigService', () => {
745
805
  callVariablesSuppressed: false,
746
806
  };
747
807
 
748
- const mockURLMapping = [
749
- {key: 'ACQUEON_API_URL', url: 'https://api.example.com'},
750
- {key: 'ACQUEON_CONSOLE_URL', url: 'https://console.example.com'},
751
- ];
752
-
753
808
  const mockAuxCodes = [
754
809
  {id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1', isDefault: true},
755
810
  {id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1', isDefault: true},
756
811
  ];
812
+ const mockAIFeatureFlags = {
813
+ data: [{realtimeTranscripts: {enable: true}}],
814
+ };
757
815
 
758
816
  const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
759
817
  agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);
@@ -762,6 +820,7 @@ describe('AgentConfigService', () => {
762
820
  agentConfigService.getSiteInfo = jest.fn().mockResolvedValue(mockSiteInfo);
763
821
  agentConfigService.getTenantData = jest.fn().mockResolvedValue(mockTenantData);
764
822
  agentConfigService.getURLMapping = jest.fn().mockResolvedValue(mockURLMapping);
823
+ agentConfigService.getAIFeatureFlags = jest.fn().mockResolvedValue(mockAIFeatureFlags);
765
824
  agentConfigService.getAllAuxCodes = jest.fn().mockResolvedValue(mockAuxCodes);
766
825
  agentConfigService.getDesktopProfileById = jest.fn().mockResolvedValue(mockAgentProfile);
767
826
  agentConfigService.getDialPlanData = jest.fn().mockResolvedValue(mockDialPlanData);
@@ -801,6 +860,7 @@ describe('AgentConfigService', () => {
801
860
  dialPlanData: mockDialPlanData,
802
861
  urlMapping: mockURLMapping,
803
862
  multimediaProfileId: mockSiteInfo.multimediaProfileId,
863
+ aiFeatureFlags: mockAIFeatureFlags,
804
864
  });
805
865
  });
806
866
 
@@ -815,7 +875,7 @@ describe('AgentConfigService', () => {
815
875
  skillProfileId: 'skillProfile456',
816
876
  siteId: 'site789',
817
877
  dbId: 'db123',
818
- defaultDialledNumber: '1234567890',
878
+ deafultDialledNumber: '1234567890',
819
879
  id: 'user001',
820
880
  teamIds: ['team1', 'team2'],
821
881
  };
@@ -857,6 +917,7 @@ describe('AgentConfigService', () => {
857
917
  const mockOrgInfo = {
858
918
  tenantId: 'tenant123',
859
919
  timezone: 'GMT',
920
+ environment: 'produs1',
860
921
  };
861
922
 
862
923
  const mockOrgSettings = {
@@ -865,12 +926,6 @@ describe('AgentConfigService', () => {
865
926
  maskSensitiveData: true,
866
927
  };
867
928
 
868
- const mockSiteInfo = {
869
- id: 'c6a5451f-5ba7-49a1-aee8-fbef70c19ece',
870
- name: 'Site-1',
871
- multimediaProfileId: 'c5888e6f-5661-4871-9936-cbcec7658d41',
872
- };
873
-
874
929
  const mockTenantData = {
875
930
  timeoutDesktopInactivityEnabled: true,
876
931
  timeoutDesktopInactivityMins: 15,
@@ -885,15 +940,13 @@ describe('AgentConfigService', () => {
885
940
  lostConnectionRecoveryTimeout: 30,
886
941
  };
887
942
 
888
- const mockURLMapping = [
889
- {key: 'ACQUEON_API_URL', url: 'https://api.example.com'},
890
- {key: 'ACQUEON_CONSOLE_URL', url: 'https://console.example.com'},
891
- ];
892
-
893
943
  const mockAuxCodes = [
894
944
  {id: 'aux1', type: 'WRAP_UP_CODE', name: 'Wrap Up Code 1'},
895
945
  {id: 'aux2', type: 'IDLE_CODE', name: 'Idle Code 1'},
896
946
  ];
947
+ const mockAIFeatureFlags = {
948
+ data: [{realtimeTranscripts: {enable: true}}],
949
+ };
897
950
 
898
951
  const parseAgentConfigsSpy = jest.spyOn(util, 'parseAgentConfigs');
899
952
  agentConfigService.getUserUsingCI = jest.fn().mockResolvedValue(mockUserConfig);
@@ -902,6 +955,7 @@ describe('AgentConfigService', () => {
902
955
  agentConfigService.getSiteInfo = jest.fn().mockResolvedValue(mockSiteInfo);
903
956
  agentConfigService.getTenantData = jest.fn().mockResolvedValue(mockTenantData);
904
957
  agentConfigService.getURLMapping = jest.fn().mockResolvedValue(mockURLMapping);
958
+ agentConfigService.getAIFeatureFlags = jest.fn().mockResolvedValue(mockAIFeatureFlags);
905
959
  agentConfigService.getAllAuxCodes = jest.fn().mockResolvedValue(mockAuxCodes);
906
960
  agentConfigService.getDesktopProfileById = jest.fn().mockResolvedValue(mockAgentProfile);
907
961
  agentConfigService.getDialPlanData = jest.fn().mockResolvedValue(mockDialPlanData);
@@ -941,6 +995,7 @@ describe('AgentConfigService', () => {
941
995
  dialPlanData: mockDialPlanData,
942
996
  urlMapping: mockURLMapping,
943
997
  multimediaProfileId: mockSiteInfo.multimediaProfileId,
998
+ aiFeatureFlags: mockAIFeatureFlags,
944
999
  });
945
1000
  });
946
1001
 
@@ -952,6 +1007,7 @@ describe('AgentConfigService', () => {
952
1007
  agentConfigService.getOrganizationSetting = jest.fn().mockResolvedValue({});
953
1008
  agentConfigService.getTenantData = jest.fn().mockResolvedValue({});
954
1009
  agentConfigService.getURLMapping = jest.fn().mockResolvedValue({});
1010
+ agentConfigService.getAIFeatureFlags = jest.fn().mockResolvedValue({data: []});
955
1011
  agentConfigService.getAllAuxCodes = jest.fn().mockResolvedValue({});
956
1012
  agentConfigService.getDesktopProfileById = jest.fn().mockResolvedValue({});
957
1013
  agentConfigService.getDialPlanData = jest.fn().mockResolvedValue({});