@adobe/spacecat-shared-tokowaka-client 1.1.0 → 1.2.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.
@@ -227,34 +227,30 @@ describe('BaseOpportunityMapper', () => {
227
227
 
228
228
  it('should remove patches by suggestion IDs using default implementation', () => {
229
229
  const config = {
230
- siteId: 'site-123',
231
- baseURL: 'https://example.com',
230
+ url: 'https://example.com/page1',
232
231
  version: '1.0',
233
- tokowakaOptimizations: {
234
- '/page1': {
235
- prerender: true,
236
- patches: [
237
- {
238
- opportunityId: 'opp-test',
239
- suggestionId: 'sugg-1',
240
- op: 'replace',
241
- value: 'value-1',
242
- },
243
- {
244
- opportunityId: 'opp-test',
245
- suggestionId: 'sugg-2',
246
- op: 'replace',
247
- value: 'value-2',
248
- },
249
- ],
232
+ forceFail: false,
233
+ prerender: true,
234
+ patches: [
235
+ {
236
+ opportunityId: 'opp-test',
237
+ suggestionId: 'sugg-1',
238
+ op: 'replace',
239
+ value: 'value-1',
250
240
  },
251
- },
241
+ {
242
+ opportunityId: 'opp-test',
243
+ suggestionId: 'sugg-2',
244
+ op: 'replace',
245
+ value: 'value-2',
246
+ },
247
+ ],
252
248
  };
253
249
 
254
250
  const result = testMapper.rollbackPatches(config, ['sugg-1'], 'opp-test');
255
251
 
256
- expect(result.tokowakaOptimizations['/page1'].patches).to.have.lengthOf(1);
257
- expect(result.tokowakaOptimizations['/page1'].patches[0].suggestionId).to.equal('sugg-2');
252
+ expect(result.patches).to.have.lengthOf(1);
253
+ expect(result.patches[0].suggestionId).to.equal('sugg-2');
258
254
  expect(result.removedCount).to.equal(1);
259
255
  });
260
256
 
@@ -268,101 +264,89 @@ describe('BaseOpportunityMapper', () => {
268
264
 
269
265
  it('should remove patches for multiple suggestion IDs', () => {
270
266
  const config = {
271
- siteId: 'site-123',
272
- baseURL: 'https://example.com',
267
+ url: 'https://example.com/page1',
273
268
  version: '1.0',
274
- tokowakaOptimizations: {
275
- '/page1': {
276
- prerender: true,
277
- patches: [
278
- {
279
- opportunityId: 'opp-test',
280
- suggestionId: 'sugg-1',
281
- op: 'replace',
282
- value: 'value-1',
283
- },
284
- {
285
- opportunityId: 'opp-test',
286
- suggestionId: 'sugg-2',
287
- op: 'replace',
288
- value: 'value-2',
289
- },
290
- {
291
- opportunityId: 'opp-test',
292
- suggestionId: 'sugg-3',
293
- op: 'replace',
294
- value: 'value-3',
295
- },
296
- ],
269
+ forceFail: false,
270
+ prerender: true,
271
+ patches: [
272
+ {
273
+ opportunityId: 'opp-test',
274
+ suggestionId: 'sugg-1',
275
+ op: 'replace',
276
+ value: 'value-1',
277
+ },
278
+ {
279
+ opportunityId: 'opp-test',
280
+ suggestionId: 'sugg-2',
281
+ op: 'replace',
282
+ value: 'value-2',
297
283
  },
298
- },
284
+ {
285
+ opportunityId: 'opp-test',
286
+ suggestionId: 'sugg-3',
287
+ op: 'replace',
288
+ value: 'value-3',
289
+ },
290
+ ],
299
291
  };
300
292
 
301
293
  const result = testMapper.rollbackPatches(config, ['sugg-1', 'sugg-3'], 'opp-test');
302
294
 
303
- expect(result.tokowakaOptimizations['/page1'].patches).to.have.lengthOf(1);
304
- expect(result.tokowakaOptimizations['/page1'].patches[0].suggestionId).to.equal('sugg-2');
295
+ expect(result.patches).to.have.lengthOf(1);
296
+ expect(result.patches[0].suggestionId).to.equal('sugg-2');
305
297
  expect(result.removedCount).to.equal(2);
306
298
  });
307
299
 
308
300
  it('should remove URL path when all patches are removed', () => {
309
301
  const config = {
310
- siteId: 'site-123',
311
- baseURL: 'https://example.com',
302
+ url: 'https://example.com/page1',
312
303
  version: '1.0',
313
- tokowakaOptimizations: {
314
- '/page1': {
315
- prerender: true,
316
- patches: [
317
- {
318
- opportunityId: 'opp-test',
319
- suggestionId: 'sugg-1',
320
- op: 'replace',
321
- value: 'value-1',
322
- },
323
- ],
304
+ forceFail: false,
305
+ prerender: true,
306
+ patches: [
307
+ {
308
+ opportunityId: 'opp-test',
309
+ suggestionId: 'sugg-1',
310
+ op: 'replace',
311
+ value: 'value-1',
324
312
  },
325
- },
313
+ ],
326
314
  };
327
315
 
328
316
  const result = testMapper.rollbackPatches(config, ['sugg-1'], 'opp-test');
329
317
 
330
- // URL path should be removed when no patches remain
331
- expect(result.tokowakaOptimizations).to.not.have.property('/page1');
318
+ // All patches removed, patches array should be empty
319
+ expect(result.patches).to.have.lengthOf(0);
332
320
  expect(result.removedCount).to.equal(1);
333
321
  });
334
322
 
335
323
  it('should preserve patches from other opportunities', () => {
336
324
  const config = {
337
- siteId: 'site-123',
338
- baseURL: 'https://example.com',
325
+ url: 'https://example.com/page1',
339
326
  version: '1.0',
340
- tokowakaOptimizations: {
341
- '/page1': {
342
- prerender: true,
343
- patches: [
344
- {
345
- opportunityId: 'opp-test',
346
- suggestionId: 'sugg-1',
347
- op: 'replace',
348
- value: 'test-value',
349
- },
350
- {
351
- opportunityId: 'opp-other',
352
- suggestionId: 'sugg-2',
353
- op: 'replace',
354
- value: 'other-value',
355
- },
356
- ],
327
+ forceFail: false,
328
+ prerender: true,
329
+ patches: [
330
+ {
331
+ opportunityId: 'opp-test',
332
+ suggestionId: 'sugg-1',
333
+ op: 'replace',
334
+ value: 'test-value',
335
+ },
336
+ {
337
+ opportunityId: 'opp-other',
338
+ suggestionId: 'sugg-2',
339
+ op: 'replace',
340
+ value: 'other-value',
357
341
  },
358
- },
342
+ ],
359
343
  };
360
344
 
361
345
  // Default implementation removes by suggestionId regardless of opportunity
362
346
  const result = testMapper.rollbackPatches(config, ['sugg-1'], 'opp-test');
363
347
 
364
- expect(result.tokowakaOptimizations['/page1'].patches).to.have.lengthOf(1);
365
- expect(result.tokowakaOptimizations['/page1'].patches[0].suggestionId).to.equal('sugg-2');
348
+ expect(result.patches).to.have.lengthOf(1);
349
+ expect(result.patches[0].suggestionId).to.equal('sugg-2');
366
350
  expect(result.removedCount).to.equal(1);
367
351
  });
368
352
  });
@@ -1325,132 +1325,96 @@ Overall, Bulk positions itself as a better choice for sports nutrition through i
1325
1325
  describe('rollbackPatches', () => {
1326
1326
  it('should remove FAQ heading when last FAQ suggestion is rolled back', () => {
1327
1327
  const config = {
1328
- siteId: 'site-123',
1329
- baseURL: 'https://example.com',
1328
+ url: 'https://example.com/page1',
1330
1329
  version: '1.0',
1331
- tokowakaOptimizations: {
1332
- '/page1': {
1333
- prerender: true,
1334
- patches: [
1335
- {
1336
- opportunityId: 'opp-faq',
1337
- // FAQ heading patch (no suggestionId)
1338
- op: 'appendChild',
1339
- selector: 'body',
1340
- value: { type: 'element', tagName: 'h2', children: [{ type: 'text', value: 'FAQs' }] },
1341
- },
1342
- {
1343
- opportunityId: 'opp-faq',
1344
- suggestionId: 'sugg-1',
1345
- op: 'appendChild',
1346
- value: { type: 'element', tagName: 'div' },
1347
- },
1348
- ],
1330
+ forceFail: false,
1331
+ prerender: true,
1332
+ patches: [
1333
+ {
1334
+ opportunityId: 'opp-faq',
1335
+ // FAQ heading patch (no suggestionId)
1336
+ op: 'appendChild',
1337
+ selector: 'body',
1338
+ value: { type: 'element', tagName: 'h2', children: [{ type: 'text', value: 'FAQs' }] },
1349
1339
  },
1350
- },
1340
+ {
1341
+ opportunityId: 'opp-faq',
1342
+ suggestionId: 'sugg-1',
1343
+ op: 'appendChild',
1344
+ value: { type: 'element', tagName: 'div' },
1345
+ },
1346
+ ],
1351
1347
  };
1352
1348
 
1353
1349
  const result = mapper.rollbackPatches(config, ['sugg-1'], 'opp-faq');
1354
1350
 
1355
1351
  // Both FAQ item and heading should be removed
1356
- expect(result.tokowakaOptimizations).to.not.have.property('/page1');
1352
+ expect(result.patches).to.have.lengthOf(0);
1357
1353
  expect(result.removedCount).to.equal(2);
1358
1354
  });
1359
1355
 
1360
1356
  it('should keep FAQ heading when other FAQ suggestions remain', () => {
1361
1357
  const config = {
1362
- siteId: 'site-123',
1363
- baseURL: 'https://example.com',
1358
+ url: 'https://example.com/page1',
1364
1359
  version: '1.0',
1365
- tokowakaOptimizations: {
1366
- '/page1': {
1367
- prerender: true,
1368
- patches: [
1369
- {
1370
- opportunityId: 'opp-faq',
1371
- // FAQ heading patch
1372
- op: 'appendChild',
1373
- selector: 'body',
1374
- value: { type: 'element', tagName: 'h2', children: [{ type: 'text', value: 'FAQs' }] },
1375
- },
1376
- {
1377
- opportunityId: 'opp-faq',
1378
- suggestionId: 'sugg-1',
1379
- op: 'appendChild',
1380
- value: { type: 'element', tagName: 'div' },
1381
- },
1382
- {
1383
- opportunityId: 'opp-faq',
1384
- suggestionId: 'sugg-2',
1385
- op: 'appendChild',
1386
- value: { type: 'element', tagName: 'div' },
1387
- },
1388
- ],
1360
+ forceFail: false,
1361
+ prerender: true,
1362
+ patches: [
1363
+ {
1364
+ opportunityId: 'opp-faq',
1365
+ // FAQ heading patch
1366
+ op: 'appendChild',
1367
+ selector: 'body',
1368
+ value: { type: 'element', tagName: 'h2', children: [{ type: 'text', value: 'FAQs' }] },
1389
1369
  },
1390
- },
1370
+ {
1371
+ opportunityId: 'opp-faq',
1372
+ suggestionId: 'sugg-1',
1373
+ op: 'appendChild',
1374
+ value: { type: 'element', tagName: 'div' },
1375
+ },
1376
+ {
1377
+ opportunityId: 'opp-faq',
1378
+ suggestionId: 'sugg-2',
1379
+ op: 'appendChild',
1380
+ value: { type: 'element', tagName: 'div' },
1381
+ },
1382
+ ],
1391
1383
  };
1392
1384
 
1393
1385
  const result = mapper.rollbackPatches(config, ['sugg-1'], 'opp-faq');
1394
1386
 
1395
1387
  // Only sugg-1 removed, heading and sugg-2 remain
1396
- expect(result.tokowakaOptimizations['/page1'].patches).to.have.lengthOf(2);
1397
- expect(result.tokowakaOptimizations['/page1'].patches[0]).to.not.have.property('suggestionId'); // Heading
1398
- expect(result.tokowakaOptimizations['/page1'].patches[1].suggestionId).to.equal('sugg-2');
1388
+ expect(result.patches).to.have.lengthOf(2);
1389
+ expect(result.patches[0]).to.not.have.property('suggestionId'); // Heading
1390
+ expect(result.patches[1].suggestionId).to.equal('sugg-2');
1399
1391
  expect(result.removedCount).to.equal(1);
1400
1392
  });
1401
1393
 
1402
1394
  it('should handle multiple URLs independently', () => {
1395
+ // Note: With the new per-URL architecture, each URL has its own config
1396
+ // This test validates that rollback works correctly for a single URL config
1403
1397
  const config = {
1404
- siteId: 'site-123',
1405
- baseURL: 'https://example.com',
1398
+ url: 'https://example.com/page1',
1406
1399
  version: '1.0',
1407
- tokowakaOptimizations: {
1408
- '/page1': {
1409
- prerender: true,
1410
- patches: [
1411
- { opportunityId: 'opp-faq', op: 'appendChild', value: 'FAQs' },
1412
- {
1413
- opportunityId: 'opp-faq',
1414
- suggestionId: 'sugg-1',
1415
- op: 'appendChild',
1416
- value: 'FAQ1',
1417
- },
1418
- ],
1419
- },
1420
- '/page2': {
1421
- prerender: true,
1422
- patches: [
1423
- { opportunityId: 'opp-faq', op: 'appendChild', value: 'FAQs' },
1424
- {
1425
- opportunityId: 'opp-faq',
1426
- suggestionId: 'sugg-2',
1427
- op: 'appendChild',
1428
- value: 'FAQ2',
1429
- },
1430
- {
1431
- opportunityId: 'opp-faq',
1432
- suggestionId: 'sugg-3',
1433
- op: 'appendChild',
1434
- value: 'FAQ3',
1435
- },
1436
- ],
1400
+ forceFail: false,
1401
+ prerender: true,
1402
+ patches: [
1403
+ { opportunityId: 'opp-faq', op: 'appendChild', value: 'FAQs' },
1404
+ {
1405
+ opportunityId: 'opp-faq',
1406
+ suggestionId: 'sugg-1',
1407
+ op: 'appendChild',
1408
+ value: 'FAQ1',
1437
1409
  },
1438
- },
1410
+ ],
1439
1411
  };
1440
1412
 
1441
- // Remove sugg-1 from page1 (should remove heading too)
1442
- // Remove sugg-2 from page2 (should keep heading because sugg-3 remains)
1443
- const result = mapper.rollbackPatches(config, ['sugg-1', 'sugg-2'], 'opp-faq');
1444
-
1445
- // page1 completely removed
1446
- expect(result.tokowakaOptimizations).to.not.have.property('/page1');
1447
-
1448
- // page2 still has heading + sugg-3
1449
- expect(result.tokowakaOptimizations['/page2'].patches).to.have.lengthOf(2);
1450
- expect(result.tokowakaOptimizations['/page2'].patches[0]).to.not.have.property('suggestionId');
1451
- expect(result.tokowakaOptimizations['/page2'].patches[1].suggestionId).to.equal('sugg-3');
1413
+ const result = mapper.rollbackPatches(config, ['sugg-1'], 'opp-faq');
1452
1414
 
1453
- expect(result.removedCount).to.equal(3); // page1: heading+sugg-1, page2: sugg-2
1415
+ // All patches removed (heading + FAQ item)
1416
+ expect(result.patches).to.have.lengthOf(0);
1417
+ expect(result.removedCount).to.equal(2);
1454
1418
  });
1455
1419
 
1456
1420
  it('should handle null/undefined config gracefully', () => {
@@ -39,7 +39,6 @@ describe('HTML Utils', () => {
39
39
  try {
40
40
  await fetchHtmlWithWarmup(
41
41
  '',
42
- 'api-key',
43
42
  'host',
44
43
  'edge-url',
45
44
  log,
@@ -51,51 +50,51 @@ describe('HTML Utils', () => {
51
50
  }
52
51
  });
53
52
 
54
- it('should throw error when apiKey is missing', async () => {
53
+ it('should throw error when forwardedHost is missing', async () => {
55
54
  try {
56
55
  await fetchHtmlWithWarmup(
57
56
  'https://example.com/page',
57
+ 'api-key',
58
58
  '',
59
- 'host',
60
59
  'edge-url',
61
60
  log,
62
61
  false,
63
62
  );
64
63
  expect.fail('Should have thrown error');
65
64
  } catch (error) {
66
- expect(error.message).to.equal('Tokowaka API key is required for fetching HTML');
65
+ expect(error.message).to.equal('Forwarded host is required for fetching HTML');
67
66
  }
68
67
  });
69
68
 
70
- it('should throw error when forwardedHost is missing', async () => {
69
+ it('should throw error when tokowakaEdgeUrl is missing', async () => {
71
70
  try {
72
71
  await fetchHtmlWithWarmup(
73
72
  'https://example.com/page',
74
73
  'api-key',
74
+ 'host',
75
75
  '',
76
- 'edge-url',
77
76
  log,
78
77
  false,
79
78
  );
80
79
  expect.fail('Should have thrown error');
81
80
  } catch (error) {
82
- expect(error.message).to.equal('Forwarded host is required for fetching HTML');
81
+ expect(error.message).to.equal('TOKOWAKA_EDGE_URL is not configured');
83
82
  }
84
83
  });
85
84
 
86
- it('should throw error when tokowakaEdgeUrl is missing', async () => {
85
+ it('should throw error when apiKey is missing', async () => {
87
86
  try {
88
87
  await fetchHtmlWithWarmup(
89
88
  'https://example.com/page',
90
- 'api-key',
91
- 'host',
92
89
  '',
90
+ 'host',
91
+ 'edge-url',
93
92
  log,
94
93
  false,
95
94
  );
96
95
  expect.fail('Should have thrown error');
97
96
  } catch (error) {
98
- expect(error.message).to.equal('TOKOWAKA_EDGE_URL is not configured');
97
+ expect(error.message).to.equal('Tokowaka API key is required for fetching HTML');
99
98
  }
100
99
  });
101
100
 
@@ -273,7 +272,6 @@ describe('HTML Utils', () => {
273
272
  // This tests the defensive 'throw lastError' fallback
274
273
  await fetchHtmlWithWarmup(
275
274
  'https://example.com/page',
276
- 'api-key',
277
275
  'host',
278
276
  'https://edge.example.com',
279
277
  log,