feat: add community publishing and news syndication
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
import { describe, expect, it, vi } from 'vitest';
|
||||
import {
|
||||
extractNimbusApiKey,
|
||||
fetchUbisoftNews,
|
||||
UbisoftNewsSourceError,
|
||||
} from './ubisoft-news-client';
|
||||
|
||||
const externalIdOne = '64f1234567890abcdef12345';
|
||||
const externalIdTwo = '64f1234567890abcdef67890';
|
||||
|
||||
function landing() {
|
||||
return new Response(
|
||||
`<script>window.__PRELOADED_STATE__ = ${JSON.stringify({ configuration: { nimbusApiKey: 'public-nimbus-key' } })};</script>`,
|
||||
{ status: 200, headers: { 'content-type': 'text/html' } },
|
||||
);
|
||||
}
|
||||
|
||||
function page(total: number, items: unknown[]) {
|
||||
return new Response(JSON.stringify({ total, items }), {
|
||||
status: 200,
|
||||
headers: { 'content-type': 'application/json' },
|
||||
});
|
||||
}
|
||||
|
||||
function item(id: string, overrides: Record<string, unknown> = {}) {
|
||||
return {
|
||||
id,
|
||||
title: 'Division Resurgence Update',
|
||||
abstract: 'Read the latest official update.',
|
||||
date: '2024-03-11T15:20:00.000Z',
|
||||
button: { buttonUrl: `/${id}/division-resurgence-update` },
|
||||
...overrides,
|
||||
};
|
||||
}
|
||||
|
||||
describe('Ubisoft News client', () => {
|
||||
it('extracts the Nimbus key from the browser configuration', () => {
|
||||
expect(
|
||||
extractNimbusApiKey(
|
||||
`<script>window.__PRELOADED_STATE__ = ${JSON.stringify({ configuration: { nimbusApiKey: 'public-nimbus-key' } })};</script>`,
|
||||
),
|
||||
).toBe('public-nimbus-key');
|
||||
});
|
||||
|
||||
it('paginates, deduplicates overlapping items, and normalizes source dates', async () => {
|
||||
const fetchImpl = vi
|
||||
.fn<typeof fetch>()
|
||||
.mockResolvedValueOnce(landing())
|
||||
.mockResolvedValueOnce(page(2, [item(externalIdTwo)]))
|
||||
.mockResolvedValueOnce(
|
||||
page(2, [
|
||||
item(externalIdTwo),
|
||||
item(externalIdOne, {
|
||||
title: 'Earlier Update',
|
||||
date: '2024-01-05T08:00:00Z',
|
||||
button: { buttonUrl: `/${externalIdOne}/earlier-update` },
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
const result = await fetchUbisoftNews(fetchImpl);
|
||||
|
||||
expect(result.map((entry) => entry.externalId)).toEqual([
|
||||
externalIdOne,
|
||||
externalIdTwo,
|
||||
]);
|
||||
expect(result[0].publishedAt).toBe('2024-01-05T08:00:00.000Z');
|
||||
expect(result[0].canonicalUrl).toBe(
|
||||
`https://www.ubisoft.com/en-us/game/the-division/resurgence/news-updates/${externalIdOne}/earlier-update`,
|
||||
);
|
||||
expect(fetchImpl).toHaveBeenCalledTimes(3);
|
||||
const secondRequest = fetchImpl.mock.calls[1];
|
||||
expect(String(secondRequest[0])).toContain('locale=en-us');
|
||||
expect(secondRequest[1]?.headers).toMatchObject({
|
||||
authorization: 'public-nimbus-key',
|
||||
});
|
||||
expect(secondRequest[1]?.redirect).toBe('error');
|
||||
});
|
||||
|
||||
it('rejects article links outside the approved Ubisoft path', async () => {
|
||||
const fetchImpl = vi
|
||||
.fn<typeof fetch>()
|
||||
.mockResolvedValueOnce(landing())
|
||||
.mockResolvedValueOnce(
|
||||
page(1, [
|
||||
item(externalIdOne, {
|
||||
button: {
|
||||
buttonUrl: `/${externalIdOne}-lookalike/not-the-same-item`,
|
||||
},
|
||||
}),
|
||||
]),
|
||||
);
|
||||
|
||||
await expect(fetchUbisoftNews(fetchImpl)).rejects.toBeInstanceOf(
|
||||
UbisoftNewsSourceError,
|
||||
);
|
||||
});
|
||||
|
||||
it('rejects malformed source dates', async () => {
|
||||
const fetchImpl = vi
|
||||
.fn<typeof fetch>()
|
||||
.mockResolvedValueOnce(landing())
|
||||
.mockResolvedValueOnce(
|
||||
page(1, [item(externalIdOne, { date: 'unknown' })]),
|
||||
);
|
||||
|
||||
await expect(fetchUbisoftNews(fetchImpl)).rejects.toThrow(
|
||||
'Ubisoft returned an invalid news item.',
|
||||
);
|
||||
});
|
||||
|
||||
it('does not follow provider redirects', async () => {
|
||||
const fetchImpl = vi.fn<typeof fetch>().mockResolvedValueOnce(
|
||||
new Response(null, {
|
||||
status: 302,
|
||||
headers: { location: 'http://127.0.0.1/internal' },
|
||||
}),
|
||||
);
|
||||
|
||||
await expect(fetchUbisoftNews(fetchImpl)).rejects.toThrow(
|
||||
'Ubisoft news request returned HTTP 302.',
|
||||
);
|
||||
expect(fetchImpl.mock.calls[0][1]?.redirect).toBe('error');
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user