📄 src/models/test/worker-client.test.ts
D-OPEN SOVEREIGN

Documentation & Insights

@vitest-environment jsdom
1/*
2 * Copyright (c) 2026 DataPond D-Library Pty Ltd. ("The Sanctuary")
3 * Non-Profit Public Library — The World Library
4 * 
5 * D-SAFE Certification issued by POND Enterprise.
6 * Published under the D-Open Code Sovereign Licence v1.0 framework
7 * DataPond D-Library All exclusive Right reserved on modifiying the code - CC Attribution to data pond, Non modifiable, Non Commercial.
8 * https://registry.world.bibliotech.com/licence
9 * Source code donated to DataPond D-Library by it's author: data pond.
10 * 
11 * Technical Guardian ("The Shield"): Pond Enterprise Pty Ltd. (ACN 694 747 987)
12 * All liability claims about D-SAFE certification issues coming from the published D-Safe direction must be addressed with Pond Enterprise Pty Ltd.
13 * Code Modification automatically voids the certified liability protection provided by Pond Enterprise.
14 */
15/**
16 * @vitest-environment jsdom
17 */
18import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
19import { Thread, initWorker } from '../worker-client.ts';
20import { WorkerEvent } from '../enums.ts';
21
22describe('worker-client (Thread)', () => {
23    let mockWorkerInstance: any;
24
25    beforeEach(() => {
26        mockWorkerInstance = {
27            addEventListener: vi.fn(),
28            postMessage: vi.fn(),
29            terminate: vi.fn(),
30            onerror: null
31        };
32
33        vi.stubGlobal('Worker', vi.fn().mockImplementation(() => mockWorkerInstance));
34    });
35
36    afterEach(() => {
37        vi.unstubAllGlobals();
38    });
39
40    it('initializes and listens to messages', () => {
41        const thread = new Thread('/worker.js');
42        expect(mockWorkerInstance.addEventListener).toHaveBeenCalledWith('message', expect.any(Function));
43        expect(mockWorkerInstance.addEventListener).toHaveBeenCalledWith('messageerror', expect.any(Function));
44    });
45
46    it('sendRequest resolves when worker replies with data', async () => {
47        const thread = new Thread('/worker.js');
48        const promise = thread.sendRequest({ cmd: 'test' });
49        
50        // Find the req ID that was generated
51        const reqId = Object.keys(thread._rrRequest)[0];
52        expect(reqId).toBeDefined();
53
54        // Simulate successful reply
55        thread._onMessage({
56            id: reqId,
57            result: { data: 'success' }
58        });
59
60        const result = await promise;
61        expect(result).toBe('success');
62    });
63
64    it('sendRequest rejects when worker replies with error', async () => {
65        const thread = new Thread('/worker.js');
66        const promise = thread.sendRequest({ cmd: 'fail' });
67        
68        const reqId = Object.keys(thread._rrRequest)[0];
69        
70        thread._onMessage({
71            id: reqId,
72            result: { error: 'something broke' }
73        });
74
75        await expect(promise).rejects.toBe('something broke');
76    });
77
78    it('cancels pending requests on error', async () => {
79        const thread = new Thread('/worker.js');
80        const promise1 = thread.sendRequest({ cmd: '1' });
81        const promise2 = thread.sendRequest({ cmd: '2' });
82
83        expect(Object.keys(thread._rrRequest).length).toBe(2);
84
85        // Simulate an error
86        thread._onError(new Error('crash'));
87
88        await expect(promise1).rejects.toMatch(/canceled/);
89        await expect(promise2).rejects.toMatch(/canceled/);
90        expect(Object.keys(thread._rrRequest).length).toBe(0);
91    });
92
93    it('terminates worker correctly', () => {
94        const thread = new Thread('/worker.js');
95        thread.terminate();
96        expect(mockWorkerInstance.terminate).toHaveBeenCalled();
97        expect(thread.getWorker()).toBeNull();
98    });
99
100    it('forwards custom events to window and internal listeners', () => {
101        const thread = new Thread('/worker.js');
102        const internalListener = vi.fn();
103        const windowListener = vi.fn();
104        
105        thread.addEventListener(WorkerEvent.WorkerReady as any, internalListener);
106        window.addEventListener(WorkerEvent.WorkerReady, windowListener);
107
108        // Simulate event payload from worker
109        thread._onMessage({
110            eventName: WorkerEvent.WorkerReady,
111            payload: { ok: true }
112        });
113
114        expect(internalListener).toHaveBeenCalledWith({ ok: true });
115        // The CustomEvent detail should be { ok: true }
116        expect(windowListener).toHaveBeenCalled();
117        
118        window.removeEventListener(WorkerEvent.WorkerReady, windowListener);
119    });
120});
121