mirror of
https://github.com/misenhower/splatoon2.ink.git
synced 2026-09-28 04:06:52 -05:00
186 lines
6.2 KiB
JavaScript
186 lines
6.2 KiB
JavaScript
import { test, beforeEach, afterEach, mock } from 'node:test';
|
|
import assert from 'node:assert/strict';
|
|
import Updater from '../../src/app/updater/updaters/Updater.js';
|
|
import { fakeSplatNet, buckets, json, keys, setSessionEnvironment } from './support.js';
|
|
|
|
setSessionEnvironment();
|
|
|
|
const NAMES = {
|
|
en: { thing: 'Widget', other: 'Gadget' },
|
|
ja: { thing: 'ウィジェット', other: 'ガジェット' },
|
|
};
|
|
|
|
function thingsFor(language) {
|
|
const names = NAMES[language] ?? NAMES.en;
|
|
|
|
return {
|
|
things: [
|
|
{ id: '1', name: names.thing, image: '/images/things/one.png' },
|
|
{ id: '2', name: names.other, image: '/images/things/two.png' },
|
|
],
|
|
};
|
|
}
|
|
|
|
class ThingsUpdater extends Updater {
|
|
constructor(buckets, options = {}) {
|
|
super(
|
|
{
|
|
name: 'Things',
|
|
filename: 'things.json',
|
|
calendarFilename: 'things.ics',
|
|
request: splatnet => splatnet.getResponse('things'),
|
|
imagePaths: ['$..image'],
|
|
localization: [{ name: 'things', entities: '$.things[*]', id: 'id', values: 'name' }],
|
|
...options,
|
|
},
|
|
buckets,
|
|
);
|
|
}
|
|
|
|
getCalendarEntries(data) {
|
|
return data.things.map(thing => ({
|
|
id: `thing-${thing.id}`,
|
|
title: thing.name,
|
|
start_time: 3600,
|
|
end_time: 7200,
|
|
}));
|
|
}
|
|
}
|
|
|
|
let b;
|
|
|
|
beforeEach(() => {
|
|
b = buckets();
|
|
});
|
|
afterEach(() => mock.restoreAll());
|
|
|
|
test('publishes data, locale document, and calendar with the right metadata', async () => {
|
|
const splatnet = fakeSplatNet({ '/api/things': ({ language }) => thingsFor(language) });
|
|
|
|
await new ThingsUpdater(b).update();
|
|
|
|
assert.deepEqual(await json(b.publicBucket, 'data/things.json'), thingsFor('en'));
|
|
assert.deepEqual((await b.publicBucket.head('data/things.json')).httpMetadata, {
|
|
contentType: 'application/json',
|
|
cacheControl: 'no-cache, stale-while-revalidate=5, stale-if-error=86400',
|
|
});
|
|
|
|
assert.deepEqual(await json(b.publicBucket, 'data/locale/en.json'), {
|
|
things: { 1: { name: 'Widget' }, 2: { name: 'Gadget' } },
|
|
});
|
|
assert.deepEqual(await json(b.publicBucket, 'data/locale/ja.json'), {
|
|
things: { 1: { name: 'ウィジェット' }, 2: { name: 'ガジェット' } },
|
|
});
|
|
|
|
const ics = await (await b.publicBucket.get('data/things.ics')).text();
|
|
|
|
assert.match(ics, /SUMMARY:Widget/);
|
|
assert.match(ics, /X-WR-CALNAME:Things/);
|
|
assert.equal((await b.publicBucket.head('data/things.ics')).httpMetadata.contentType, 'text/calendar');
|
|
|
|
assert.equal(splatnet.api[0].language, 'en');
|
|
assert.equal(splatnet.api[0].region, 'NA');
|
|
});
|
|
|
|
test('fetches each language with missing strings using that region\'s session, then stops needing to', async () => {
|
|
const splatnet = fakeSplatNet({ '/api/things': ({ language }) => thingsFor(language) });
|
|
|
|
await new ThingsUpdater(b).update();
|
|
|
|
assert.deepEqual(
|
|
splatnet.api.map(r => `${r.region}/${r.language}`),
|
|
['NA/en', 'EU/es', 'NA/es-MX', 'EU/fr', 'NA/fr-CA', 'EU/de', 'EU/nl', 'EU/it', 'EU/ru', 'JP/ja'],
|
|
);
|
|
|
|
const second = fakeSplatNet({ '/api/things': ({ language }) => thingsFor(language) });
|
|
const puts = mock.method(b.publicBucket, 'put');
|
|
|
|
await new ThingsUpdater(b).update();
|
|
|
|
assert.deepEqual(
|
|
second.api.map(r => r.language),
|
|
['en'],
|
|
);
|
|
assert.deepEqual(
|
|
puts.mock.calls.map(c => c.arguments[0]).filter(k => k.startsWith('data/locale/')),
|
|
[],
|
|
);
|
|
});
|
|
|
|
test('mirrors only images that are missing, using one list per directory', async () => {
|
|
await b.publicBucket.put('assets/splatnet/images/things/one.png', 'existing');
|
|
|
|
const splatnet = fakeSplatNet({ '/api/things': ({ language }) => thingsFor(language) });
|
|
const list = mock.method(b.publicBucket, 'list');
|
|
const head = mock.method(b.publicBucket, 'head');
|
|
|
|
await new ThingsUpdater(b).update();
|
|
|
|
assert.deepEqual(
|
|
splatnet.images.map(r => r.path),
|
|
['/images/things/two.png'],
|
|
);
|
|
assert.equal(splatnet.images[0].cookie, null);
|
|
assert.equal(await (await b.publicBucket.get('assets/splatnet/images/things/one.png')).text(), 'existing');
|
|
assert.deepEqual(
|
|
list.mock.calls.map(c => c.arguments[0].prefix),
|
|
['assets/splatnet/images/things/'],
|
|
);
|
|
assert.equal(head.mock.calls.length, 0);
|
|
assert.equal(
|
|
(await b.publicBucket.get('assets/splatnet/images/things/two.png')).httpMetadata.contentType,
|
|
'image/png',
|
|
);
|
|
});
|
|
|
|
test('uses the embedded backup for the image SplatNet no longer serves', async () => {
|
|
const { cdnBackup } = await import('#cdn-images');
|
|
const missing = '/images/skill/53c62995f9d2dc4a60f3850c5dbdd2323f1eef87.png';
|
|
const splatnet = fakeSplatNet({
|
|
'/api/things': () => ({ things: [{ id: '1', name: 'Widget', image: missing }] }),
|
|
});
|
|
|
|
await new ThingsUpdater(b).update();
|
|
|
|
assert.deepEqual(splatnet.images, []);
|
|
|
|
const written = new Uint8Array(await (await b.publicBucket.get(`assets/splatnet${missing}`)).arrayBuffer());
|
|
|
|
assert.deepEqual(written, await cdnBackup(missing));
|
|
assert.deepEqual(written.slice(0, 4), new Uint8Array([0x89, 0x50, 0x4e, 0x47])); // a real PNG
|
|
assert.equal(written.byteLength, 8440);
|
|
});
|
|
|
|
test('filters root keys, dropping hidden timeline items', async () => {
|
|
fakeSplatNet({
|
|
'/api/things': () => ({ keep: { importance: 1, x: 1 }, hidden: { importance: -1 }, extra: 'dropped' }),
|
|
});
|
|
await new ThingsUpdater(b, {
|
|
rootKeys: ['keep', 'hidden', 'absent'],
|
|
localization: undefined,
|
|
imagePaths: undefined,
|
|
calendarFilename: undefined,
|
|
}).update();
|
|
|
|
assert.deepEqual(await json(b.publicBucket, 'data/things.json'), {
|
|
keep: { importance: 1, x: 1 },
|
|
hidden: null,
|
|
absent: null,
|
|
});
|
|
});
|
|
|
|
test('fails the run and writes nothing when SplatNet rejects the session', async () => {
|
|
fakeSplatNet({ '/api/things': () => new Response('forbidden', { status: 403 }) });
|
|
|
|
await assert.rejects(new ThingsUpdater(b).update(), /status 403/);
|
|
assert.deepEqual(keys(b.publicBucket), []);
|
|
});
|
|
|
|
test('leaves the existing locale document untouched when the update fails', async () => {
|
|
await b.publicBucket.put('data/locale/en.json', JSON.stringify({ things: { 9: { name: 'Old' } } }));
|
|
fakeSplatNet({ '/api/things': () => new Response('down', { status: 503 }) });
|
|
|
|
await assert.rejects(new ThingsUpdater(b).update());
|
|
assert.deepEqual(await json(b.publicBucket, 'data/locale/en.json'), { things: { 9: { name: 'Old' } } });
|
|
});
|