@adobe/spacecat-shared-tokowaka-client 1.2.4 → 1.3.1

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.
@@ -1040,6 +1040,59 @@ describe('TokowakaClient', () => {
1040
1040
  // Both suggestions are in result but sugg-1 skipped deployment due to no patches
1041
1041
  expect(result.succeededSuggestions).to.have.length(2);
1042
1042
  expect(result.s3Paths).to.have.length(1); // Only one URL actually deployed
1043
+ expect(log.warn).to.have.been.calledWith('No config generated for URL: https://example.com/page1');
1044
+ });
1045
+
1046
+ it('should skip URL when config has no patches after generation', async () => {
1047
+ // Stub generateConfig to return a config with no patches (defensive check)
1048
+ const originalGenerateConfig = client.generateConfig.bind(client);
1049
+ sinon.stub(client, 'generateConfig').callsFake((url, ...args) => {
1050
+ const config = originalGenerateConfig(url, ...args);
1051
+ if (config && url === 'https://example.com/page1') {
1052
+ // Return config but with empty patches array (simulating edge case)
1053
+ return { ...config, patches: [] };
1054
+ }
1055
+ return config;
1056
+ });
1057
+
1058
+ mockSuggestions = [
1059
+ {
1060
+ getId: () => 'sugg-1',
1061
+ getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
1062
+ getData: () => ({
1063
+ url: 'https://example.com/page1',
1064
+ recommendedAction: 'New Heading',
1065
+ checkType: 'heading-empty',
1066
+ transformRules: {
1067
+ action: 'replace',
1068
+ selector: 'h1',
1069
+ },
1070
+ }),
1071
+ },
1072
+ {
1073
+ getId: () => 'sugg-2',
1074
+ getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
1075
+ getData: () => ({
1076
+ url: 'https://example.com/page2',
1077
+ recommendedAction: 'New Subtitle',
1078
+ checkType: 'heading-empty',
1079
+ transformRules: {
1080
+ action: 'replace',
1081
+ selector: 'h2',
1082
+ },
1083
+ }),
1084
+ },
1085
+ ];
1086
+
1087
+ const result = await client.deploySuggestions(
1088
+ mockSite,
1089
+ mockOpportunity,
1090
+ mockSuggestions,
1091
+ );
1092
+
1093
+ expect(result.succeededSuggestions).to.have.length(2);
1094
+ expect(result.s3Paths).to.have.length(1); // Only page2 deployed
1095
+ expect(log.warn).to.have.been.calledWith('No eligible suggestions to deploy for URL: https://example.com/page1');
1043
1096
  });
1044
1097
 
