@adobe/spacecat-shared-tokowaka-client 1.3.2 → 1.4.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.
- package/CHANGELOG.md +14 -0
- package/README.md +107 -7
- package/package.json +1 -1
- package/src/cdn/cdn-client-registry.js +2 -0
- package/src/cdn/fastly-cdn-client.js +156 -0
- package/src/index.d.ts +47 -11
- package/src/index.js +138 -81
- package/src/mappers/generic-mapper.js +5 -1
- package/test/cdn/fastly-cdn-client.test.js +484 -0
- package/test/index.test.js +313 -113
- package/test/mappers/generic-mapper.test.js +137 -2
package/test/index.test.js
CHANGED
|
@@ -368,25 +368,6 @@ describe('TokowakaClient', () => {
|
|
|
368
368
|
expect(command.input.Key).to.equal('opportunities/example.com/config');
|
|
369
369
|
});
|
|
370
370
|
|
|
371
|
-
it('should fetch metaconfig from preview bucket', async () => {
|
|
372
|
-
const metaconfig = {
|
|
373
|
-
siteId: 'site-123',
|
|
374
|
-
prerender: true,
|
|
375
|
-
};
|
|
376
|
-
|
|
377
|
-
s3Client.send.resolves({
|
|
378
|
-
Body: {
|
|
379
|
-
transformToString: async () => JSON.stringify(metaconfig),
|
|
380
|
-
},
|
|
381
|
-
});
|
|
382
|
-
|
|
383
|
-
await client.fetchMetaconfig('https://example.com/page1', true);
|
|
384
|
-
|
|
385
|
-
const command = s3Client.send.firstCall.args[0];
|
|
386
|
-
expect(command.input.Bucket).to.equal('test-preview-bucket');
|
|
387
|
-
expect(command.input.Key).to.equal('preview/opportunities/example.com/config');
|
|
388
|
-
});
|
|
389
|
-
|
|
390
371
|
it('should return null if metaconfig does not exist', async () => {
|
|
391
372
|
const noSuchKeyError = new Error('NoSuchKey');
|
|
392
373
|
noSuchKeyError.name = 'NoSuchKey';
|
|
@@ -441,20 +422,6 @@ describe('TokowakaClient', () => {
|
|
|
441
422
|
expect(JSON.parse(command.input.Body)).to.deep.equal(metaconfig);
|
|
442
423
|
});
|
|
443
424
|
|
|
444
|
-
it('should upload metaconfig to preview bucket', async () => {
|
|
445
|
-
const metaconfig = {
|
|
446
|
-
siteId: 'site-123',
|
|
447
|
-
prerender: true,
|
|
448
|
-
};
|
|
449
|
-
|
|
450
|
-
const s3Path = await client.uploadMetaconfig('https://example.com/page1', metaconfig, true);
|
|
451
|
-
|
|
452
|
-
expect(s3Path).to.equal('preview/opportunities/example.com/config');
|
|
453
|
-
|
|
454
|
-
const command = s3Client.send.firstCall.args[0];
|
|
455
|
-
expect(command.input.Bucket).to.equal('test-preview-bucket');
|
|
456
|
-
});
|
|
457
|
-
|
|
458
425
|
it('should throw error if URL is missing', async () => {
|
|
459
426
|
try {
|
|
460
427
|
await client.uploadMetaconfig('', { siteId: 'site-123', prerender: true });
|
|
@@ -810,16 +777,19 @@ describe('TokowakaClient', () => {
|
|
|
810
777
|
|
|
811
778
|
describe('deploySuggestions', () => {
|
|
812
779
|
beforeEach(() => {
|
|
813
|
-
// Stub CDN invalidation for deploy tests
|
|
814
|
-
sinon.stub(client, 'invalidateCdnCache').resolves({
|
|
780
|
+
// Stub CDN invalidation for deploy tests (now handles both single and batch)
|
|
781
|
+
sinon.stub(client, 'invalidateCdnCache').resolves([{
|
|
815
782
|
status: 'success',
|
|
816
783
|
provider: 'cloudfront',
|
|
817
784
|
invalidationId: 'I123',
|
|
818
|
-
});
|
|
785
|
+
}]);
|
|
819
786
|
// Stub fetchConfig to return null by default (no existing config)
|
|
820
787
|
sinon.stub(client, 'fetchConfig').resolves(null);
|
|
821
|
-
// Stub fetchMetaconfig to return
|
|
822
|
-
sinon.stub(client, 'fetchMetaconfig').resolves(
|
|
788
|
+
// Stub fetchMetaconfig to return existing metaconfig (required for deployment)
|
|
789
|
+
sinon.stub(client, 'fetchMetaconfig').resolves({
|
|
790
|
+
siteId: 'site-123',
|
|
791
|
+
prerender: true,
|
|
792
|
+
});
|
|
823
793
|
// Stub uploadMetaconfig
|
|
824
794
|
sinon.stub(client, 'uploadMetaconfig').resolves('opportunities/example.com/config');
|
|
825
795
|
});
|
|
@@ -835,43 +805,28 @@ describe('TokowakaClient', () => {
|
|
|
835
805
|
expect(result.s3Paths).to.be.an('array').with.length(1);
|
|
836
806
|
expect(result.s3Paths[0]).to.equal('opportunities/example.com/L3BhZ2Ux');
|
|
837
807
|
expect(result).to.have.property('cdnInvalidations');
|
|
808
|
+
// Only 1 invalidation result returned (for batch URLs)
|
|
809
|
+
// Metaconfig invalidation happens inside uploadMetaconfig() automatically
|
|
838
810
|
expect(result.cdnInvalidations).to.be.an('array').with.length(1);
|
|
839
811
|
expect(result.succeededSuggestions).to.have.length(2);
|
|
840
812
|
expect(result.failedSuggestions).to.have.length(0);
|
|
841
813
|
expect(s3Client.send).to.have.been.called;
|
|
842
814
|
});
|
|
843
815
|
|
|
844
|
-
it('should
|
|
845
|
-
|
|
846
|
-
mockSite,
|
|
847
|
-
mockOpportunity,
|
|
848
|
-
mockSuggestions,
|
|
849
|
-
);
|
|
850
|
-
|
|
851
|
-
expect(client.fetchMetaconfig).to.have.been.calledOnce;
|
|
852
|
-
expect(client.uploadMetaconfig).to.have.been.calledOnce;
|
|
816
|
+
it('should throw error if metaconfig does not exist', async () => {
|
|
817
|
+
client.fetchMetaconfig.resolves(null);
|
|
853
818
|
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
});
|
|
866
|
-
|
|
867
|
-
await client.deploySuggestions(
|
|
868
|
-
mockSite,
|
|
869
|
-
mockOpportunity,
|
|
870
|
-
mockSuggestions,
|
|
871
|
-
);
|
|
872
|
-
|
|
873
|
-
expect(client.fetchMetaconfig).to.have.been.calledOnce;
|
|
874
|
-
expect(client.uploadMetaconfig).to.not.have.been.called;
|
|
819
|
+
try {
|
|
820
|
+
await client.deploySuggestions(
|
|
821
|
+
mockSite,
|
|
822
|
+
mockOpportunity,
|
|
823
|
+
mockSuggestions,
|
|
824
|
+
);
|
|
825
|
+
expect.fail('Should have thrown error');
|
|
826
|
+
} catch (error) {
|
|
827
|
+
expect(error.message).to.include('No domain-level metaconfig found');
|
|
828
|
+
expect(error.status).to.equal(400);
|
|
829
|
+
}
|
|
875
830
|
});
|
|
876
831
|
|
|
877
832
|
it('should handle suggestions for multiple URLs', async () => {
|
|
@@ -911,7 +866,9 @@ describe('TokowakaClient', () => {
|
|
|
911
866
|
);
|
|
912
867
|
|
|
913
868
|
expect(result.s3Paths).to.have.length(2);
|
|
914
|
-
|
|
869
|
+
// Only 1 invalidation result returned (for batch URLs)
|
|
870
|
+
// Metaconfig invalidation happens inside uploadMetaconfig() automatically
|
|
871
|
+
expect(result.cdnInvalidations).to.have.length(1);
|
|
915
872
|
expect(result.succeededSuggestions).to.have.length(2);
|
|
916
873
|
});
|
|
917
874
|
|
|
@@ -1147,6 +1104,8 @@ describe('TokowakaClient', () => {
|
|
|
1147
1104
|
expect(result.succeededSuggestions).to.have.length(1);
|
|
1148
1105
|
expect(result.failedSuggestions).to.have.length(0);
|
|
1149
1106
|
expect(result.s3Paths).to.have.length(1);
|
|
1107
|
+
// Only 1 invalidation result returned (for batch URLs)
|
|
1108
|
+
// Metaconfig invalidation happens inside uploadMetaconfig() automatically
|
|
1150
1109
|
expect(result.cdnInvalidations).to.have.length(1);
|
|
1151
1110
|
|
|
1152
1111
|
// Verify uploaded config has no patches but prerender is enabled
|
|
@@ -1155,12 +1114,12 @@ describe('TokowakaClient', () => {
|
|
|
1155
1114
|
expect(uploadedConfig.prerender).to.equal(true);
|
|
1156
1115
|
expect(uploadedConfig.url).to.equal('https://example.com/page1');
|
|
1157
1116
|
|
|
1158
|
-
// Verify CDN was invalidated
|
|
1117
|
+
// Verify CDN was invalidated using batch method with new options signature
|
|
1159
1118
|
expect(client.invalidateCdnCache).to.have.been.calledOnce;
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
'
|
|
1163
|
-
);
|
|
1119
|
+
const invalidateCall = client.invalidateCdnCache.firstCall.args[0];
|
|
1120
|
+
expect(invalidateCall).to.deep.include({
|
|
1121
|
+
urls: ['https://example.com/page1'],
|
|
1122
|
+
});
|
|
1164
1123
|
});
|
|
1165
1124
|
|
|
1166
1125
|
it('should throw error for unsupported opportunity type', async () => {
|
|
@@ -1254,12 +1213,12 @@ describe('TokowakaClient', () => {
|
|
|
1254
1213
|
|
|
1255
1214
|
describe('rollbackSuggestions', () => {
|
|
1256
1215
|
beforeEach(() => {
|
|
1257
|
-
// Stub CDN invalidation for rollback tests
|
|
1258
|
-
sinon.stub(client, 'invalidateCdnCache').resolves({
|
|
1216
|
+
// Stub CDN invalidation for rollback tests (now handles both single and batch)
|
|
1217
|
+
sinon.stub(client, 'invalidateCdnCache').resolves([{
|
|
1259
1218
|
status: 'success',
|
|
1260
1219
|
provider: 'cloudfront',
|
|
1261
1220
|
invalidationId: 'I123',
|
|
1262
|
-
});
|
|
1221
|
+
}]);
|
|
1263
1222
|
});
|
|
1264
1223
|
|
|
1265
1224
|
it('should rollback suggestions successfully', async () => {
|
|
@@ -1375,12 +1334,12 @@ describe('TokowakaClient', () => {
|
|
|
1375
1334
|
expect(uploadedConfig.patches).to.have.length(1);
|
|
1376
1335
|
expect(uploadedConfig.patches[0].suggestionId).to.equal('other-sugg-1');
|
|
1377
1336
|
|
|
1378
|
-
// Verify CDN was invalidated
|
|
1337
|
+
// Verify CDN was invalidated using batch method with new options signature
|
|
1379
1338
|
expect(client.invalidateCdnCache).to.have.been.calledOnce;
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
'
|
|
1383
|
-
);
|
|
1339
|
+
const invalidateCall = client.invalidateCdnCache.firstCall.args[0];
|
|
1340
|
+
expect(invalidateCall).to.deep.include({
|
|
1341
|
+
urls: ['https://example.com/page1'],
|
|
1342
|
+
});
|
|
1384
1343
|
});
|
|
1385
1344
|
|
|
1386
1345
|
it('should handle no existing config gracefully', async () => {
|
|
@@ -1629,7 +1588,8 @@ describe('TokowakaClient', () => {
|
|
|
1629
1588
|
);
|
|
1630
1589
|
|
|
1631
1590
|
expect(result.s3Paths).to.have.length(2);
|
|
1632
|
-
|
|
1591
|
+
// Batch invalidation returns 1 result per CDN provider, not per URL
|
|
1592
|
+
expect(result.cdnInvalidations).to.have.length(1);
|
|
1633
1593
|
expect(result.succeededSuggestions).to.have.length(2);
|
|
1634
1594
|
});
|
|
1635
1595
|
|
|
@@ -2048,10 +2008,11 @@ describe('TokowakaClient', () => {
|
|
|
2048
2008
|
);
|
|
2049
2009
|
|
|
2050
2010
|
expect(client.invalidateCdnCache).to.have.been.calledOnce;
|
|
2051
|
-
const
|
|
2052
|
-
expect(
|
|
2053
|
-
|
|
2054
|
-
|
|
2011
|
+
const invalidateCall = client.invalidateCdnCache.firstCall.args[0];
|
|
2012
|
+
expect(invalidateCall).to.deep.include({
|
|
2013
|
+
urls: ['https://example.com/page1'],
|
|
2014
|
+
isPreview: true,
|
|
2015
|
+
});
|
|
2055
2016
|
});
|
|
2056
2017
|
|
|
2057
2018
|
it('should throw error if suggestions span multiple URLs', async () => {
|
|
@@ -2118,9 +2079,12 @@ describe('TokowakaClient', () => {
|
|
|
2118
2079
|
});
|
|
2119
2080
|
|
|
2120
2081
|
it('should invalidate CDN cache successfully', async () => {
|
|
2121
|
-
const result = await client.invalidateCdnCache('https://example.com/page1', 'cloudfront');
|
|
2082
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2122
2083
|
|
|
2123
|
-
|
|
2084
|
+
// Now returns array with one result per provider
|
|
2085
|
+
expect(result).to.be.an('array');
|
|
2086
|
+
expect(result).to.have.lengthOf(1);
|
|
2087
|
+
expect(result[0]).to.deep.equal({
|
|
2124
2088
|
status: 'success',
|
|
2125
2089
|
provider: 'cloudfront',
|
|
2126
2090
|
invalidationId: 'I123',
|
|
@@ -2129,63 +2093,299 @@ describe('TokowakaClient', () => {
|
|
|
2129
2093
|
expect(mockCdnClient.invalidateCache).to.have.been.calledWith([
|
|
2130
2094
|
'/opportunities/example.com/L3BhZ2Ux',
|
|
2131
2095
|
]);
|
|
2132
|
-
expect(log.
|
|
2096
|
+
expect(log.info).to.have.been.calledWith(sinon.match(/Invalidating CDN cache/));
|
|
2133
2097
|
expect(log.info).to.have.been.calledWith(sinon.match(/CDN cache invalidation completed/));
|
|
2134
2098
|
});
|
|
2135
2099
|
|
|
2136
2100
|
it('should invalidate CDN cache for preview path', async () => {
|
|
2137
|
-
await client.invalidateCdnCache('https://example.com/page1', 'cloudfront', true);
|
|
2101
|
+
await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront', isPreview: true });
|
|
2138
2102
|
|
|
2139
2103
|
expect(mockCdnClient.invalidateCache).to.have.been.calledWith([
|
|
2140
2104
|
'/preview/opportunities/example.com/L3BhZ2Ux',
|
|
2141
2105
|
]);
|
|
2142
2106
|
});
|
|
2143
2107
|
|
|
2144
|
-
it('should
|
|
2145
|
-
|
|
2146
|
-
|
|
2147
|
-
expect.fail('Should have thrown error');
|
|
2148
|
-
} catch (error) {
|
|
2149
|
-
expect(error.message).to.equal('URL and provider are required');
|
|
2150
|
-
expect(error.status).to.equal(400);
|
|
2151
|
-
}
|
|
2108
|
+
it('should return empty array if URL array is empty', async () => {
|
|
2109
|
+
const result = await client.invalidateCdnCache({ urls: [], providers: 'cloudfront' });
|
|
2110
|
+
expect(result).to.deep.equal([]);
|
|
2152
2111
|
});
|
|
2153
2112
|
|
|
2154
|
-
it('should
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
} catch (error) {
|
|
2159
|
-
expect(error.message).to.equal('URL and provider are required');
|
|
2160
|
-
expect(error.status).to.equal(400);
|
|
2161
|
-
}
|
|
2113
|
+
it('should return empty array if provider is missing', async () => {
|
|
2114
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: '' });
|
|
2115
|
+
expect(result).to.deep.equal([]);
|
|
2116
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2162
2117
|
});
|
|
2163
2118
|
|
|
2164
2119
|
it('should return error object if no CDN client available', async () => {
|
|
2165
2120
|
client.cdnClientRegistry.getClient.returns(null);
|
|
2166
2121
|
|
|
2167
|
-
const result = await client.invalidateCdnCache('https://example.com/page1', 'cloudfront');
|
|
2122
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2168
2123
|
|
|
2169
|
-
|
|
2124
|
+
// Now returns array with one result per provider
|
|
2125
|
+
expect(result).to.be.an('array');
|
|
2126
|
+
expect(result).to.have.lengthOf(1);
|
|
2127
|
+
expect(result[0]).to.deep.equal({
|
|
2170
2128
|
status: 'error',
|
|
2171
2129
|
provider: 'cloudfront',
|
|
2172
2130
|
message: 'No CDN client available for provider: cloudfront',
|
|
2173
2131
|
});
|
|
2174
|
-
expect(log.error).to.have.been.calledWith(sinon.match(/Failed to invalidate Tokowaka CDN cache/));
|
|
2175
2132
|
});
|
|
2176
2133
|
|
|
2177
2134
|
it('should return error object if CDN invalidation fails', async () => {
|
|
2178
2135
|
mockCdnClient.invalidateCache.rejects(new Error('CDN API error'));
|
|
2179
2136
|
|
|
2180
|
-
const result = await client.invalidateCdnCache('https://example.com/page1', 'cloudfront');
|
|
2137
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2181
2138
|
|
|
2182
|
-
|
|
2139
|
+
// Now returns array with one result per provider
|
|
2140
|
+
expect(result).to.be.an('array');
|
|
2141
|
+
expect(result).to.have.lengthOf(1);
|
|
2142
|
+
expect(result[0]).to.deep.equal({
|
|
2183
2143
|
status: 'error',
|
|
2184
2144
|
provider: 'cloudfront',
|
|
2185
2145
|
message: 'CDN API error',
|
|
2186
2146
|
});
|
|
2187
2147
|
|
|
2188
|
-
expect(log.
|
|
2148
|
+
expect(log.warn).to.have.been.calledWith(sinon.match(/Failed to invalidate cloudfront CDN cache/));
|
|
2149
|
+
});
|
|
2150
|
+
});
|
|
2151
|
+
|
|
2152
|
+
describe('invalidateCdnCache (batch/multiple URLs)', () => {
|
|
2153
|
+
let mockCdnClient;
|
|
2154
|
+
|
|
2155
|
+
beforeEach(() => {
|
|
2156
|
+
mockCdnClient = {
|
|
2157
|
+
invalidateCache: sinon.stub().resolves({
|
|
2158
|
+
status: 'success',
|
|
2159
|
+
provider: 'cloudfront',
|
|
2160
|
+
invalidationId: 'I123',
|
|
2161
|
+
}),
|
|
2162
|
+
};
|
|
2163
|
+
|
|
2164
|
+
sinon.stub(client.cdnClientRegistry, 'getClient').returns(mockCdnClient);
|
|
2165
|
+
});
|
|
2166
|
+
|
|
2167
|
+
it('should invalidate CDN cache for multiple URLs (batch)', async () => {
|
|
2168
|
+
const urls = [
|
|
2169
|
+
'https://example.com/page1',
|
|
2170
|
+
'https://example.com/page2',
|
|
2171
|
+
'https://example.com/page3',
|
|
2172
|
+
];
|
|
2173
|
+
|
|
2174
|
+
// Pass array of URLs for batch invalidation
|
|
2175
|
+
const result = await client.invalidateCdnCache({ urls, providers: 'cloudfront' });
|
|
2176
|
+
|
|
2177
|
+
expect(result).to.be.an('array');
|
|
2178
|
+
expect(result).to.have.lengthOf(1);
|
|
2179
|
+
expect(result[0]).to.deep.equal({
|
|
2180
|
+
status: 'success',
|
|
2181
|
+
provider: 'cloudfront',
|
|
2182
|
+
invalidationId: 'I123',
|
|
2183
|
+
});
|
|
2184
|
+
|
|
2185
|
+
expect(mockCdnClient.invalidateCache).to.have.been.calledWith([
|
|
2186
|
+
'/opportunities/example.com/L3BhZ2Ux',
|
|
2187
|
+
'/opportunities/example.com/L3BhZ2Uy',
|
|
2188
|
+
'/opportunities/example.com/L3BhZ2Uz',
|
|
2189
|
+
]);
|
|
2190
|
+
expect(log.info).to.have.been.calledWith(sinon.match(/Invalidating CDN cache for 3 path\(s\)/));
|
|
2191
|
+
});
|
|
2192
|
+
|
|
2193
|
+
it('should return empty array for empty URLs array', async () => {
|
|
2194
|
+
const result = await client.invalidateCdnCache({ urls: [], providers: 'cloudfront' });
|
|
2195
|
+
expect(result).to.deep.equal([]);
|
|
2196
|
+
});
|
|
2197
|
+
|
|
2198
|
+
it('should return empty array if providers is empty', async () => {
|
|
2199
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: '' });
|
|
2200
|
+
expect(result).to.deep.equal([]);
|
|
2201
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2202
|
+
});
|
|
2203
|
+
|
|
2204
|
+
it('should return error object if no CDN client available', async () => {
|
|
2205
|
+
client.cdnClientRegistry.getClient.returns(null);
|
|
2206
|
+
|
|
2207
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2208
|
+
|
|
2209
|
+
expect(result).to.be.an('array');
|
|
2210
|
+
expect(result).to.have.lengthOf(1);
|
|
2211
|
+
expect(result[0]).to.deep.equal({
|
|
2212
|
+
status: 'error',
|
|
2213
|
+
provider: 'cloudfront',
|
|
2214
|
+
message: 'No CDN client available for provider: cloudfront',
|
|
2215
|
+
});
|
|
2216
|
+
});
|
|
2217
|
+
|
|
2218
|
+
it('should return error object if CDN invalidation fails', async () => {
|
|
2219
|
+
mockCdnClient.invalidateCache.rejects(new Error('CDN API error'));
|
|
2220
|
+
|
|
2221
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2222
|
+
|
|
2223
|
+
expect(result).to.be.an('array');
|
|
2224
|
+
expect(result).to.have.lengthOf(1);
|
|
2225
|
+
expect(result[0]).to.deep.equal({
|
|
2226
|
+
status: 'error',
|
|
2227
|
+
provider: 'cloudfront',
|
|
2228
|
+
message: 'CDN API error',
|
|
2229
|
+
});
|
|
2230
|
+
});
|
|
2231
|
+
|
|
2232
|
+
it('should handle multiple providers in parallel', async () => {
|
|
2233
|
+
const mockFastlyClient = {
|
|
2234
|
+
invalidateCache: sinon.stub().resolves({
|
|
2235
|
+
status: 'success',
|
|
2236
|
+
provider: 'fastly',
|
|
2237
|
+
purgeId: 'F456',
|
|
2238
|
+
}),
|
|
2239
|
+
};
|
|
2240
|
+
|
|
2241
|
+
client.cdnClientRegistry.getClient.withArgs('cloudfront').returns(mockCdnClient);
|
|
2242
|
+
client.cdnClientRegistry.getClient.withArgs('fastly').returns(mockFastlyClient);
|
|
2243
|
+
|
|
2244
|
+
const result = await client.invalidateCdnCache({
|
|
2245
|
+
urls: ['https://example.com/page1'],
|
|
2246
|
+
providers: ['cloudfront', 'fastly'],
|
|
2247
|
+
});
|
|
2248
|
+
|
|
2249
|
+
expect(result).to.be.an('array');
|
|
2250
|
+
expect(result).to.have.lengthOf(2);
|
|
2251
|
+
expect(result[0].provider).to.equal('cloudfront');
|
|
2252
|
+
expect(result[1].provider).to.equal('fastly');
|
|
2253
|
+
|
|
2254
|
+
expect(mockCdnClient.invalidateCache).to.have.been.calledOnce;
|
|
2255
|
+
expect(mockFastlyClient.invalidateCache).to.have.been.calledOnce;
|
|
2256
|
+
});
|
|
2257
|
+
|
|
2258
|
+
it('should handle errors from getClient', async () => {
|
|
2259
|
+
// Simulate an error when getting the CDN client
|
|
2260
|
+
client.cdnClientRegistry.getClient.restore();
|
|
2261
|
+
sinon.stub(client.cdnClientRegistry, 'getClient').throws(new Error('Unexpected error'));
|
|
2262
|
+
|
|
2263
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2264
|
+
|
|
2265
|
+
expect(result).to.be.an('array');
|
|
2266
|
+
expect(result).to.have.lengthOf(1);
|
|
2267
|
+
expect(result[0]).to.deep.equal({
|
|
2268
|
+
status: 'error',
|
|
2269
|
+
provider: 'cloudfront',
|
|
2270
|
+
message: 'Unexpected error',
|
|
2271
|
+
});
|
|
2272
|
+
|
|
2273
|
+
// Error is caught in provider-specific error handler
|
|
2274
|
+
expect(log.warn).to.have.been.calledWith(sinon.match(/Failed to invalidate cloudfront CDN cache/));
|
|
2275
|
+
});
|
|
2276
|
+
|
|
2277
|
+
it('should handle empty CDN provider config', async () => {
|
|
2278
|
+
// Test with existing client but passing no providers
|
|
2279
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: [] });
|
|
2280
|
+
|
|
2281
|
+
expect(result).to.be.an('array');
|
|
2282
|
+
expect(result).to.have.lengthOf(0);
|
|
2283
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2284
|
+
});
|
|
2285
|
+
|
|
2286
|
+
it('should handle multiple providers passed as array', async () => {
|
|
2287
|
+
const mockFastlyClient = {
|
|
2288
|
+
invalidateCache: sinon.stub().resolves({
|
|
2289
|
+
status: 'success',
|
|
2290
|
+
provider: 'fastly',
|
|
2291
|
+
}),
|
|
2292
|
+
};
|
|
2293
|
+
|
|
2294
|
+
client.cdnClientRegistry.getClient.restore();
|
|
2295
|
+
sinon.stub(client.cdnClientRegistry, 'getClient')
|
|
2296
|
+
.withArgs('cloudfront')
|
|
2297
|
+
.returns(mockCdnClient)
|
|
2298
|
+
.withArgs('fastly')
|
|
2299
|
+
.returns(mockFastlyClient);
|
|
2300
|
+
|
|
2301
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: ['cloudfront', 'fastly'] });
|
|
2302
|
+
|
|
2303
|
+
expect(result).to.be.an('array');
|
|
2304
|
+
expect(result).to.have.lengthOf(2);
|
|
2305
|
+
expect(mockCdnClient.invalidateCache).to.have.been.calledOnce;
|
|
2306
|
+
expect(mockFastlyClient.invalidateCache).to.have.been.calledOnce;
|
|
2307
|
+
});
|
|
2308
|
+
|
|
2309
|
+
it('should handle empty paths after filtering', async () => {
|
|
2310
|
+
// Call the method with URLs to test path generation
|
|
2311
|
+
const result = await client.invalidateCdnCache({ urls: ['https://example.com/page1'], providers: 'cloudfront' });
|
|
2312
|
+
|
|
2313
|
+
// Should successfully invalidate (paths are generated from URL)
|
|
2314
|
+
expect(result).to.be.an('array');
|
|
2315
|
+
expect(result).to.have.lengthOf(1);
|
|
2316
|
+
});
|
|
2317
|
+
});
|
|
2318
|
+
|
|
2319
|
+
describe('#getCdnProviders (edge cases)', () => {
|
|
2320
|
+
it('should handle missing CDN provider config (lines 105-106)', async () => {
|
|
2321
|
+
// Temporarily remove TOKOWAKA_CDN_PROVIDER to test early return
|
|
2322
|
+
const originalProvider = client.env.TOKOWAKA_CDN_PROVIDER;
|
|
2323
|
+
delete client.env.TOKOWAKA_CDN_PROVIDER;
|
|
2324
|
+
|
|
2325
|
+
// Call uploadMetaconfig which uses #getCdnProviders internally
|
|
2326
|
+
await client.uploadMetaconfig('https://example.com/page1', { siteId: 'test', prerender: true });
|
|
2327
|
+
|
|
2328
|
+
// No CDN invalidation should happen (no providers configured)
|
|
2329
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2330
|
+
|
|
2331
|
+
// Restore
|
|
2332
|
+
client.env.TOKOWAKA_CDN_PROVIDER = originalProvider;
|
|
2333
|
+
});
|
|
2334
|
+
|
|
2335
|
+
it('should handle array provider config with falsy values (line 111)', async () => {
|
|
2336
|
+
// Temporarily modify env to test array filtering
|
|
2337
|
+
const originalProvider = client.env.TOKOWAKA_CDN_PROVIDER;
|
|
2338
|
+
client.env.TOKOWAKA_CDN_PROVIDER = ['cloudfront', '', null, undefined]; // Array with falsy values
|
|
2339
|
+
|
|
2340
|
+
const mockCloudFrontClient = {
|
|
2341
|
+
invalidateCache: sinon.stub().resolves({
|
|
2342
|
+
status: 'success',
|
|
2343
|
+
provider: 'cloudfront',
|
|
2344
|
+
}),
|
|
2345
|
+
};
|
|
2346
|
+
|
|
2347
|
+
sinon.stub(client.cdnClientRegistry, 'getClient')
|
|
2348
|
+
.withArgs('cloudfront')
|
|
2349
|
+
.returns(mockCloudFrontClient);
|
|
2350
|
+
|
|
2351
|
+
// Call uploadMetaconfig which uses #getCdnProviders internally
|
|
2352
|
+
await client.uploadMetaconfig('https://example.com/page1', { siteId: 'test', prerender: true });
|
|
2353
|
+
|
|
2354
|
+
// Should only use 'cloudfront' (falsy values filtered out)
|
|
2355
|
+
expect(mockCloudFrontClient.invalidateCache).to.have.been.calledOnce;
|
|
2356
|
+
|
|
2357
|
+
// Restore
|
|
2358
|
+
client.env.TOKOWAKA_CDN_PROVIDER = originalProvider;
|
|
2359
|
+
});
|
|
2360
|
+
|
|
2361
|
+
it('should handle invalid CDN provider type - number (lines 120-121)', async () => {
|
|
2362
|
+
// Temporarily modify env to test invalid type
|
|
2363
|
+
const originalProvider = client.env.TOKOWAKA_CDN_PROVIDER;
|
|
2364
|
+
client.env.TOKOWAKA_CDN_PROVIDER = 12345; // Invalid type (number)
|
|
2365
|
+
|
|
2366
|
+
// Call uploadMetaconfig which uses #getCdnProviders internally
|
|
2367
|
+
await client.uploadMetaconfig('https://example.com/page1', { siteId: 'test', prerender: true });
|
|
2368
|
+
|
|
2369
|
+
// No CDN invalidation should happen (no providers)
|
|
2370
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2371
|
+
|
|
2372
|
+
// Restore
|
|
2373
|
+
client.env.TOKOWAKA_CDN_PROVIDER = originalProvider;
|
|
2374
|
+
});
|
|
2375
|
+
|
|
2376
|
+
it('should handle invalid CDN provider type - object (lines 120-121)', async () => {
|
|
2377
|
+
// Temporarily modify env to test invalid type
|
|
2378
|
+
const originalProvider = client.env.TOKOWAKA_CDN_PROVIDER;
|
|
2379
|
+
client.env.TOKOWAKA_CDN_PROVIDER = { key: 'value' }; // Invalid type (object)
|
|
2380
|
+
|
|
2381
|
+
// Call uploadMetaconfig which uses #getCdnProviders internally
|
|
2382
|
+
await client.uploadMetaconfig('https://example.com/page1', { siteId: 'test', prerender: true });
|
|
2383
|
+
|
|
2384
|
+
// No CDN invalidation should happen (no providers)
|
|
2385
|
+
expect(log.warn).to.have.been.calledWith('No CDN providers specified for cache invalidation');
|
|
2386
|
+
|
|
2387
|
+
// Restore
|
|
2388
|
+
client.env.TOKOWAKA_CDN_PROVIDER = originalProvider;
|
|
2189
2389
|
});
|
|
2190
2390
|
});
|
|
2191
2391
|
});
|