mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 07:52:24 +00:00
- Add `tests/services/exploreDataService.test.mjs` to test `buildWatchHistoryTagCounts`. - Mock global `Worker` to simulate worker behavior. - Mock `console.warn` to verify error logging and suppression. - Use dynamic import to handle `devLogger` initialization dependent on global flags. Co-authored-by: PR0M3TH3AN <[email protected]>
168 lines
5.1 KiB
JavaScript
168 lines
5.1 KiB
JavaScript
|
|
import { describe, it, before, beforeEach, afterEach, after, mock } from 'node:test';
|
|
import assert from 'node:assert';
|
|
|
|
// Set Dev Mode Override to ensure logs are emitted
|
|
globalThis.__BITVID_DEV_MODE_OVERRIDE__ = true;
|
|
|
|
// Mock Worker globally before importing the service
|
|
class MockWorker {
|
|
constructor(scriptURL) {
|
|
this.scriptURL = scriptURL;
|
|
this.listeners = {};
|
|
MockWorker.instances.push(this);
|
|
}
|
|
|
|
addEventListener(event, handler) {
|
|
if (!this.listeners[event]) {
|
|
this.listeners[event] = [];
|
|
}
|
|
this.listeners[event].push(handler);
|
|
}
|
|
|
|
removeEventListener(event, handler) {
|
|
if (this.listeners[event]) {
|
|
this.listeners[event] = this.listeners[event].filter((h) => h !== handler);
|
|
}
|
|
}
|
|
|
|
postMessage(data) {
|
|
// Simulate async processing
|
|
setImmediate(() => {
|
|
if (MockWorker.onPostMessage) {
|
|
MockWorker.onPostMessage(this, data);
|
|
}
|
|
});
|
|
}
|
|
|
|
// Helper to simulate incoming message from worker
|
|
emitMessage(data) {
|
|
if (this.listeners['message']) {
|
|
const event = { data };
|
|
this.listeners['message'].forEach((handler) => handler(event));
|
|
}
|
|
}
|
|
}
|
|
|
|
MockWorker.instances = [];
|
|
MockWorker.onPostMessage = null;
|
|
|
|
globalThis.Worker = MockWorker;
|
|
|
|
describe('exploreDataService - buildWatchHistoryTagCounts', () => {
|
|
let buildWatchHistoryTagCounts;
|
|
let consoleWarnMock;
|
|
let initialCallCount = 0;
|
|
|
|
before(async () => {
|
|
// Mock console.warn BEFORE import so logger.js captures the mock
|
|
// if console.warn is already mocked (e.g. by other tests? unlikely in isolated process), restore it first
|
|
if (console.warn.mock) console.warn.mock.restore();
|
|
|
|
consoleWarnMock = mock.method(console, 'warn', () => {});
|
|
|
|
// Dynamic import
|
|
const mod = await import('../../js/services/exploreDataService.js');
|
|
buildWatchHistoryTagCounts = mod.buildWatchHistoryTagCounts;
|
|
});
|
|
|
|
after(() => {
|
|
if (consoleWarnMock) consoleWarnMock.mock.restore();
|
|
});
|
|
|
|
beforeEach(() => {
|
|
MockWorker.instances = [];
|
|
MockWorker.onPostMessage = null;
|
|
initialCallCount = consoleWarnMock.mock.calls.length;
|
|
});
|
|
|
|
it('should return empty Map if watchHistoryService is missing', async () => {
|
|
const result = await buildWatchHistoryTagCounts({});
|
|
assert.ok(result instanceof Map);
|
|
assert.strictEqual(result.size, 0);
|
|
});
|
|
|
|
it('should handle missing loadLatest method gracefully', async () => {
|
|
const watchHistoryService = {};
|
|
const result = await buildWatchHistoryTagCounts({ watchHistoryService });
|
|
assert.strictEqual(result.size, 0);
|
|
});
|
|
|
|
it('should handle loadLatest failure gracefully', async () => {
|
|
const watchHistoryService = {
|
|
loadLatest: mock.fn(async () => {
|
|
throw new Error('Load failed');
|
|
}),
|
|
};
|
|
|
|
// Even if load fails, it should continue with empty items
|
|
MockWorker.onPostMessage = (worker, data) => {
|
|
assert.deepStrictEqual(data.payload.items, []);
|
|
worker.emitMessage({ id: data.id, result: new Map() });
|
|
};
|
|
|
|
const result = await buildWatchHistoryTagCounts({ watchHistoryService });
|
|
|
|
assert.ok(result instanceof Map);
|
|
assert.strictEqual(result.size, 0);
|
|
assert.strictEqual(watchHistoryService.loadLatest.mock.callCount(), 1);
|
|
|
|
// Verify warning was logged
|
|
const newCalls = consoleWarnMock.mock.calls.slice(initialCallCount);
|
|
const warning = newCalls.find(c => c.arguments[0] && String(c.arguments[0]).includes('Failed to load watch history entries'));
|
|
assert.ok(warning, 'Should log warning about load failure');
|
|
});
|
|
|
|
it('should return counts from worker on success', async () => {
|
|
const watchHistoryService = {
|
|
loadLatest: mock.fn(async () => ['item1']),
|
|
};
|
|
const nostrService = {
|
|
getVideosMap: mock.fn(() => new Map([['v1', { tags: ['t1'] }]])),
|
|
};
|
|
|
|
MockWorker.onPostMessage = (worker, data) => {
|
|
assert.strictEqual(data.type, 'CALC_HISTORY_COUNTS');
|
|
assert.deepStrictEqual(data.payload.items, ['item1']);
|
|
|
|
// Verify worker receives correct payload structure
|
|
assert.ok(data.payload.videosMap instanceof Map);
|
|
|
|
// Simulate successful result
|
|
worker.emitMessage({
|
|
id: data.id,
|
|
result: new Map([['t1', 5]])
|
|
});
|
|
};
|
|
|
|
const result = await buildWatchHistoryTagCounts({
|
|
watchHistoryService,
|
|
nostrService,
|
|
actor: 'user1'
|
|
});
|
|
|
|
assert.strictEqual(result.get('t1'), 5);
|
|
});
|
|
|
|
it('should handle worker error gracefully', async () => {
|
|
const watchHistoryService = {
|
|
loadLatest: mock.fn(async () => ['item1']),
|
|
};
|
|
|
|
MockWorker.onPostMessage = (worker, data) => {
|
|
worker.emitMessage({
|
|
id: data.id,
|
|
error: 'Worker exploded'
|
|
});
|
|
};
|
|
|
|
const result = await buildWatchHistoryTagCounts({ watchHistoryService });
|
|
assert.ok(result instanceof Map);
|
|
assert.strictEqual(result.size, 0);
|
|
|
|
const newCalls = consoleWarnMock.mock.calls.slice(initialCallCount);
|
|
const warning = newCalls.find(c => c.arguments[0] && String(c.arguments[0]).includes('Worker failed to calculate history counts'));
|
|
assert.ok(warning, 'Should log warning about worker failure');
|
|
});
|
|
});
|