1045
1098
  it('should return early when no eligible suggestions', async () => {
@@ -1066,6 +1119,50 @@ describe('TokowakaClient', () => {
1066
1119
  expect(s3Client.send).to.not.have.been.called;
1067
1120
  });
1068
1121
 
1122
+ it('should deploy prerender-only suggestions with no patches', async () => {
1123
+ // Create prerender opportunity
1124
+ const prerenderOpportunity = {
1125
+ getId: () => 'opp-prerender-123',
1126
+ getType: () => 'prerender',
1127
+ };
1128
+
1129
+ // Create prerender suggestions with no transform rules (prerender-only)
1130
+ const prerenderSuggestions = [
1131
+ {
1132
+ getId: () => 'prerender-sugg-1',
1133
+ getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
1134
+ getData: () => ({
1135
+ url: 'https://example.com/page1',
1136
+ // No transform rules - prerender only
1137
+ }),
1138
+ },
1139
+ ];
1140
+
1141
+ const result = await client.deploySuggestions(
1142
+ mockSite,
1143
+ prerenderOpportunity,
1144
+ prerenderSuggestions,
1145
+ );
1146
+
1147
+ expect(result.succeededSuggestions).to.have.length(1);
1148
+ expect(result.failedSuggestions).to.have.length(0);
1149
+ expect(result.s3Paths).to.have.length(1);
1150
+ expect(result.cdnInvalidations).to.have.length(1);
1151
+
1152
+ // Verify uploaded config has no patches but prerender is enabled
1153
+ const uploadedConfig = JSON.parse(s3Client.send.firstCall.args[0].input.Body);
1154
+ expect(uploadedConfig.patches).to.have.length(0);
1155
+ expect(uploadedConfig.prerender).to.equal(true);
1156
+ expect(uploadedConfig.url).to.equal('https://example.com/page1');
1157
+
1158
+ // Verify CDN was invalidated
1159
+ expect(client.invalidateCdnCache).to.have.been.calledOnce;
1160
+ expect(client.invalidateCdnCache).to.have.been.calledWith(
1161
+ 'https://example.com/page1',
1162
+ 'cloudfront',
1163
+ );
1164
+ });
1165
+
1069
1166
  it('should throw error for unsupported opportunity type', async () => {
1070
1167
  mockOpportunity.getType = () => 'unsupported-type';
1071
1168
 
@@ -1222,6 +1319,70 @@ describe('TokowakaClient', () => {
1222
1319
  expect(uploadedConfig.patches[0].suggestionId).to.equal('sugg-3');
1223
1320
  });
1224
1321
 
1322
+ it('should rollback prerender suggestions by disabling prerender flag', async () => {
1323
+ // Create prerender opportunity
1324
+ const prerenderOpportunity = {
1325
+ getId: () => 'opp-prerender-123',
1326
+ getType: () => 'prerender',
1327
+ };
1328
+
1329
+ // Create prerender suggestions
1330
+ const prerenderSuggestions = [
1331
+ {
1332
+ getId: () => 'prerender-sugg-1',
1333
+ getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
1334
+ getData: () => ({
1335
+ url: 'https://example.com/page1',
1336
+ }),
1337
+ },
1338
+ ];
1339
+
1340
+ const existingConfig = {
1341
+ url: 'https://example.com/page1',
1342
+ version: '1.0',
1343
+ forceFail: false,
1344
+ prerender: true,
1345
+ patches: [
1346
+ {
1347
+ op: 'replace',
1348
+ selector: 'h1',
1349
+ value: 'Heading 1',
1350
+ opportunityId: 'opp-other-123',
1351
+ suggestionId: 'other-sugg-1',
1352
+ prerenderRequired: false,
1353
+ lastUpdated: 1234567890,
1354
+ },
1355
+ ],
1356
+ };
1357
+
1358
+ sinon.stub(client, 'fetchConfig').resolves(existingConfig);
1359
+
1360
+ const result = await client.rollbackSuggestions(
1361
+ mockSite,
1362
+ prerenderOpportunity,
1363
+ prerenderSuggestions,
1364
+ );
1365
+
1366
+ expect(result.s3Paths).to.have.length(1);
1367
+ expect(result.s3Paths[0]).to.equal('opportunities/example.com/L3BhZ2Ux');
1368
+ expect(result.succeededSuggestions).to.have.length(1);
1369
+ expect(result.failedSuggestions).to.have.length(0);
1370
+ expect(result.removedPatchesCount).to.equal(1);
1371
+
1372
+ // Verify uploaded config has prerender disabled but patches intact
1373
+ const uploadedConfig = JSON.parse(s3Client.send.firstCall.args[0].input.Body);
1374
+ expect(uploadedConfig.prerender).to.equal(false);
1375
+ expect(uploadedConfig.patches).to.have.length(1);
1376
+ expect(uploadedConfig.patches[0].suggestionId).to.equal('other-sugg-1');
1377
+
1378
+ // Verify CDN was invalidated
1379
+ expect(client.invalidateCdnCache).to.have.been.calledOnce;
1380
+ expect(client.invalidateCdnCache).to.have.been.calledWith(
1381
+ 'https://example.com/page1',
1382
+ 'cloudfront',
1383
+ );
1384
+ });
1385
+
1225
1386
  it('should handle no existing config gracefully', async () => {
1226
1387
  sinon.stub(client, 'fetchConfig').resolves(null);
1227
1388
 
@@ -1262,6 +1423,30 @@ describe('TokowakaClient', () => {
1262
1423
  expect(s3Client.send).to.not.have.been.called;
1263
1424
  });
1264
1425
 
1426
+ it('should handle missing patches property in config', async () => {
1427
+ const existingConfig = {
1428
+ url: 'https://example.com/page1',
1429
+ version: '1.0',
1430
+ forceFail: false,
1431
+ prerender: true,
1432
+ // patches property is missing
1433
+ };
1434
+
1435
+ sinon.stub(client, 'fetchConfig').resolves(existingConfig);
1436
+
1437
+ const result = await client.rollbackSuggestions(
1438
+ mockSite,
1439
+ mockOpportunity,
1440
+ mockSuggestions,
1441
+ );
1442
+
1443
+ // Code marks eligible suggestions as succeeded even if patches property missing
1444
+ expect(result.succeededSuggestions).to.have.length(2);
1445
+ expect(result.failedSuggestions).to.have.length(0);
1446
+ expect(result.s3Paths).to.have.length(0);
1447
+ expect(s3Client.send).to.not.have.been.called;
1448
+ });
1449
+
1265
1450
  it('should handle suggestions not found in config', async () => {
1266
1451
  const existingConfig = {
1267
1452
  url: 'https://example.com/page1',
@@ -1589,6 +1774,64 @@ describe('TokowakaClient', () => {
1589
1774
  expect(s3Client.send).to.have.been.calledOnce;
1590
1775
  });
1591
1776
 
1777
+ it('should preview prerender-only suggestions with no patches', async () => {
1778
+ // Update fetchConfig to return existing config with deployed patches
1779
+ client.fetchConfig.resolves({
1780
+ url: 'https://example.com/page1',
1781
+ version: '1.0',
1782
+ forceFail: false,
1783
+ prerender: false,
1784
+ patches: [
1785
+ {
1786
+ op: 'replace',
1787
+ selector: 'h1',
1788
+ value: 'Existing Heading',
1789
+ opportunityId: 'opp-other-123',
1790
+ suggestionId: 'sugg-other',
1791
+ prerenderRequired: false,
1792
+ lastUpdated: 1234567890,
1793
+ },
1794
+ ],
1795
+ });
1796
+
1797
+ // Create prerender opportunity
1798
+ const prerenderOpportunity = {
1799
+ getId: () => 'opp-prerender-123',
1800
+ getType: () => 'prerender',
1801
+ };
1802
+
1803
+ // Create prerender suggestions with no transform rules (prerender-only)
1804
+ const prerenderSuggestions = [
1805
+ {
1806
+ getId: () => 'prerender-sugg-1',
1807
+ getUpdatedAt: () => '2025-01-15T10:00:00.000Z',
1808
+ getData: () => ({
1809
+ url: 'https://example.com/page1',
1810
+ // No transform rules - prerender only
1811
+ }),
1812
+ },
1813
+ ];
1814
+
1815
+ const result = await client.previewSuggestions(
1816
+ mockSite,
1817
+ prerenderOpportunity,
1818
+ prerenderSuggestions,
1819
+ { warmupDelayMs: 0 },
1820
+ );
1821
+
1822
+ expect(result).to.have.property('s3Path');
1823
+ expect(result.config).to.not.be.null;
1824
+ expect(result.config.patches).to.have.length(1); // Merged with existing deployed patch
1825
+ expect(result.config.prerender).to.equal(true); // Prerender enabled
1826
+ expect(result.succeededSuggestions).to.have.length(1);
1827
+ expect(result.failedSuggestions).to.have.length(0);
1828
+ expect(result).to.have.property('html');
1829
+
1830
+ // Verify fetch was called for HTML fetching
1831
+ expect(fetchStub.callCount).to.equal(4);
1832
+ expect(s3Client.send).to.have.been.calledOnce;
1833
+ });
1834
+
1592
1835
  it('should throw error if TOKOWAKA_EDGE_URL is not configured', async () => {
1593
1836
  delete client.env.TOKOWAKA_EDGE_URL;
1594
1837