Files
wehub-resource-sync 719032b19f
Update Schema / Update configuration json schema (push) Has been cancelled
Memory Benchmark / Memory Test (Full Analysis) (push) Has been cancelled
CI Quality / Lint GitHub Actions (actionlint) (push) Has been cancelled
CI Quality / Lint GitHub Actions (zizmor) (push) Has been cancelled
CI Quality / Check typos (push) Has been cancelled
CI / Test coverage (push) Has been cancelled
CI / Test (22.x, windows-latest) (push) Has been cancelled
CI / Test (24.x, macos-latest) (push) Has been cancelled
CI / Test (24.x, ubuntu-latest) (push) Has been cancelled
CI / Test (24.x, windows-latest) (push) Has been cancelled
CI / Test (26.x, macos-latest) (push) Has been cancelled
CI / Test (26.x, ubuntu-latest) (push) Has been cancelled
CI / Test (26.x, windows-latest) (push) Has been cancelled
CI / Test with Bun (latest, macos-latest) (push) Has been cancelled
CI / Test with Bun (latest, ubuntu-latest) (push) Has been cancelled
CI / Test with Bun (latest, windows-latest) (push) Has been cancelled
CI / Build and run (22.x, macos-latest) (push) Has been cancelled
CI / Build and run (22.x, ubuntu-latest) (push) Has been cancelled
CI / Build and run (22.x, windows-latest) (push) Has been cancelled
autofix.ci / autofix (push) Has been cancelled
CI Browser Extension / Lint Browser Extension (push) Has been cancelled
CI Browser Extension / Test Browser Extension (push) Has been cancelled
Memory Benchmark / Memory Test (push) Has been cancelled
CI Website / Lint Website Client (push) Has been cancelled
CI Website / Lint Website Server (push) Has been cancelled
CI Website / Test Website Server (push) Has been cancelled
CI Website / Bundle Website Server (push) Has been cancelled
CI / Lint Biome (push) Has been cancelled
CI / Lint oxlint (push) Has been cancelled
CI / Lint TypeScript (push) Has been cancelled
CI / Lint Secretlint (push) Has been cancelled
CI / Test (22.x, macos-latest) (push) Has been cancelled
CI / Test (22.x, ubuntu-latest) (push) Has been cancelled
CI / Build and run (24.x, macos-latest) (push) Has been cancelled
CI / Build and run (24.x, ubuntu-latest) (push) Has been cancelled
CI / Build and run (24.x, windows-latest) (push) Has been cancelled
CI / Build and run (26.x, macos-latest) (push) Has been cancelled
CI / Build and run (26.x, ubuntu-latest) (push) Has been cancelled
CI / Build and run (26.x, windows-latest) (push) Has been cancelled
CI / Build and run with Bun (latest, macos-latest) (push) Has been cancelled
CI / Build and run with Bun (latest, ubuntu-latest) (push) Has been cancelled
CI / Build and run with Bun (latest, windows-latest) (push) Has been cancelled
CodeQL / Analyze (javascript-typescript) (push) Has been cancelled
Docker / build (linux/amd64, ubuntu-latest) (push) Has been cancelled
Docker / build (linux/arm/v7, ubuntu-24.04-arm) (push) Has been cancelled
Docker / build (linux/arm64, ubuntu-24.04-arm) (push) Has been cancelled
Docker / merge (push) Has been cancelled
Pack repository with Repomix / pack-repo (push) Has been cancelled
Performance Benchmark History / Benchmark (macos-latest) (push) Has been cancelled
Performance Benchmark History / Benchmark (ubuntu-latest) (push) Has been cancelled
Performance Benchmark History / Benchmark (windows-latest) (push) Has been cancelled
Performance Benchmark History / Store Results (push) Has been cancelled
Test Repomix Action / Test Node.js 22 (push) Has been cancelled
Test Repomix Action / Test Node.js 24 (push) Has been cancelled
Test Repomix Action / Test Node.js 26 (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:39:37 +08:00

240 lines
9.9 KiB
TypeScript

import { beforeEach, describe, expect, it, vi } from 'vitest';
// We need to test the internal functions, so we'll test through the module behavior
// Mock all worker modules
vi.mock('../../src/core/file/workers/fileProcessWorker.js', () => ({
default: vi.fn().mockResolvedValue({ processed: true }),
onWorkerTermination: vi.fn(),
}));
vi.mock('../../src/core/security/workers/securityCheckWorker.js', () => ({
default: vi.fn().mockResolvedValue(null),
onWorkerTermination: vi.fn(),
}));
vi.mock('../../src/core/metrics/workers/calculateMetricsWorker.js', () => ({
default: vi.fn().mockResolvedValue(100),
onWorkerTermination: vi.fn(),
}));
// Mock worker_threads
vi.mock('node:worker_threads', () => ({
workerData: undefined,
}));
describe('unifiedWorker', () => {
beforeEach(() => {
vi.clearAllMocks();
// Reset module cache to clear handler cache
vi.resetModules();
});
describe('inferWorkerTypeFromTask', () => {
it('should infer fileProcess from task with rawFile and config', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const task = {
rawFile: { path: 'test.ts', content: 'code' },
config: {},
};
await handler(task);
const fileProcessWorker = await import('../../src/core/file/workers/fileProcessWorker.js');
expect(fileProcessWorker.default).toHaveBeenCalledWith(task);
});
it('should infer calculateMetrics from task with content and encoding', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const task = {
content: 'test content',
encoding: 'cl100k_base',
};
await handler(task);
const calculateMetricsWorker = await import('../../src/core/metrics/workers/calculateMetricsWorker.js');
expect(calculateMetricsWorker.default).toHaveBeenCalledWith(task);
});
it('should infer securityCheck from task with items (without encoding)', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const task = {
items: [{ filePath: 'test.ts', content: 'test content', type: 'file' }],
};
await handler(task);
const securityCheckWorker = await import('../../src/core/security/workers/securityCheckWorker.js');
expect(securityCheckWorker.default).toHaveBeenCalledWith(task);
});
it('should throw error for unrecognizable task structure', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const task = { unknownField: 'value' };
await expect(handler(task)).rejects.toThrow('Cannot determine worker type');
});
it('should throw error for null task', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
await expect(handler(null)).rejects.toThrow('Cannot determine worker type');
});
it('should throw error for non-object task', async () => {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
await expect(handler('string')).rejects.toThrow('Cannot determine worker type');
});
});
describe('repeated calls', () => {
it('routes every call through the worker handler without throwing', async () => {
// Note: this is a smoke test for repeated invocations, not a cache verifier.
// Whether handlerCache short-circuits in loadWorkerHandler can't be observed
// from outside — both paths still call Map.set(workerType, ...) once per type,
// and Node's own module cache makes the dynamic import effectively free on
// repeat. Verifying the cache behavior would require either exposing the
// cache or measuring import timing, neither of which is worth it for a
// micro-optimization.
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const fileProcessWorker = await import('../../src/core/file/workers/fileProcessWorker.js');
await handler({ rawFile: { path: 'a.ts', content: '' }, config: {} });
await handler({ rawFile: { path: 'b.ts', content: '' }, config: {} });
expect(fileProcessWorker.default).toHaveBeenCalledTimes(2);
});
});
describe('task-based inference overrides workerData', () => {
it('lets a task that matches another workerType override the configured one (bundled-env reuse)', async () => {
// Tinypool may reuse a child process configured for one worker type to run
// tasks for another in bundled environments. inferWorkerTypeFromTask must
// win over getWorkerTypeFromWorkerData so the right handler is dispatched.
vi.resetModules();
vi.doMock('node:worker_threads', () => ({
workerData: ['x', { workerType: 'securityCheck' }],
}));
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const fileProcessWorker = await import('../../src/core/file/workers/fileProcessWorker.js');
const securityCheckWorker = await import('../../src/core/security/workers/securityCheckWorker.js');
// Task structure infers fileProcess even though workerData says securityCheck.
await handler({ rawFile: { path: 'a.ts', content: '' }, config: {} });
expect(fileProcessWorker.default).toHaveBeenCalled();
expect(securityCheckWorker.default).not.toHaveBeenCalled();
vi.doUnmock('node:worker_threads');
});
it('falls back to workerData when task structure is unrecognizable (no inference)', async () => {
// Mirror of the override case: same workerData (securityCheck), but with an
// ambiguous task that produces no inferred type. Together with the override
// test above, this distinguishes "inference always wins" from "inference
// wins only when it yields a value" — the production behavior at unifiedWorker.ts:140-142.
vi.resetModules();
vi.doMock('node:worker_threads', () => ({
workerData: ['x', { workerType: 'securityCheck' }],
}));
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const fileProcessWorker = await import('../../src/core/file/workers/fileProcessWorker.js');
const securityCheckWorker = await import('../../src/core/security/workers/securityCheckWorker.js');
await handler({ ambiguous: true });
expect(securityCheckWorker.default).toHaveBeenCalled();
expect(fileProcessWorker.default).not.toHaveBeenCalled();
vi.doUnmock('node:worker_threads');
});
});
describe('workerData detection', () => {
it('uses workerType from array-shaped workerData (Tinypool child_process)', async () => {
vi.resetModules();
vi.doMock('node:worker_threads', () => ({
workerData: ['something', { workerType: 'fileProcess' }],
}));
// Task that is not auto-inferable so workerData is the only signal.
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const fileProcessWorker = await import('../../src/core/file/workers/fileProcessWorker.js');
await handler({ ambiguous: true });
expect(fileProcessWorker.default).toHaveBeenCalled();
vi.doUnmock('node:worker_threads');
});
it('uses workerType from object-shaped workerData (worker_threads)', async () => {
vi.resetModules();
vi.doMock('node:worker_threads', () => ({
workerData: { workerType: 'securityCheck' },
}));
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const securityCheckWorker = await import('../../src/core/security/workers/securityCheckWorker.js');
await handler({ ambiguous: true });
expect(securityCheckWorker.default).toHaveBeenCalled();
vi.doUnmock('node:worker_threads');
});
it('falls back to REPOMIX_WORKER_TYPE env var', async () => {
vi.resetModules();
vi.doMock('node:worker_threads', () => ({ workerData: undefined }));
const original = process.env.REPOMIX_WORKER_TYPE;
process.env.REPOMIX_WORKER_TYPE = 'calculateMetrics';
try {
const { default: handler } = await import('../../src/shared/unifiedWorker.js');
const calculateMetricsWorker = await import('../../src/core/metrics/workers/calculateMetricsWorker.js');
await handler({ ambiguous: true });
expect(calculateMetricsWorker.default).toHaveBeenCalled();
} finally {
if (original === undefined) {
delete process.env.REPOMIX_WORKER_TYPE;
} else {
process.env.REPOMIX_WORKER_TYPE = original;
}
vi.doUnmock('node:worker_threads');
}
});
});
describe('onWorkerTermination', () => {
it('should call cleanup on cached handlers', async () => {
// First, load a handler to populate the cache
const { default: handler, onWorkerTermination } = await import('../../src/shared/unifiedWorker.js');
const task = {
content: 'test content',
encoding: 'cl100k_base',
};
await handler(task);
// Now call termination
await onWorkerTermination();
const calculateMetricsWorker = await import('../../src/core/metrics/workers/calculateMetricsWorker.js');
expect(calculateMetricsWorker.onWorkerTermination).toHaveBeenCalled();
});
it('should clear handler cache after cleanup', async () => {
const { default: handler, onWorkerTermination } = await import('../../src/shared/unifiedWorker.js');
// Load handler
await handler({ content: 'test', encoding: 'cl100k_base' });
// Terminate
await onWorkerTermination();
// Load again - should call the module import again
vi.clearAllMocks();
await handler({ content: 'test', encoding: 'cl100k_base' });
const calculateMetricsWorker = await import('../../src/core/metrics/workers/calculateMetricsWorker.js');
// The handler should be called again (cache was cleared)
expect(calculateMetricsWorker.default).toHaveBeenCalled();
});
});
});