feat: bootstrap auto virtual tryon admin frontend
This commit is contained in:
28
tests/scripts/dev-stack-stack-mjs.d.ts
vendored
Normal file
28
tests/scripts/dev-stack-stack-mjs.d.ts
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
declare module "../../scripts/dev-stack/stack.mjs" {
|
||||
export type StackServiceConfig = {
|
||||
key: string;
|
||||
cwd: string;
|
||||
port: number | null;
|
||||
command: string[];
|
||||
};
|
||||
|
||||
export type StackConfig = {
|
||||
frontendRoot: string;
|
||||
backendRoot: string;
|
||||
runtimeRoot: string;
|
||||
pidRoot: string;
|
||||
logRoot: string;
|
||||
temporalDatabaseFile: string;
|
||||
services: StackServiceConfig[];
|
||||
};
|
||||
|
||||
export function getDefaultTemporalCandidates(tempDirectory: string): string[];
|
||||
|
||||
export function resolveTemporalCli(options: {
|
||||
pathLookupResult: string | null;
|
||||
candidatePaths: string[];
|
||||
existingPaths: Set<string>;
|
||||
}): string | null;
|
||||
|
||||
export function createStackConfig(frontendRoot: string, backendRoot?: string): StackConfig;
|
||||
}
|
||||
183
tests/scripts/dev-stack.test.ts
Normal file
183
tests/scripts/dev-stack.test.ts
Normal file
@@ -0,0 +1,183 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
createStackConfig,
|
||||
formatServiceLogs,
|
||||
getDefaultTemporalCandidates,
|
||||
resolveTemporalCli,
|
||||
selectServicesForLogs,
|
||||
} from "../../scripts/dev-stack/stack.mjs";
|
||||
|
||||
describe("createStackConfig", () => {
|
||||
it("derives frontend and sibling backend paths from the workspace root", () => {
|
||||
const config = createStackConfig("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
|
||||
expect(config.frontendRoot).toBe("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
expect(config.backendRoot).toBe("/Volumes/DockCase/codes/auto-virtual-tryon");
|
||||
expect(config.runtimeRoot).toBe(
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon-frontend/.dev-stack",
|
||||
);
|
||||
expect(config.temporalDatabaseFile).toBe(
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon-frontend/.dev-stack/temporal-cli-dev.db",
|
||||
);
|
||||
});
|
||||
|
||||
it("builds the expected service commands", () => {
|
||||
const config = createStackConfig("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
|
||||
expect(config.services).toEqual([
|
||||
{
|
||||
key: "temporal",
|
||||
cwd: "/Volumes/DockCase/codes/auto-virtual-tryon-frontend/.dev-stack",
|
||||
port: 7233,
|
||||
command: [
|
||||
"__TEMPORAL_CLI__",
|
||||
"server",
|
||||
"start-dev",
|
||||
"--ip",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"7233",
|
||||
"--headless",
|
||||
"--db-filename",
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon-frontend/.dev-stack/temporal-cli-dev.db",
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "backend-api",
|
||||
cwd: "/Volumes/DockCase/codes/auto-virtual-tryon",
|
||||
port: 8000,
|
||||
command: [
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon/.venv/bin/python",
|
||||
"-m",
|
||||
"uvicorn",
|
||||
"app.main:app",
|
||||
"--host",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"8000",
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "backend-worker",
|
||||
cwd: "/Volumes/DockCase/codes/auto-virtual-tryon",
|
||||
port: null,
|
||||
command: [
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon/.venv/bin/python",
|
||||
"-m",
|
||||
"app.workers.runner",
|
||||
],
|
||||
},
|
||||
{
|
||||
key: "frontend",
|
||||
cwd: "/Volumes/DockCase/codes/auto-virtual-tryon-frontend",
|
||||
port: 3000,
|
||||
command: [
|
||||
"/Volumes/DockCase/codes/auto-virtual-tryon-frontend/node_modules/.bin/next",
|
||||
"dev",
|
||||
"--hostname",
|
||||
"127.0.0.1",
|
||||
"--port",
|
||||
"3000",
|
||||
],
|
||||
},
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("resolveTemporalCli", () => {
|
||||
it("prefers a CLI already available on PATH", () => {
|
||||
const resolved = resolveTemporalCli({
|
||||
pathLookupResult: "/usr/local/bin/temporal",
|
||||
candidatePaths: ["/tmp/temporal-sdk-python-1.24.0"],
|
||||
existingPaths: new Set(["/usr/local/bin/temporal", "/tmp/temporal-sdk-python-1.24.0"]),
|
||||
});
|
||||
|
||||
expect(resolved).toBe("/usr/local/bin/temporal");
|
||||
});
|
||||
|
||||
it("falls back to a downloaded SDK binary when PATH does not contain temporal", () => {
|
||||
const resolved = resolveTemporalCli({
|
||||
pathLookupResult: null,
|
||||
candidatePaths: ["/tmp/temporal-sdk-python-1.24.0", "/tmp/temporal-cli"],
|
||||
existingPaths: new Set(["/tmp/temporal-sdk-python-1.24.0"]),
|
||||
});
|
||||
|
||||
expect(resolved).toBe("/tmp/temporal-sdk-python-1.24.0");
|
||||
});
|
||||
|
||||
it("returns null when no temporal binary is available", () => {
|
||||
const resolved = resolveTemporalCli({
|
||||
pathLookupResult: null,
|
||||
candidatePaths: ["/tmp/temporal-sdk-python-1.24.0"],
|
||||
existingPaths: new Set<string>(),
|
||||
});
|
||||
|
||||
expect(resolved).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe("getDefaultTemporalCandidates", () => {
|
||||
it("includes the known SDK cache binary names under the temp directory", () => {
|
||||
expect(getDefaultTemporalCandidates("/var/folders/example/T")).toEqual([
|
||||
"/var/folders/example/T/temporal-sdk-python-1.24.0",
|
||||
"/var/folders/example/T/temporal-sdk-python-1.25.0",
|
||||
"/var/folders/example/T/temporal-sdk-python-1.26.0",
|
||||
"/var/folders/example/T/temporal-cli",
|
||||
]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("selectServicesForLogs", () => {
|
||||
it("returns every service when no filter is provided", () => {
|
||||
const config = createStackConfig("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
|
||||
expect(
|
||||
selectServicesForLogs(config.services, null).map((service: { key: string }) => service.key),
|
||||
).toEqual(["temporal", "backend-api", "backend-worker", "frontend"]);
|
||||
});
|
||||
|
||||
it("filters to a single service key", () => {
|
||||
const config = createStackConfig("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
|
||||
expect(
|
||||
selectServicesForLogs(config.services, "frontend").map((service: { key: string }) => service.key),
|
||||
).toEqual(["frontend"]);
|
||||
});
|
||||
|
||||
it("throws a helpful error for an unknown service", () => {
|
||||
const config = createStackConfig("/Volumes/DockCase/codes/auto-virtual-tryon-frontend");
|
||||
|
||||
expect(() => selectServicesForLogs(config.services, "foo")).toThrow(
|
||||
"Unknown service \"foo\". Expected one of: temporal, backend-api, backend-worker, frontend",
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
describe("formatServiceLogs", () => {
|
||||
it("formats sections with stable headers and preserves line order", () => {
|
||||
const output = formatServiceLogs([
|
||||
{
|
||||
key: "frontend",
|
||||
logFilePath: "/tmp/frontend.log",
|
||||
lines: ["> dev", "ready"],
|
||||
},
|
||||
{
|
||||
key: "backend-api",
|
||||
logFilePath: "/tmp/backend-api.log",
|
||||
lines: [],
|
||||
},
|
||||
]);
|
||||
|
||||
expect(output).toBe(
|
||||
[
|
||||
"--- frontend (/tmp/frontend.log) ---",
|
||||
"> dev",
|
||||
"ready",
|
||||
"",
|
||||
"--- backend-api (/tmp/backend-api.log) ---",
|
||||
"(log file is empty)",
|
||||
].join("\n"),
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user