feat(python): add queued worker runtime and configurable transform mode

This commit is contained in:
2026-02-23 22:26:54 +01:00
parent 8e8f099768
commit 838ea34ab7
21 changed files with 744 additions and 88 deletions

View File

@@ -117,6 +117,33 @@ describe('SettingsView Diff Preferences', () => {
);
});
it('includes python runtime mode in metadata save payload', async () => {
(window as any).electronAPI.meta.getProjectMetadata = vi.fn().mockResolvedValue({
maxPostsPerPage: 75,
publicUrl: 'https://example.com',
pythonRuntimeMode: 'main-thread',
categorySettings: {
article: { renderInLists: true, showTitle: true },
picture: { renderInLists: true, showTitle: true },
aside: { renderInLists: true, showTitle: false },
page: { renderInLists: false, showTitle: true },
},
});
render(<SettingsView />);
await screen.findByDisplayValue('Main Thread (Legacy)');
const saveButton = screen.getByRole('button', { name: /save project settings/i });
fireEvent.click(saveButton);
await new Promise((resolve) => setTimeout(resolve, 0));
expect((window as any).electronAPI.meta.updateProjectMetadata).toHaveBeenCalledWith(
expect.objectContaining({ pythonRuntimeMode: 'main-thread' })
);
});
it('renders category settings checkboxes with required defaults', async () => {
render(<SettingsView />);

View File

@@ -191,6 +191,32 @@ describe('PythonRuntimeManager', () => {
await expect(runPromise).rejects.toThrow('boom');
});
it('queues concurrent execute calls and sends the next request only after completion', async () => {
const worker = new MockWorker();
const manager = new PythonRuntimeManager(() => worker as unknown as Worker);
const initPromise = manager.initialize();
worker.emitMessage({ type: 'ready' });
await initPromise;
const firstRun = manager.execute('1 + 1');
const secondRun = manager.execute('2 + 2');
await Promise.resolve();
expect(worker.postedMessages).toHaveLength(1);
const firstRequest = worker.postedMessages[0] as { requestId: string };
worker.emitMessage({ type: 'runResult', requestId: firstRequest.requestId, result: '2' });
await expect(firstRun).resolves.toEqual({ result: '2', stdout: '' });
await Promise.resolve();
expect(worker.postedMessages).toHaveLength(2);
const secondRequest = worker.postedMessages[1] as { requestId: string };
worker.emitMessage({ type: 'runResult', requestId: secondRequest.requestId, result: '4' });
await expect(secondRun).resolves.toEqual({ result: '4', stdout: '' });
});
it('terminates timed out worker and recovers with a new worker', async () => {
const workers: MockWorker[] = [];
const manager = new PythonRuntimeManager(() => {