|
|
@@ -0,0 +1,137 @@
|
|
|
+/// <reference types="bun-types" />
|
|
|
+
|
|
|
+import { afterEach, beforeEach, describe, expect, mock, test } from 'bun:test';
|
|
|
+import {
|
|
|
+ mkdirSync,
|
|
|
+ mkdtempSync,
|
|
|
+ readFileSync,
|
|
|
+ rmSync,
|
|
|
+ writeFileSync,
|
|
|
+} from 'node:fs';
|
|
|
+import { tmpdir } from 'node:os';
|
|
|
+import { join } from 'node:path';
|
|
|
+
|
|
|
+type SpawnResult = {
|
|
|
+ exited: Promise<number>;
|
|
|
+ stdout: () => Promise<string>;
|
|
|
+ stderr: () => Promise<string>;
|
|
|
+ kill: () => boolean;
|
|
|
+ exitCode: number | null;
|
|
|
+ proc: never;
|
|
|
+};
|
|
|
+
|
|
|
+const crossSpawnMock = mock((_command: string[]) => createSpawnResult());
|
|
|
+
|
|
|
+mock.module('../utils/compat', () => ({
|
|
|
+ crossSpawn: crossSpawnMock,
|
|
|
+}));
|
|
|
+
|
|
|
+let importCounter = 0;
|
|
|
+
|
|
|
+function createSpawnResult(exitCode = 0): SpawnResult {
|
|
|
+ return {
|
|
|
+ exited: Promise.resolve(exitCode),
|
|
|
+ stdout: () => Promise.resolve(''),
|
|
|
+ stderr: () => Promise.resolve(''),
|
|
|
+ kill: () => true,
|
|
|
+ exitCode,
|
|
|
+ proc: {} as never,
|
|
|
+ };
|
|
|
+}
|
|
|
+
|
|
|
+async function importFreshConfigIo() {
|
|
|
+ return import(`./config-io?test=${importCounter++}`);
|
|
|
+}
|
|
|
+
|
|
|
+describe('warmOpenCodePluginCache', () => {
|
|
|
+ const originalArgv = [...process.argv];
|
|
|
+ const originalXdgCacheHome = process.env.XDG_CACHE_HOME;
|
|
|
+
|
|
|
+ beforeEach(() => {
|
|
|
+ crossSpawnMock.mockReset();
|
|
|
+ crossSpawnMock.mockImplementation((_command: string[]) =>
|
|
|
+ createSpawnResult(),
|
|
|
+ );
|
|
|
+ delete process.env.XDG_CACHE_HOME;
|
|
|
+ });
|
|
|
+
|
|
|
+ afterEach(() => {
|
|
|
+ process.argv = [...originalArgv];
|
|
|
+ if (originalXdgCacheHome === undefined) {
|
|
|
+ delete process.env.XDG_CACHE_HOME;
|
|
|
+ } else {
|
|
|
+ process.env.XDG_CACHE_HOME = originalXdgCacheHome;
|
|
|
+ }
|
|
|
+ });
|
|
|
+
|
|
|
+ test('prewarms the OpenCode cache for bunx installs', async () => {
|
|
|
+ const tmpDir = mkdirTemp();
|
|
|
+ const cacheHome = join(tmpDir, 'cache');
|
|
|
+ process.env.XDG_CACHE_HOME = cacheHome;
|
|
|
+
|
|
|
+ const packageRoot = join(
|
|
|
+ tmpDir,
|
|
|
+ 'bunx-1000-oh-my-opencode-slim@latest',
|
|
|
+ 'node_modules',
|
|
|
+ 'oh-my-opencode-slim',
|
|
|
+ );
|
|
|
+ mkdirSync(join(packageRoot, 'dist', 'cli'), { recursive: true });
|
|
|
+ writeFileSync(
|
|
|
+ join(packageRoot, 'package.json'),
|
|
|
+ JSON.stringify({ name: 'oh-my-opencode-slim' }),
|
|
|
+ );
|
|
|
+ process.argv[1] = join(packageRoot, 'dist', 'cli', 'index.js');
|
|
|
+
|
|
|
+ const { warmOpenCodePluginCache } = await importFreshConfigIo();
|
|
|
+ const result = await warmOpenCodePluginCache();
|
|
|
+
|
|
|
+ const expectedCacheDir = join(
|
|
|
+ cacheHome,
|
|
|
+ 'opencode',
|
|
|
+ 'packages',
|
|
|
+ 'oh-my-opencode-slim@latest',
|
|
|
+ );
|
|
|
+
|
|
|
+ expect(result?.success).toBe(true);
|
|
|
+ expect(result?.configPath).toBe(expectedCacheDir);
|
|
|
+ expect(crossSpawnMock).toHaveBeenCalledTimes(1);
|
|
|
+ expect(crossSpawnMock.mock.calls[0][0]).toEqual(['bun', 'install']);
|
|
|
+ expect(crossSpawnMock.mock.calls[0][1]).toEqual(
|
|
|
+ expect.objectContaining({ cwd: expectedCacheDir }),
|
|
|
+ );
|
|
|
+ expect(
|
|
|
+ JSON.parse(readFileSync(join(expectedCacheDir, 'package.json'), 'utf-8')),
|
|
|
+ ).toEqual({
|
|
|
+ name: 'oh-my-opencode-slim-cache',
|
|
|
+ private: true,
|
|
|
+ dependencies: {
|
|
|
+ 'oh-my-opencode-slim': 'latest',
|
|
|
+ },
|
|
|
+ });
|
|
|
+
|
|
|
+ rmSync(tmpDir, { recursive: true, force: true });
|
|
|
+ });
|
|
|
+
|
|
|
+ test('skips cache warm-up for local repo installs', async () => {
|
|
|
+ const tmpDir = mkdirTemp();
|
|
|
+ const packageRoot = join(tmpDir, 'repo');
|
|
|
+ mkdirSync(join(packageRoot, 'dist', 'cli'), { recursive: true });
|
|
|
+ writeFileSync(
|
|
|
+ join(packageRoot, 'package.json'),
|
|
|
+ JSON.stringify({ name: 'oh-my-opencode-slim' }),
|
|
|
+ );
|
|
|
+ process.argv[1] = join(packageRoot, 'dist', 'cli', 'index.js');
|
|
|
+
|
|
|
+ const { warmOpenCodePluginCache } = await importFreshConfigIo();
|
|
|
+ const result = await warmOpenCodePluginCache();
|
|
|
+
|
|
|
+ expect(result).toBeNull();
|
|
|
+ expect(crossSpawnMock).not.toHaveBeenCalled();
|
|
|
+
|
|
|
+ rmSync(tmpDir, { recursive: true, force: true });
|
|
|
+ });
|
|
|
+});
|
|
|
+
|
|
|
+function mkdirTemp(): string {
|
|
|
+ return mkdtempSync(join(tmpdir(), 'opencode-cache-test-'));
|
|
|
+}
|