test(cli): add unit tests for init sessionPrefix, name, port, and --smart fixes
- Verify auto mode writes `name` and `sessionPrefix` to project config - Verify warning is shown when no free port is found in range - Verify `--smart` flag description includes "coming soon" - Verify `sessionPrefix` matches core `generateSessionPrefix` output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c52caec441
commit
8eff44aa3f
|
|
@ -2,9 +2,10 @@ import { describe, it, expect, vi, afterEach } from "vitest";
|
|||
import { mkdtempSync, writeFileSync, readFileSync, rmSync, existsSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
import { tmpdir } from "node:os";
|
||||
import { createServer } from "node:net";
|
||||
import { createServer, type Server } from "node:net";
|
||||
|
||||
import { Command } from "commander";
|
||||
import { parse as yamlParse } from "yaml";
|
||||
import { registerInit } from "../../src/commands/init.js";
|
||||
|
||||
let tmpDir: string;
|
||||
|
|
@ -85,4 +86,101 @@ describe("init command", () => {
|
|||
await new Promise<void>((resolve) => blocker.close(() => resolve()));
|
||||
}
|
||||
});
|
||||
|
||||
it("auto mode writes name and sessionPrefix to project config", async () => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), "ao-init-test-"));
|
||||
const outputPath = join(tmpDir, "agent-orchestrator.yaml");
|
||||
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerInit(program);
|
||||
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await program.parseAsync(["node", "test", "init", "--auto", "--output", outputPath]);
|
||||
|
||||
const content = readFileSync(outputPath, "utf-8");
|
||||
const config = yamlParse(content) as Record<string, unknown>;
|
||||
const projects = config.projects as Record<string, Record<string, unknown>>;
|
||||
const projectIds = Object.keys(projects);
|
||||
expect(projectIds.length).toBeGreaterThan(0);
|
||||
|
||||
const projectId = projectIds[0];
|
||||
const project = projects[projectId];
|
||||
|
||||
// BUG-01: sessionPrefix must exist and not be "undefined"
|
||||
expect(project.sessionPrefix).toBeDefined();
|
||||
expect(project.sessionPrefix).not.toBe("undefined");
|
||||
expect(typeof project.sessionPrefix).toBe("string");
|
||||
expect((project.sessionPrefix as string).length).toBeGreaterThan(0);
|
||||
|
||||
// BUG-02: name must match the project ID
|
||||
expect(project.name).toBe(projectId);
|
||||
});
|
||||
|
||||
it("auto mode warns when no free port is found", async () => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), "ao-init-test-"));
|
||||
const outputPath = join(tmpDir, "agent-orchestrator.yaml");
|
||||
|
||||
// Mock isPortAvailable to always return false (all ports busy)
|
||||
const webDirModule = await import("../../src/lib/web-dir.js");
|
||||
vi.spyOn(webDirModule, "isPortAvailable").mockResolvedValue(false);
|
||||
|
||||
const logSpy = vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerInit(program);
|
||||
|
||||
await program.parseAsync(["node", "test", "init", "--auto", "--output", outputPath]);
|
||||
|
||||
// Should warn about no free port
|
||||
const logCalls = logSpy.mock.calls.map((args) => args.join(" "));
|
||||
const hasPortWarning = logCalls.some((msg) => msg.includes("No free port found"));
|
||||
expect(hasPortWarning).toBe(true);
|
||||
|
||||
// Should still write config with fallback port 3000
|
||||
const content = readFileSync(outputPath, "utf-8");
|
||||
expect(content).toContain("port: 3000");
|
||||
});
|
||||
|
||||
it("--smart flag description includes 'coming soon'", () => {
|
||||
const program = new Command();
|
||||
registerInit(program);
|
||||
|
||||
const initCmd = program.commands.find((c) => c.name() === "init");
|
||||
expect(initCmd).toBeDefined();
|
||||
|
||||
const smartOption = initCmd!.options.find((o) => o.long === "--smart");
|
||||
expect(smartOption).toBeDefined();
|
||||
expect(smartOption!.description).toContain("coming soon");
|
||||
});
|
||||
|
||||
it("auto mode sessionPrefix uses core generateSessionPrefix heuristics", async () => {
|
||||
tmpDir = mkdtempSync(join(tmpdir(), "ao-init-test-"));
|
||||
const outputPath = join(tmpDir, "agent-orchestrator.yaml");
|
||||
|
||||
// Run from a directory whose basename is kebab-case
|
||||
// The cwd() in init.ts determines the projectId via basename()
|
||||
// We can't easily control cwd in tests, but we can verify the prefix
|
||||
// is consistent with generateSessionPrefix for whatever projectId is used
|
||||
const { generateSessionPrefix } = await import("@composio/ao-core");
|
||||
|
||||
const program = new Command();
|
||||
program.exitOverride();
|
||||
registerInit(program);
|
||||
|
||||
vi.spyOn(console, "log").mockImplementation(() => {});
|
||||
|
||||
await program.parseAsync(["node", "test", "init", "--auto", "--output", outputPath]);
|
||||
|
||||
const content = readFileSync(outputPath, "utf-8");
|
||||
const config = yamlParse(content) as Record<string, unknown>;
|
||||
const projects = config.projects as Record<string, Record<string, unknown>>;
|
||||
const projectId = Object.keys(projects)[0];
|
||||
const project = projects[projectId];
|
||||
|
||||
// sessionPrefix must match what generateSessionPrefix produces
|
||||
expect(project.sessionPrefix).toBe(generateSessionPrefix(projectId));
|
||||
});
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in New Issue