Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions .scripts/generate-groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@ publicElements.forEach(file => {
}
});

// The mock HAL+JSON server is not a component directory, so it gets one group of its own. Without
// it there is nothing globbing ./src/server, and since the config has no top-level `files` key a
// test placed there is silently never run.
groups.push({
name: 'server',
files: './src/server/**/*.test.ts',
});

const configURL = new URL('../web-test-runner.groups.js', import.meta.url);
const config = `export const groups = ${JSON.stringify(groups, null, 2)}`;

Expand Down
43 changes: 43 additions & 0 deletions src/server/router/createResourceGetHandler.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
import type { HandleResult, Router } from 'service-worker-router';

import { createRouter } from '../hapi/index';
import { expect } from '@open-wc/testing';

type ResponseBody = {
_embedded?: Record<string, unknown>;
_links: { self: { href: string } };
};

const base = 'https://demo.api/hapi';

function get(router: Router, url: string): Promise<ResponseBody> {
const result = router.handleRequest(new Request(url)) as HandleResult;
return result.handlerPromise.then(response => response.json());
}

describe('createResourceGetHandler', () => {
it('gives each of two overlapping requests for one resource its own _links', async () => {
const zoomedURL = `${base}/customers/0?zoom=attributes`;
const plainURL = `${base}/customers/0`;
const router = createRouter();

// Both requests are dispatched before either is awaited, so the second one runs while the
// first is still resolving its embeds.
const whenZoomed = get(router, zoomedURL);
const whenPlain = get(router, plainURL);
const [zoomed, plain] = await Promise.all([whenZoomed, whenPlain]);

expect(zoomed._links.self).to.have.property('href', zoomedURL);
expect(plain._links.self).to.have.property('href', plainURL);
});

it('does not leave _embedded from a zoomed request on a later plain request', async () => {
const router = createRouter();

const zoomed = await get(router, `${base}/customers/0?zoom=attributes`);
const plain = await get(router, `${base}/customers/0`);

expect(zoomed._embedded).to.have.property('fx:attributes');
expect(plain).to.not.have.property('_embedded');
});
});
9 changes: 7 additions & 2 deletions src/server/router/createResourceGetHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,13 @@ async function addEmbeds(router: Router, result: Document, zoom: string[][]) {
export function createResourceGetHandler(router: Router, dataset: Dataset, links: Links) {
return async ({ params, url }: HandlerContext): Promise<Response> => {
const { collection, prefix, id } = params;
const document = dataset[collection]?.find(v => v.id == id);
if (!document) return new Response('Not found', { status: 404 });
const storedDocument = dataset[collection]?.find(v => v.id == id);
if (!storedDocument) return new Response('Not found', { status: 404 });

// Serialization happens after `addEmbeds` yields, so `_links` and `_embedded` go onto a copy.
// Writing them to the stored document lets two overlapping requests for the same resource
// overwrite each other's `_links`, and leaves `_embedded` behind for later unzoomed requests.
const document = { ...storedDocument };

const resourceLinks = links[collection]?.(document) ?? {};
resourceLinks.self = { href: url.toString() };
Expand Down
4 changes: 4 additions & 0 deletions web-test-runner.groups.js
Original file line number Diff line number Diff line change
Expand Up @@ -707,4 +707,8 @@ export const groups = [
name: 'foxy-webhook-status-card',
files: './src/elements/public/WebhookStatusCard/**/*.test.ts',
},
{
name: 'server',
files: './src/server/**/*.test.ts',
},
];