mirror of https://github.com/garrytan/gstack.git
Merge d98b587928 into c43c850cae
This commit is contained in:
commit
55d4275894
|
|
@ -749,6 +749,47 @@ describe('CLI lifecycle', () => {
|
||||||
expect(result.stdout).toContain('Status: healthy');
|
expect(result.stdout).toContain('Status: healthy');
|
||||||
expect(result.stderr).toContain('Starting server');
|
expect(result.stderr).toContain('Starting server');
|
||||||
}, 20000);
|
}, 20000);
|
||||||
|
|
||||||
|
test('sequential CLI invocations reuse the same daemon and page state', async () => {
|
||||||
|
const stateFile = `/tmp/browse-test-persist-${Date.now()}.json`;
|
||||||
|
const cliPath = path.resolve(__dirname, '../src/cli.ts');
|
||||||
|
const cliEnv: Record<string, string> = {};
|
||||||
|
for (const [k, v] of Object.entries(process.env)) {
|
||||||
|
if (v !== undefined) cliEnv[k] = v;
|
||||||
|
}
|
||||||
|
cliEnv.BROWSE_STATE_FILE = stateFile;
|
||||||
|
|
||||||
|
const runCli = (args: string[]) =>
|
||||||
|
new Promise<{ code: number; stdout: string; stderr: string }>((resolve) => {
|
||||||
|
const proc = spawn('bun', ['run', cliPath, ...args], {
|
||||||
|
timeout: 15000,
|
||||||
|
env: cliEnv,
|
||||||
|
});
|
||||||
|
let stdout = '';
|
||||||
|
let stderr = '';
|
||||||
|
proc.stdout.on('data', (d) => stdout += d.toString());
|
||||||
|
proc.stderr.on('data', (d) => stderr += d.toString());
|
||||||
|
proc.on('close', (code) => resolve({ code: code ?? 1, stdout, stderr }));
|
||||||
|
});
|
||||||
|
|
||||||
|
const gotoResult = await runCli(['goto', `${baseUrl}/basic.html`]);
|
||||||
|
expect(gotoResult.code).toBe(0);
|
||||||
|
expect(gotoResult.stdout).toContain('Navigated to');
|
||||||
|
|
||||||
|
const pid1 = JSON.parse(fs.readFileSync(stateFile, 'utf-8')).pid;
|
||||||
|
|
||||||
|
const textResult = await runCli(['text']);
|
||||||
|
expect(textResult.code).toBe(0);
|
||||||
|
expect(textResult.stdout).toContain('Hello World');
|
||||||
|
|
||||||
|
const pid2 = JSON.parse(fs.readFileSync(stateFile, 'utf-8')).pid;
|
||||||
|
|
||||||
|
try { fs.unlinkSync(stateFile); } catch {}
|
||||||
|
try { process.kill(pid2, 'SIGTERM'); } catch {}
|
||||||
|
|
||||||
|
expect(pid1).toBe(pid2);
|
||||||
|
expect(textResult.stderr).not.toContain('Starting server');
|
||||||
|
}, 20000);
|
||||||
});
|
});
|
||||||
|
|
||||||
// ─── Buffer bounds ──────────────────────────────────────────────
|
// ─── Buffer bounds ──────────────────────────────────────────────
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,94 @@
|
||||||
|
import { describe, test, expect } from 'bun:test';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import { spawnSync } from 'child_process';
|
||||||
|
|
||||||
|
const ROOT = path.resolve(import.meta.dir, '..');
|
||||||
|
|
||||||
|
function makeTempDir(prefix: string): string {
|
||||||
|
return fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyRepoWithoutGit(dest: string): void {
|
||||||
|
fs.cpSync(ROOT, dest, {
|
||||||
|
recursive: true,
|
||||||
|
force: true,
|
||||||
|
preserveTimestamps: true,
|
||||||
|
filter: (src) => {
|
||||||
|
const rel = path.relative(ROOT, src);
|
||||||
|
if (!rel) return true;
|
||||||
|
const top = rel.split(path.sep)[0];
|
||||||
|
return top !== '.git' && top !== 'node_modules' && top !== '.agents';
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const nodeModulesSrc = path.join(ROOT, 'node_modules');
|
||||||
|
const nodeModulesDest = path.join(dest, 'node_modules');
|
||||||
|
fs.symlinkSync(
|
||||||
|
nodeModulesSrc,
|
||||||
|
nodeModulesDest,
|
||||||
|
process.platform === 'win32' ? 'junction' : 'dir'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function runSetup(cwd: string, homeDir: string): ReturnType<typeof spawnSync> {
|
||||||
|
return spawnSync('bash', ['./setup', '--host', 'codex'], {
|
||||||
|
cwd,
|
||||||
|
env: { ...process.env, HOME: homeDir },
|
||||||
|
encoding: 'utf-8',
|
||||||
|
timeout: 120_000,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
describe('setup --host codex smoke', () => {
|
||||||
|
test('global install creates Codex runtime root and generated skills', () => {
|
||||||
|
const repoDir = makeTempDir('gstack-codex-global-repo-');
|
||||||
|
const homeDir = makeTempDir('gstack-codex-global-home-');
|
||||||
|
|
||||||
|
try {
|
||||||
|
copyRepoWithoutGit(repoDir);
|
||||||
|
const result = runSetup(repoDir, homeDir);
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(result.stdout).toContain('gstack ready (codex).');
|
||||||
|
|
||||||
|
const runtimeRoot = path.join(homeDir, '.codex', 'skills', 'gstack');
|
||||||
|
const reviewSkill = path.join(homeDir, '.codex', 'skills', 'gstack-review', 'SKILL.md');
|
||||||
|
|
||||||
|
expect(fs.existsSync(path.join(runtimeRoot, 'SKILL.md'))).toBe(true);
|
||||||
|
expect(fs.existsSync(path.join(runtimeRoot, 'browse', 'dist'))).toBe(true);
|
||||||
|
expect(fs.existsSync(path.join(reviewSkill))).toBe(true);
|
||||||
|
expect(fs.lstatSync(path.join(homeDir, '.codex', 'skills', 'gstack-review')).isSymbolicLink()).toBe(true);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(repoDir, { recursive: true, force: true });
|
||||||
|
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}, 120_000);
|
||||||
|
|
||||||
|
test('repo-local install writes generated skills next to .agents checkout only', () => {
|
||||||
|
const projectDir = makeTempDir('gstack-codex-local-project-');
|
||||||
|
const homeDir = makeTempDir('gstack-codex-local-home-');
|
||||||
|
const repoDir = path.join(projectDir, '.agents', 'skills', 'gstack');
|
||||||
|
|
||||||
|
try {
|
||||||
|
fs.mkdirSync(path.dirname(repoDir), { recursive: true });
|
||||||
|
copyRepoWithoutGit(repoDir);
|
||||||
|
const result = runSetup(repoDir, homeDir);
|
||||||
|
|
||||||
|
expect(result.status).toBe(0);
|
||||||
|
expect(result.stdout).toContain('gstack ready (codex).');
|
||||||
|
|
||||||
|
const localSkill = path.join(projectDir, '.agents', 'skills', 'gstack-review', 'SKILL.md');
|
||||||
|
const localSidecar = path.join(projectDir, '.agents', 'skills', 'gstack', 'bin');
|
||||||
|
const globalSkill = path.join(homeDir, '.codex', 'skills', 'gstack-review');
|
||||||
|
|
||||||
|
expect(fs.existsSync(localSkill)).toBe(true);
|
||||||
|
expect(fs.existsSync(localSidecar)).toBe(true);
|
||||||
|
expect(fs.existsSync(globalSkill)).toBe(false);
|
||||||
|
} finally {
|
||||||
|
fs.rmSync(projectDir, { recursive: true, force: true });
|
||||||
|
fs.rmSync(homeDir, { recursive: true, force: true });
|
||||||
|
}
|
||||||
|
}, 120_000);
|
||||||
|
});
|
||||||
Loading…
Reference in New Issue