fix: restore CLI validation and test baseline (#1857)
Fixed lazy CLI helper imports for tunnel flows Restored update command version detection Made web test and dead-code commands run reliably
This commit is contained in:
+1
-1
@@ -51,7 +51,7 @@
|
|||||||
"vscode:package": "bun run --cwd packages/vscode package",
|
"vscode:package": "bun run --cwd packages/vscode package",
|
||||||
"vscode:type-check": "bun run --cwd packages/vscode type-check",
|
"vscode:type-check": "bun run --cwd packages/vscode type-check",
|
||||||
"docs:validate": "node scripts/docs/validate-docs.mjs",
|
"docs:validate": "node scripts/docs/validate-docs.mjs",
|
||||||
"dead-code": "bunx knip --no-exit-code --include files,exports,nsExports,types,nsTypes,enumMembers,namespaceMembers,duplicates",
|
"dead-code": "bunx knip@5.80.0 --no-exit-code --include files,exports,nsExports,types,nsTypes,enumMembers,duplicates",
|
||||||
"doctor": "node scripts/react-doctor.mjs",
|
"doctor": "node scripts/react-doctor.mjs",
|
||||||
"icons:sprite": "node scripts/generate-file-type-sprite.mjs",
|
"icons:sprite": "node scripts/generate-file-type-sprite.mjs",
|
||||||
"icons:generate": "bun run scripts/generate-icon-sprite.mjs",
|
"icons:generate": "bun run scripts/generate-icon-sprite.mjs",
|
||||||
|
|||||||
@@ -210,6 +210,7 @@ export {
|
|||||||
requestServerShutdown,
|
requestServerShutdown,
|
||||||
requestJson,
|
requestJson,
|
||||||
isServerHealthReady,
|
isServerHealthReady,
|
||||||
|
waitForServerHealth,
|
||||||
fetchTunnelProvidersFromPort,
|
fetchTunnelProvidersFromPort,
|
||||||
fetchSystemInfoFromPort,
|
fetchSystemInfoFromPort,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -145,6 +145,7 @@ export {
|
|||||||
resolveServeHost,
|
resolveServeHost,
|
||||||
resolveApiHost,
|
resolveApiHost,
|
||||||
formatHostForUrl,
|
formatHostForUrl,
|
||||||
|
isUnsafeBrowserPort,
|
||||||
buildLocalUrl,
|
buildLocalUrl,
|
||||||
detectLanIPv4Address,
|
detectLanIPv4Address,
|
||||||
assertSafeBrowserPort,
|
assertSafeBrowserPort,
|
||||||
|
|||||||
@@ -107,5 +107,7 @@ export {
|
|||||||
getLogFilePath,
|
getLogFilePath,
|
||||||
getTunnelProfilesFilePath,
|
getTunnelProfilesFilePath,
|
||||||
getLegacyCloudflareManagedRemoteFilePath,
|
getLegacyCloudflareManagedRemoteFilePath,
|
||||||
|
readLastManagedLocalConfigPath,
|
||||||
|
writeLastManagedLocalConfigPath,
|
||||||
getRunDir,
|
getRunDir,
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import path from 'path';
|
|||||||
import crypto from 'crypto';
|
import crypto from 'crypto';
|
||||||
import { EXIT_CODE, TunnelCliError } from './cli-errors.js';
|
import { EXIT_CODE, TunnelCliError } from './cli-errors.js';
|
||||||
import { DEFAULT_PORT, findClosestMatch, generateCompletionScript, showTunnelHelp } from './cli-args.js';
|
import { DEFAULT_PORT, findClosestMatch, generateCompletionScript, showTunnelHelp } from './cli-args.js';
|
||||||
import { requestJson, fetchSystemInfoFromPort } from './cli-http.js';
|
import { requestJson, fetchSystemInfoFromPort, waitForServerHealth } from './cli-http.js';
|
||||||
import {
|
import {
|
||||||
discoverRunningInstances,
|
discoverRunningInstances,
|
||||||
getLatestInstance,
|
getLatestInstance,
|
||||||
@@ -34,7 +34,11 @@ import {
|
|||||||
resolveTunnelTtlOverrides,
|
resolveTunnelTtlOverrides,
|
||||||
} from './cli-tunnel-utils.js';
|
} from './cli-tunnel-utils.js';
|
||||||
import { DEFAULT_TUNNEL_PROVIDER_CAPABILITIES } from './cli-tunnel-capabilities.js';
|
import { DEFAULT_TUNNEL_PROVIDER_CAPABILITIES } from './cli-tunnel-capabilities.js';
|
||||||
import { assertSafeBrowserPort, buildLocalUrl } from './cli-network.js';
|
import { assertSafeBrowserPort, buildLocalUrl, isUnsafeBrowserPort } from './cli-network.js';
|
||||||
|
import {
|
||||||
|
readLastManagedLocalConfigPath,
|
||||||
|
writeLastManagedLocalConfigPath,
|
||||||
|
} from './cli-paths.js';
|
||||||
import {
|
import {
|
||||||
intro as clackIntro,
|
intro as clackIntro,
|
||||||
outro as clackOutro,
|
outro as clackOutro,
|
||||||
|
|||||||
@@ -1,11 +1,11 @@
|
|||||||
import { beforeEach, describe, expect, it, mock } from 'bun:test';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
|
|
||||||
const gitLibraries = {
|
const gitLibraries = {
|
||||||
stageFiles: mock(),
|
stageFiles: vi.fn(),
|
||||||
unstageFiles: mock(),
|
unstageFiles: vi.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
mock.module('./index.js', () => ({
|
vi.mock('./index.js', () => ({
|
||||||
stageFiles: gitLibraries.stageFiles,
|
stageFiles: gitLibraries.stageFiles,
|
||||||
unstageFiles: gitLibraries.unstageFiles,
|
unstageFiles: gitLibraries.unstageFiles,
|
||||||
}));
|
}));
|
||||||
|
|||||||
@@ -982,6 +982,7 @@ export function registerGitHubRoutes(app) {
|
|||||||
if (upstream) {
|
if (upstream) {
|
||||||
try {
|
try {
|
||||||
const { getRemotes } = await import('../git/index.js');
|
const { getRemotes } = await import('../git/index.js');
|
||||||
|
const { resolveGitHubRepoFromDirectory } = await import('./index.js');
|
||||||
const remotes = await getRemotes(directory);
|
const remotes = await getRemotes(directory);
|
||||||
for (const r of remotes) {
|
for (const r of remotes) {
|
||||||
if (r?.name) {
|
if (r?.name) {
|
||||||
|
|||||||
@@ -621,7 +621,7 @@ export function getUpdateCommand(pm = detectPackageManager()) {
|
|||||||
/**
|
/**
|
||||||
* Get current installed version from package.json
|
* Get current installed version from package.json
|
||||||
*/
|
*/
|
||||||
function getCurrentVersion() {
|
export function getCurrentVersion() {
|
||||||
try {
|
try {
|
||||||
const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
|
const pkgPath = path.resolve(__dirname, '..', '..', 'package.json');
|
||||||
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ vi.mock('node:child_process', () => ({
|
|||||||
spawnSync: vi.fn(() => ({ status: 0, stdout: '/usr/local/bin', stderr: '' })),
|
spawnSync: vi.fn(() => ({ status: 0, stdout: '/usr/local/bin', stderr: '' })),
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const { checkForUpdates } = await import('./package-manager.js');
|
const { checkForUpdates, getCurrentVersion } = await import('./package-manager.js');
|
||||||
|
|
||||||
/** Helper: create a fetch mock that routes by URL pattern */
|
/** Helper: create a fetch mock that routes by URL pattern */
|
||||||
function createFetchMock() {
|
function createFetchMock() {
|
||||||
@@ -244,3 +244,10 @@ describe('checkForUpdates', () => {
|
|||||||
expect(result.available).toBe(false);
|
expect(result.available).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('getCurrentVersion', () => {
|
||||||
|
it('is exported for the CLI update command', () => {
|
||||||
|
expect(typeof getCurrentVersion).toBe('function');
|
||||||
|
expect(getCurrentVersion()).toMatch(/^\d+\.\d+\.\d+|unknown$/);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -32,14 +32,16 @@ describe('createWebFilesAPI', () => {
|
|||||||
runtimeFetchMock.mockResolvedValueOnce(Response.json({ path: '/worktree-b/file.txt', isFile: true, size: 12 }));
|
runtimeFetchMock.mockResolvedValueOnce(Response.json({ path: '/worktree-b/file.txt', isFile: true, size: 12 }));
|
||||||
await api.statFile?.('/worktree-b/file.txt', { directory: '/worktree-a' });
|
await api.statFile?.('/worktree-b/file.txt', { directory: '/worktree-a' });
|
||||||
|
|
||||||
expect(runtimeFetchMock).toHaveBeenLastCalledWith('/api/fs/stat?path=%2Fworktree-b%2Ffile.txt', {
|
expect(runtimeFetchMock).toHaveBeenLastCalledWith('/api/fs/stat', {
|
||||||
|
query: new URLSearchParams({ path: '/worktree-b/file.txt' }),
|
||||||
headers: { 'x-opencode-directory': '/worktree-a' },
|
headers: { 'x-opencode-directory': '/worktree-a' },
|
||||||
});
|
});
|
||||||
|
|
||||||
runtimeFetchMock.mockResolvedValueOnce(new Response('content'));
|
runtimeFetchMock.mockResolvedValueOnce(new Response('content'));
|
||||||
await api.readFile?.('/worktree-b/file.txt', { directory: '/worktree-a' });
|
await api.readFile?.('/worktree-b/file.txt', { directory: '/worktree-a' });
|
||||||
|
|
||||||
expect(runtimeFetchMock).toHaveBeenLastCalledWith('/api/fs/read?path=%2Fworktree-b%2Ffile.txt', {
|
expect(runtimeFetchMock).toHaveBeenLastCalledWith('/api/fs/read', {
|
||||||
|
query: new URLSearchParams({ path: '/worktree-b/file.txt' }),
|
||||||
cache: 'default',
|
cache: 'default',
|
||||||
headers: { 'x-opencode-directory': '/worktree-a' },
|
headers: { 'x-opencode-directory': '/worktree-a' },
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -0,0 +1,31 @@
|
|||||||
|
import {
|
||||||
|
afterAll,
|
||||||
|
afterEach,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
test,
|
||||||
|
vi,
|
||||||
|
} from 'vitest';
|
||||||
|
|
||||||
|
const mock = Object.assign(
|
||||||
|
<T extends (...args: never[]) => unknown>(implementation?: T) => vi.fn(implementation),
|
||||||
|
{
|
||||||
|
module: vi.mock,
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
export {
|
||||||
|
afterAll,
|
||||||
|
afterEach,
|
||||||
|
beforeAll,
|
||||||
|
beforeEach,
|
||||||
|
describe,
|
||||||
|
expect,
|
||||||
|
it,
|
||||||
|
mock,
|
||||||
|
test,
|
||||||
|
vi,
|
||||||
|
};
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
import { fileURLToPath } from 'node:url';
|
||||||
|
import { defineConfig } from 'vitest/config';
|
||||||
|
|
||||||
|
export default defineConfig({
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
'bun:test': fileURLToPath(new URL('./test/bun-test-shim.ts', import.meta.url)),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user