fix: request HTTPS tiles for ion-backed Bing imagery

This commit is contained in:
hifiguy committed 2026-10-12 20:57:15 -04:00
1 parent 8b8ccc9a81
commit e5eac0c7f8
4 files changed
+204 -1

No files matched your search

+157 -1
View File
@@ -89,7 +89,7 @@ test('imagery and terrain pass their own ion token without relying on SDK defaul
calls.push({ kind: 'terrain', resource, options });
return { id: 'terrain' };
};
await createIonImagery(Cesium.IonWorldImageryStyle.AERIAL, 'imagery-token');
await createIonImagery(12345, 'imagery-token');
const result = await createWorldTerrain('terrain-token');
assert.equal(calls[0].options.accessToken, 'imagery-token');
assert.equal(calls[1].options.accessToken, 'terrain-token');
@@ -104,6 +104,162 @@ test('imagery and terrain pass their own ion token without relying on SDK defaul
}
});
test('both Bing ion stacks use HTTPS and retain ion and per-tile attribution', async (t) => {
const defaultToken = Cesium.Ion.defaultAccessToken;
const metadataCalls = [];
const bingCalls = [];
const perTileCalls = [];
const bingCredit = new Cesium.Credit('Bing tile credit', true);
const endpoint = {
type: 'IMAGERY',
externalType: 'BING',
options: Object.freeze({
url: 'https://dev.virtualearth.net',
key: 'ion-issued-bing-fixture',
mapStyle: 'Aerial',
tileProtocol: 'http',
}),
attributions: [
{ html: 'Ion required credit', collapsible: false },
{ html: 'Ion optional credit', collapsible: true },
],
};
t.mock.method(Cesium.Resource.prototype, 'fetchJson', async function () {
metadataCalls.push({
url: this.url,
token: this.queryParameters.access_token,
});
const mapStyle = this.url.includes(
`/assets/${Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS}/`,
)
? 'AerialWithLabels'
: 'Aerial';
return {
...endpoint,
options: Object.freeze({ ...endpoint.options, mapStyle }),
};
});
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', () =>
assert.fail('Bing must not inherit the local HTTP page protocol'),
);
t.mock.method(
Cesium.BingMapsImageryProvider,
'fromUrl',
async (url, options) => {
bingCalls.push({ url, options });
const provider = {
errorEvent: new Cesium.Event(),
getTileCredits(x, y, level) {
assert.equal(this, provider);
perTileCalls.push([x, y, level]);
return level === 0 ? undefined : [bingCredit];
},
};
return provider;
},
);
for (const style of [
Cesium.IonWorldImageryStyle.AERIAL,
Cesium.IonWorldImageryStyle.AERIAL_WITH_LABELS,
]) {
const provider = await createIonImagery(style, ' own-ion-token ');
const credits = provider.getTileCredits(3, 4, 5);
assert.deepEqual(
credits.map(({ html, showOnScreen }) => ({ html, showOnScreen })),
[
{ html: 'Ion required credit', showOnScreen: true },
{ html: 'Ion optional credit', showOnScreen: false },
{ html: 'Bing tile credit', showOnScreen: true },
],
);
assert.equal(credits[2], bingCredit);
assert.equal(provider.getTileCredits(0, 0, 0).length, 2);
assert.ok(provider.errorEvent instanceof Cesium.Event);
assert.equal(metadataCalls.at(-1).token, 'own-ion-token');
const requested = new URL(metadataCalls.at(-1).url);
assert.equal(requested.origin, 'https://api.cesium.com');
assert.equal(requested.pathname, `/v1/assets/${style}/endpoint`);
}
assert.deepEqual(perTileCalls, [
[3, 4, 5],
[0, 0, 0],
[3, 4, 5],
[0, 0, 0],
]);
assert.equal(bingCalls.length, 2);
for (const [index, { url, options }] of bingCalls.entries()) {
assert.equal(url, 'https://dev.virtualearth.net');
assert.deepEqual(options, {
key: 'ion-issued-bing-fixture',
mapStyle: index === 0 ? 'Aerial' : 'AerialWithLabels',
tileProtocol: 'https',
});
}
assert.equal(endpoint.options.tileProtocol, 'http');
assert.equal(Cesium.Ion.defaultAccessToken, defaultToken);
});
test('Bing ion metadata and construction failures propagate without another source', async (t) => {
const style = Cesium.IonWorldImageryStyle.AERIAL;
const metadataError = new Error('ion metadata fixture failure');
const providerError = new Error('Bing construction fixture failure');
let metadata = () => {
throw metadataError;
};
let bingCalls = 0;
t.mock.method(Cesium.Resource.prototype, 'fetchJson', async () => metadata());
t.mock.method(Cesium.BingMapsImageryProvider, 'fromUrl', async () => {
bingCalls += 1;
throw providerError;
});
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', () =>
assert.fail('Bing failures must not silently use an alternate provider'),
);
await assert.rejects(createIonImagery(style, 'test-token'), metadataError);
for (const endpoint of [
undefined,
{
type: 'TERRAIN',
externalType: 'BING',
options: { url: 'https://example.test' },
},
{
type: 'IMAGERY',
externalType: 'OTHER',
options: { url: 'https://example.test' },
},
{ type: 'IMAGERY', externalType: 'BING', options: {} },
]) {
metadata = () => endpoint;
await assert.rejects(
createIonImagery(style, 'test-token'),
/Bing imagery endpoint/,
);
}
assert.equal(bingCalls, 0);
metadata = () => ({
type: 'IMAGERY',
externalType: 'BING',
options: { url: 'https://dev.virtualearth.net', key: 'bing-fixture' },
});
await assert.rejects(createIonImagery(style, 'test-token'), providerError);
assert.equal(bingCalls, 1);
});
test('other ion imagery assets keep the standard SDK factory', async (t) => {
const expected = {};
const calls = [];
t.mock.method(Cesium.Resource.prototype, 'fetchJson', () =>
assert.fail('generic assets must use the SDK ion factory'),
);
t.mock.method(Cesium.IonImageryProvider, 'fromAssetId', async (...args) => {
calls.push(args);
return expected;
});
assert.equal(await createIonImagery(12345, ' custom-token '), expected);
assert.deepEqual(calls, [[12345, { accessToken: 'custom-token' }]]);
});
test('cancellation after ion metadata prevents terrain construction', async () => {
const originalResource = Cesium.IonResource.fromAssetId;
const originalTerrain = Cesium.CesiumTerrainProvider.fromUrl;