@tenonhq/sincronia-core 0.0.78 → 0.0.80

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.
@@ -0,0 +1,220 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for US-012: Add staleness warning for active task
4
+ *
5
+ * Validates:
6
+ * - On startup, if .sinc-active-task.json is older than 7 days, a warning is logged
7
+ * - Fresh task files produce no warning
8
+ * - Warning includes task name and age in days
9
+ */
10
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
+ if (k2 === undefined) k2 = k;
12
+ var desc = Object.getOwnPropertyDescriptor(m, k);
13
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
14
+ desc = { enumerable: true, get: function() { return m[k]; } };
15
+ }
16
+ Object.defineProperty(o, k2, desc);
17
+ }) : (function(o, m, k, k2) {
18
+ if (k2 === undefined) k2 = k;
19
+ o[k2] = m[k];
20
+ }));
21
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
22
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
23
+ }) : function(o, v) {
24
+ o["default"] = v;
25
+ });
26
+ var __importStar = (this && this.__importStar) || (function () {
27
+ var ownKeys = function(o) {
28
+ ownKeys = Object.getOwnPropertyNames || function (o) {
29
+ var ar = [];
30
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
31
+ return ar;
32
+ };
33
+ return ownKeys(o);
34
+ };
35
+ return function (mod) {
36
+ if (mod && mod.__esModule) return mod;
37
+ var result = {};
38
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
39
+ __setModuleDefault(result, mod);
40
+ return result;
41
+ };
42
+ })();
43
+ Object.defineProperty(exports, "__esModule", { value: true });
44
+ // --- Mock setup ---
45
+ const mockSNClient = {
46
+ getScopeId: jest.fn(),
47
+ getUserSysId: jest.fn(),
48
+ getCurrentAppUserPrefSysId: jest.fn(),
49
+ updateCurrentAppUserPref: jest.fn(),
50
+ createCurrentAppUserPref: jest.fn(),
51
+ changeScope: jest.fn().mockResolvedValue(undefined),
52
+ createUpdateSet: jest.fn(),
53
+ changeUpdateSet: jest.fn(),
54
+ getCurrentUpdateSet: jest.fn(),
55
+ client: {
56
+ get: jest.fn(),
57
+ },
58
+ };
59
+ jest.mock("../snClient", () => ({
60
+ defaultClient: jest.fn(() => mockSNClient),
61
+ unwrapSNResponse: jest.fn((val) => val),
62
+ }));
63
+ jest.mock("../FileUtils", () => ({
64
+ getFileContextFromPath: jest.fn(),
65
+ getFileContextWithSkipReason: jest.fn(),
66
+ }));
67
+ jest.mock("../appUtils", () => ({
68
+ groupAppFiles: jest.fn(),
69
+ pushFiles: jest.fn(),
70
+ }));
71
+ jest.mock("../logMessages", () => ({
72
+ logFilePush: jest.fn(),
73
+ }));
74
+ jest.mock("../recentEdits", () => ({
75
+ writeRecentEdit: jest.fn(),
76
+ }));
77
+ jest.mock("../Logger", () => ({
78
+ logger: {
79
+ info: jest.fn(),
80
+ error: jest.fn(),
81
+ warn: jest.fn(),
82
+ debug: jest.fn(),
83
+ success: jest.fn(),
84
+ getLogLevel: jest.fn().mockReturnValue("info"),
85
+ },
86
+ }));
87
+ jest.mock("../config", () => ({
88
+ loadConfigs: jest.fn().mockResolvedValue(undefined),
89
+ getConfig: jest.fn(),
90
+ getRootDir: jest.fn().mockReturnValue("/project"),
91
+ updateManifest: jest.fn(),
92
+ getManifest: jest.fn(),
93
+ getSourcePath: jest.fn().mockReturnValue("/project/src"),
94
+ getScopeManifestPath: jest.fn((scope) => `/project/sinc.manifest.${scope}.json`),
95
+ getManifestPath: jest.fn().mockReturnValue("/project/sinc.manifest.json"),
96
+ }));
97
+ // Filesystem mock store
98
+ var mockFsStore = {};
99
+ var mockFsStatStore = {};
100
+ jest.mock("fs", () => ({
101
+ existsSync: jest.fn((p) => {
102
+ return p in mockFsStore;
103
+ }),
104
+ readFileSync: jest.fn((p) => {
105
+ if (p in mockFsStore)
106
+ return mockFsStore[p];
107
+ throw new Error("ENOENT: " + p);
108
+ }),
109
+ writeFileSync: jest.fn((p, data) => {
110
+ mockFsStore[p] = data;
111
+ }),
112
+ statSync: jest.fn((p) => {
113
+ if (p in mockFsStatStore)
114
+ return mockFsStatStore[p];
115
+ return { mtimeMs: Date.now() };
116
+ }),
117
+ promises: {
118
+ readFile: jest.fn(),
119
+ writeFile: jest.fn(),
120
+ readdir: jest.fn(),
121
+ mkdir: jest.fn(),
122
+ access: jest.fn(),
123
+ stat: jest.fn(),
124
+ },
125
+ }));
126
+ jest.mock("chokidar", () => ({
127
+ watch: jest.fn(() => ({
128
+ on: jest.fn().mockReturnThis(),
129
+ close: jest.fn(),
130
+ })),
131
+ }));
132
+ jest.mock("lodash", () => {
133
+ var actual = jest.requireActual("lodash");
134
+ return {
135
+ ...actual,
136
+ debounce: jest.fn((fn) => {
137
+ var wrapper = jest.fn();
138
+ wrapper.cancel = jest.fn();
139
+ wrapper.flush = jest.fn(() => fn());
140
+ return wrapper;
141
+ }),
142
+ };
143
+ });
144
+ jest.mock("axios", () => ({
145
+ default: {
146
+ create: jest.fn(() => ({
147
+ get: jest.fn().mockResolvedValue({ data: { result: null } }),
148
+ })),
149
+ },
150
+ }));
151
+ // --- Imports ---
152
+ const Logger_1 = require("../Logger");
153
+ const MultiScopeWatcher_1 = require("../MultiScopeWatcher");
154
+ const path = __importStar(require("path"));
155
+ // --- Tests ---
156
+ describe("US-012: Add staleness warning for active task", () => {
157
+ var taskPath;
158
+ beforeEach(() => {
159
+ jest.clearAllMocks();
160
+ mockFsStore = {};
161
+ mockFsStatStore = {};
162
+ taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
163
+ });
164
+ it("should warn when active task file is older than 7 days", () => {
165
+ var validTask = {
166
+ taskId: "TASK-123",
167
+ taskName: "Old Feature",
168
+ updateSetName: "CU-TASK-123",
169
+ };
170
+ mockFsStore[taskPath] = JSON.stringify(validTask);
171
+ // 10 days ago
172
+ mockFsStatStore[taskPath] = { mtimeMs: Date.now() - (10 * 24 * 60 * 60 * 1000) };
173
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
174
+ expect(result).not.toBeNull();
175
+ expect(result.taskId).toBe("TASK-123");
176
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("Old Feature"));
177
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("10 days ago"));
178
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("sinc task clear"));
179
+ });
180
+ it("should not warn when active task file is fresh (under 7 days)", () => {
181
+ var validTask = {
182
+ taskId: "TASK-456",
183
+ taskName: "Recent Feature",
184
+ updateSetName: "CU-TASK-456",
185
+ };
186
+ mockFsStore[taskPath] = JSON.stringify(validTask);
187
+ // 2 days ago
188
+ mockFsStatStore[taskPath] = { mtimeMs: Date.now() - (2 * 24 * 60 * 60 * 1000) };
189
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
190
+ expect(result).not.toBeNull();
191
+ expect(result.taskId).toBe("TASK-456");
192
+ expect(Logger_1.logger.warn).not.toHaveBeenCalled();
193
+ });
194
+ it("should use taskId as fallback when taskName is missing", () => {
195
+ var taskNoName = {
196
+ taskId: "TASK-789",
197
+ updateSetName: "CU-TASK-789",
198
+ };
199
+ mockFsStore[taskPath] = JSON.stringify(taskNoName);
200
+ // 14 days ago
201
+ mockFsStatStore[taskPath] = { mtimeMs: Date.now() - (14 * 24 * 60 * 60 * 1000) };
202
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
203
+ expect(result).not.toBeNull();
204
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("TASK-789"));
205
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("14 days ago"));
206
+ });
207
+ it("should not warn when task file is exactly 6 days old", () => {
208
+ var validTask = {
209
+ taskId: "TASK-EDGE",
210
+ taskName: "Edge Case",
211
+ updateSetName: "CU-TASK-EDGE",
212
+ };
213
+ mockFsStore[taskPath] = JSON.stringify(validTask);
214
+ // 6 days ago
215
+ mockFsStatStore[taskPath] = { mtimeMs: Date.now() - (6 * 24 * 60 * 60 * 1000) };
216
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
217
+ expect(result).not.toBeNull();
218
+ expect(Logger_1.logger.warn).not.toHaveBeenCalled();
219
+ });
220
+ });
@@ -0,0 +1,304 @@
1
+ "use strict";
2
+ /**
3
+ * Tests for US-010: Validate task ID before ServiceNow queries
4
+ *
5
+ * Validates:
6
+ * - taskId is validated as non-empty string before constructing nameLIKECU-{taskId} query
7
+ * - Empty/undefined taskId logs an error and skips update set lookup
8
+ * - readActiveTask() validates required fields (taskId, updateSetName) and returns null if missing
9
+ * - Missing required fields produce a clear error, not a crash
10
+ */
11
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
12
+ if (k2 === undefined) k2 = k;
13
+ var desc = Object.getOwnPropertyDescriptor(m, k);
14
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
15
+ desc = { enumerable: true, get: function() { return m[k]; } };
16
+ }
17
+ Object.defineProperty(o, k2, desc);
18
+ }) : (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ o[k2] = m[k];
21
+ }));
22
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
23
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
24
+ }) : function(o, v) {
25
+ o["default"] = v;
26
+ });
27
+ var __importStar = (this && this.__importStar) || (function () {
28
+ var ownKeys = function(o) {
29
+ ownKeys = Object.getOwnPropertyNames || function (o) {
30
+ var ar = [];
31
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
32
+ return ar;
33
+ };
34
+ return ownKeys(o);
35
+ };
36
+ return function (mod) {
37
+ if (mod && mod.__esModule) return mod;
38
+ var result = {};
39
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
40
+ __setModuleDefault(result, mod);
41
+ return result;
42
+ };
43
+ })();
44
+ Object.defineProperty(exports, "__esModule", { value: true });
45
+ // --- Mock setup ---
46
+ const mockSNClient = {
47
+ getScopeId: jest.fn(),
48
+ getUserSysId: jest.fn(),
49
+ getCurrentAppUserPrefSysId: jest.fn(),
50
+ updateCurrentAppUserPref: jest.fn(),
51
+ createCurrentAppUserPref: jest.fn(),
52
+ changeScope: jest.fn().mockResolvedValue(undefined),
53
+ createUpdateSet: jest.fn(),
54
+ changeUpdateSet: jest.fn(),
55
+ getCurrentUpdateSet: jest.fn(),
56
+ client: {
57
+ get: jest.fn(),
58
+ },
59
+ };
60
+ jest.mock("../snClient", () => ({
61
+ defaultClient: jest.fn(() => mockSNClient),
62
+ unwrapSNResponse: jest.fn((val) => val),
63
+ }));
64
+ jest.mock("../FileUtils", () => ({
65
+ getFileContextFromPath: jest.fn(),
66
+ getFileContextWithSkipReason: jest.fn(),
67
+ }));
68
+ jest.mock("../appUtils", () => ({
69
+ groupAppFiles: jest.fn(),
70
+ pushFiles: jest.fn(),
71
+ }));
72
+ jest.mock("../logMessages", () => ({
73
+ logFilePush: jest.fn(),
74
+ }));
75
+ jest.mock("../recentEdits", () => ({
76
+ writeRecentEdit: jest.fn(),
77
+ }));
78
+ jest.mock("../Logger", () => ({
79
+ logger: {
80
+ info: jest.fn(),
81
+ error: jest.fn(),
82
+ warn: jest.fn(),
83
+ debug: jest.fn(),
84
+ success: jest.fn(),
85
+ getLogLevel: jest.fn().mockReturnValue("info"),
86
+ },
87
+ }));
88
+ jest.mock("../config", () => ({
89
+ loadConfigs: jest.fn().mockResolvedValue(undefined),
90
+ getConfig: jest.fn(),
91
+ getRootDir: jest.fn().mockReturnValue("/project"),
92
+ updateManifest: jest.fn(),
93
+ getManifest: jest.fn(),
94
+ getSourcePath: jest.fn().mockReturnValue("/project/src"),
95
+ getScopeManifestPath: jest.fn((scope) => `/project/sinc.manifest.${scope}.json`),
96
+ getManifestPath: jest.fn().mockReturnValue("/project/sinc.manifest.json"),
97
+ }));
98
+ // Filesystem mock store
99
+ var mockFsStore = {};
100
+ jest.mock("fs", () => ({
101
+ existsSync: jest.fn((p) => {
102
+ return p in mockFsStore;
103
+ }),
104
+ readFileSync: jest.fn((p) => {
105
+ if (p in mockFsStore)
106
+ return mockFsStore[p];
107
+ throw new Error("ENOENT: " + p);
108
+ }),
109
+ writeFileSync: jest.fn((p, data) => {
110
+ mockFsStore[p] = data;
111
+ }),
112
+ statSync: jest.fn(() => ({ mtimeMs: Date.now() })),
113
+ promises: {
114
+ readFile: jest.fn(),
115
+ writeFile: jest.fn(),
116
+ readdir: jest.fn(),
117
+ mkdir: jest.fn(),
118
+ access: jest.fn(),
119
+ stat: jest.fn(),
120
+ },
121
+ }));
122
+ jest.mock("chokidar", () => ({
123
+ watch: jest.fn(() => ({
124
+ on: jest.fn().mockReturnThis(),
125
+ close: jest.fn(),
126
+ })),
127
+ }));
128
+ jest.mock("lodash", () => {
129
+ var actual = jest.requireActual("lodash");
130
+ return {
131
+ ...actual,
132
+ debounce: jest.fn((fn) => {
133
+ var wrapper = jest.fn();
134
+ wrapper.cancel = jest.fn();
135
+ wrapper.flush = jest.fn(() => fn());
136
+ return wrapper;
137
+ }),
138
+ };
139
+ });
140
+ jest.mock("axios", () => ({
141
+ default: {
142
+ create: jest.fn(() => ({
143
+ get: jest.fn().mockResolvedValue({ data: { result: null } }),
144
+ })),
145
+ },
146
+ }));
147
+ // --- Imports ---
148
+ const Logger_1 = require("../Logger");
149
+ const MultiScopeWatcher_1 = require("../MultiScopeWatcher");
150
+ const path = __importStar(require("path"));
151
+ // --- Tests ---
152
+ describe("US-010: Validate task ID before ServiceNow queries", () => {
153
+ beforeEach(() => {
154
+ jest.clearAllMocks();
155
+ mockFsStore = {};
156
+ // Default: scope switching succeeds
157
+ mockSNClient.getScopeId.mockResolvedValue([{ sys_id: "scope_sys_id" }]);
158
+ mockSNClient.getUserSysId.mockResolvedValue([{ sys_id: "user_sys_id" }]);
159
+ mockSNClient.getCurrentAppUserPrefSysId.mockResolvedValue([{ sys_id: "pref_sys_id" }]);
160
+ mockSNClient.updateCurrentAppUserPref.mockResolvedValue({});
161
+ mockSNClient.createCurrentAppUserPref.mockResolvedValue({});
162
+ });
163
+ afterEach(() => {
164
+ (0, MultiScopeWatcher_1.stopMultiScopeWatching)();
165
+ });
166
+ describe("readActiveTask() validation", () => {
167
+ it("returns null and logs error when taskId is empty string", () => {
168
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
169
+ mockFsStore[taskPath] = JSON.stringify({
170
+ taskId: "",
171
+ taskName: "Test Task",
172
+ updateSetName: "CU- Test Task",
173
+ description: "Test",
174
+ taskUrl: "",
175
+ scopes: {},
176
+ });
177
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
178
+ expect(result).toBeNull();
179
+ expect(Logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining("missing a valid taskId"));
180
+ });
181
+ it("returns null and logs error when taskId is undefined", () => {
182
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
183
+ mockFsStore[taskPath] = JSON.stringify({
184
+ taskName: "Test Task",
185
+ updateSetName: "CU-abc Test Task",
186
+ description: "Test",
187
+ taskUrl: "",
188
+ scopes: {},
189
+ });
190
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
191
+ expect(result).toBeNull();
192
+ expect(Logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining("missing a valid taskId"));
193
+ });
194
+ it("returns null and logs error when updateSetName is empty string", () => {
195
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
196
+ mockFsStore[taskPath] = JSON.stringify({
197
+ taskId: "abc123",
198
+ taskName: "Test Task",
199
+ updateSetName: "",
200
+ description: "Test",
201
+ taskUrl: "",
202
+ scopes: {},
203
+ });
204
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
205
+ expect(result).toBeNull();
206
+ expect(Logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining("missing a valid updateSetName"));
207
+ });
208
+ it("returns null and logs error when updateSetName is missing", () => {
209
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
210
+ mockFsStore[taskPath] = JSON.stringify({
211
+ taskId: "abc123",
212
+ taskName: "Test Task",
213
+ description: "Test",
214
+ taskUrl: "",
215
+ scopes: {},
216
+ });
217
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
218
+ expect(result).toBeNull();
219
+ expect(Logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining("missing a valid updateSetName"));
220
+ });
221
+ it("returns valid task when both taskId and updateSetName are present", () => {
222
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
223
+ mockFsStore[taskPath] = JSON.stringify({
224
+ taskId: "abc123",
225
+ taskName: "Test Task",
226
+ updateSetName: "CU-abc123 Test Task",
227
+ description: "Test",
228
+ taskUrl: "",
229
+ scopes: {},
230
+ });
231
+ var result = MultiScopeWatcher_1.multiScopeWatcher.readActiveTask();
232
+ expect(result).not.toBeNull();
233
+ expect(result.taskId).toBe("abc123");
234
+ expect(result.updateSetName).toBe("CU-abc123 Test Task");
235
+ expect(Logger_1.logger.error).not.toHaveBeenCalled();
236
+ });
237
+ });
238
+ describe("ensureUpdateSetForScope() taskId validation", () => {
239
+ it("skips update set lookup when taskId is whitespace-only", async () => {
240
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
241
+ mockFsStore[taskPath] = JSON.stringify({
242
+ taskId: " ",
243
+ taskName: "Test Task",
244
+ updateSetName: "CU- Test Task",
245
+ description: "Test",
246
+ taskUrl: "",
247
+ scopes: {},
248
+ });
249
+ await MultiScopeWatcher_1.multiScopeWatcher.ensureUpdateSetForScope("x_test_scope");
250
+ // readActiveTask should have returned null due to whitespace-only taskId
251
+ // which triggers the no-active-task warning instead
252
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("No update set configured for scope"));
253
+ // Should NOT have made any ServiceNow API calls
254
+ expect(mockSNClient.changeScope).not.toHaveBeenCalled();
255
+ expect(mockSNClient.client.get).not.toHaveBeenCalled();
256
+ expect(mockSNClient.createUpdateSet).not.toHaveBeenCalled();
257
+ });
258
+ it("does not crash with missing required fields in task file", async () => {
259
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
260
+ mockFsStore[taskPath] = JSON.stringify({
261
+ taskName: "Partial Task",
262
+ });
263
+ // Should not throw
264
+ await MultiScopeWatcher_1.multiScopeWatcher.ensureUpdateSetForScope("x_test_scope");
265
+ // readActiveTask returns null → falls through to no-active-task warning
266
+ expect(Logger_1.logger.error).toHaveBeenCalledWith(expect.stringContaining("missing a valid taskId"));
267
+ expect(Logger_1.logger.warn).toHaveBeenCalledWith(expect.stringContaining("No update set configured for scope"));
268
+ // No ServiceNow API calls
269
+ expect(mockSNClient.changeScope).not.toHaveBeenCalled();
270
+ });
271
+ it("proceeds with valid taskId and makes ServiceNow query", async () => {
272
+ var taskPath = path.resolve(process.cwd(), ".sinc-active-task.json");
273
+ mockFsStore[taskPath] = JSON.stringify({
274
+ taskId: "abc123",
275
+ taskName: "Test Task",
276
+ updateSetName: "CU-abc123 Test Task",
277
+ description: "Test",
278
+ taskUrl: "",
279
+ scopes: {},
280
+ });
281
+ // Mock: scope found, existing update set found
282
+ mockSNClient.getScopeId.mockResolvedValue([{ sys_id: "scope_sys_id" }]);
283
+ mockSNClient.client.get.mockResolvedValue({
284
+ data: { result: [{ sys_id: "us_123", name: "CU-abc123 Test Task", state: "in progress" }] },
285
+ });
286
+ mockSNClient.changeUpdateSet.mockResolvedValue(undefined);
287
+ mockSNClient.getCurrentUpdateSet.mockResolvedValue({
288
+ data: { result: { sysId: "us_123", name: "CU-abc123 Test Task" } },
289
+ });
290
+ await MultiScopeWatcher_1.multiScopeWatcher.ensureUpdateSetForScope("x_valid_scope");
291
+ // Should have proceeded to search ServiceNow
292
+ expect(mockSNClient.client.get).toHaveBeenCalled();
293
+ // The query should contain the taskId
294
+ var getCall = mockSNClient.client.get.mock.calls[0];
295
+ expect(getCall[1].params.sysparm_query).toContain("CU-abc123");
296
+ // No "empty taskId" error
297
+ var errorCalls = Logger_1.logger.error.mock.calls.map(function (c) { return c[0]; });
298
+ var hasEmptyTaskIdError = errorCalls.some(function (msg) {
299
+ return msg.indexOf("empty taskId") !== -1;
300
+ });
301
+ expect(hasEmptyTaskIdError).toBe(false);
302
+ });
303
+ });
304
+ });