111 lines
3.6 KiB
JavaScript
111 lines
3.6 KiB
JavaScript
import express from 'express';
|
|||
|
|
import { afterEach, beforeEach, describe, expect, it } from 'vitest';
|
||
|
|
import { registerWalkthroughRoutes } from './routes.js';
|
||
|
|
|
||
|
|
// These run over real HTTP on purpose. The bug this file exists for was
|
||
|
|
// invisible to unit tests: the service and the store were both correct, and the
|
||
|
|
// response was dropped by a disconnect check that misread a healthy request.
|
||
|
|
|
||
|
|
const SOURCE = { kind: 'working-tree', scope: 'all' };
|
||
|
|
|
||
|
|
describe('walkthrough routes', () => {
|
||
|
|
let server;
|
||
|
|
let base;
|
||
|
|
let releaseJob;
|
||
|
|
let job;
|
||
|
|
|
||
|
|
const service = {
|
||
|
|
async getWalkthrough() {
|
||
|
|
return { walkthrough: null, hunks: [], hunkCount: 0, generating: Boolean(job) };
|
||
|
|
},
|
||
|
|
async generateWalkthrough() {
|
||
|
|
if (job) return job;
|
||
|
|
job = new Promise((resolve) => {
|
||
|
|
releaseJob = () => resolve({ walkthrough: { title: 'DONE' }, hunks: [], hunkCount: 1 });
|
||
|
|
}).finally(() => { job = null; });
|
||
|
|
return job;
|
||
|
|
},
|
||
|
|
async cancelWalkthroughGeneration() {
|
||
|
|
return { cancelled: Boolean(job) };
|
||
|
|
},
|
||
|
|
};
|
||
|
|
|
||
|
|
const generate = (signal) => fetch(`${base}/api/walkthrough/generate`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ directory: '/repo', source: SOURCE }),
|
||
|
|
signal,
|
||
|
|
});
|
||
|
|
|
||
|
|
beforeEach(async () => {
|
||
|
|
job = null;
|
||
|
|
releaseJob = undefined;
|
||
|
|
const app = express();
|
||
|
|
app.use(express.json());
|
||
|
|
registerWalkthroughRoutes(app, { getWalkthroughService: async () => service });
|
||
|
|
server = app.listen(0);
|
||
|
|
await new Promise((resolve) => server.once('listening', resolve));
|
||
|
|
base = `http://127.0.0.1:${server.address().port}`;
|
||
|
|
});
|
||
|
|
|
||
|
|
afterEach(async () => {
|
||
|
|
await new Promise((resolve) => server.close(resolve));
|
||
|
|
});
|
||
|
|
|
||
|
|
it('answers a generation request that nobody interrupted', async () => {
|
||
|
|
const pending = generate();
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
||
|
|
releaseJob();
|
||
|
|
|
||
|
|
const body = await (await pending).json();
|
||
|
|
|
||
|
|
expect(body.walkthrough).toEqual({ title: 'DONE' });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('delivers the result to a client that reconnected after a refresh', async () => {
|
||
|
|
const controller = new AbortController();
|
||
|
|
generate(controller.signal).catch(() => {});
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
||
|
|
controller.abort();
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
||
|
|
|
||
|
|
// The reloaded page sees work in progress and re-attaches to it.
|
||
|
|
const read = await (await fetch(
|
||
|
|
`${base}/api/walkthrough?directory=/repo&source=${encodeURIComponent(JSON.stringify(SOURCE))}`,
|
||
|
|
)).json();
|
||
|
|
expect(read.generating).toBe(true);
|
||
|
|
|
||
|
|
const reattached = generate();
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
||
|
|
releaseJob();
|
||
|
|
|
||
|
|
const body = await (await reattached).json();
|
||
|
|
expect(body.walkthrough).toEqual({ title: 'DONE' });
|
||
|
|
});
|
||
|
|
|
||
|
|
it('rejects a request without a directory before touching the service', async () => {
|
||
|
|
const response = await fetch(`${base}/api/walkthrough/generate`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ source: SOURCE }),
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(response.status).toBe(400);
|
||
|
|
expect(job).toBeNull();
|
||
|
|
});
|
||
|
|
|
||
|
|
it('cancels through its own endpoint rather than a dropped connection', async () => {
|
||
|
|
generate().catch(() => {});
|
||
|
|
await new Promise((resolve) => setTimeout(resolve, 20));
|
||
|
|
|
||
|
|
const response = await fetch(`${base}/api/walkthrough/cancel`, {
|
||
|
|
method: 'POST',
|
||
|
|
headers: { 'Content-Type': 'application/json' },
|
||
|
|
body: JSON.stringify({ directory: '/repo', source: SOURCE }),
|
||
|
|
});
|
||
|
|
|
||
|
|
expect(await response.json()).toEqual({ cancelled: true });
|
||
|
|
releaseJob();
|
||
|
|
});
|
||
|
|
});
|