261 lines
7.0 KiB
TypeScript
261 lines
7.0 KiB
TypeScript
import {
|
|
test,
|
|
afterEach,
|
|
expect,
|
|
describe,
|
|
setDefaultTimeout,
|
|
beforeAll,
|
|
} from "bun:test";
|
|
import { execContainer, readFileContainer, runTerraformInit } from "~test";
|
|
import {
|
|
loadTestFile,
|
|
writeExecutable,
|
|
setup as setupUtil,
|
|
execModuleScript,
|
|
expectAgentAPIStarted,
|
|
} from "./test-util";
|
|
|
|
let cleanupFunctions: (() => Promise<void>)[] = [];
|
|
|
|
const registerCleanup = (cleanup: () => Promise<void>) => {
|
|
cleanupFunctions.push(cleanup);
|
|
};
|
|
|
|
// Cleanup logic depends on the fact that bun's built-in test runner
|
|
// runs tests sequentially.
|
|
// https://bun.sh/docs/test/discovery#execution-order
|
|
// Weird things would happen if tried to run tests in parallel.
|
|
// One test could clean up resources that another test was still using.
|
|
afterEach(async () => {
|
|
// reverse the cleanup functions so that they are run in the correct order
|
|
const cleanupFnsCopy = cleanupFunctions.slice().reverse();
|
|
cleanupFunctions = [];
|
|
for (const cleanup of cleanupFnsCopy) {
|
|
try {
|
|
await cleanup();
|
|
} catch (error) {
|
|
console.error("Error during cleanup:", error);
|
|
}
|
|
}
|
|
});
|
|
|
|
interface SetupProps {
|
|
skipAgentAPIMock?: boolean;
|
|
moduleVariables?: Record<string, string>;
|
|
}
|
|
|
|
const moduleDirName = ".agentapi-module";
|
|
|
|
const setup = async (props?: SetupProps): Promise<{ id: string }> => {
|
|
const projectDir = "/home/coder/project";
|
|
const { id } = await setupUtil({
|
|
moduleVariables: {
|
|
experiment_report_tasks: "true",
|
|
install_agentapi: props?.skipAgentAPIMock ? "true" : "false",
|
|
web_app_display_name: "AgentAPI Web",
|
|
web_app_slug: "agentapi-web",
|
|
web_app_icon: "/icon/coder.svg",
|
|
cli_app_display_name: "AgentAPI CLI",
|
|
cli_app_slug: "agentapi-cli",
|
|
agentapi_version: "latest",
|
|
module_dir_name: moduleDirName,
|
|
start_script: await loadTestFile(import.meta.dir, "agentapi-start.sh"),
|
|
folder: projectDir,
|
|
...props?.moduleVariables,
|
|
},
|
|
registerCleanup,
|
|
projectDir,
|
|
skipAgentAPIMock: props?.skipAgentAPIMock,
|
|
moduleDir: import.meta.dir,
|
|
});
|
|
await writeExecutable({
|
|
containerId: id,
|
|
filePath: "/usr/bin/aiagent",
|
|
content: await loadTestFile(import.meta.dir, "ai-agent-mock.js"),
|
|
});
|
|
return { id };
|
|
};
|
|
|
|
// increase the default timeout to 60 seconds
|
|
setDefaultTimeout(60 * 1000);
|
|
|
|
// we don't run these tests in CI because they take too long and make network
|
|
// calls. they are dedicated for local development.
|
|
describe("agentapi", async () => {
|
|
beforeAll(async () => {
|
|
await runTerraformInit(import.meta.dir);
|
|
});
|
|
|
|
test("happy-path", async () => {
|
|
const { id } = await setup();
|
|
|
|
await execModuleScript(id);
|
|
|
|
await expectAgentAPIStarted(id);
|
|
});
|
|
|
|
test("custom-port", async () => {
|
|
const { id } = await setup({
|
|
moduleVariables: {
|
|
agentapi_port: "3827",
|
|
},
|
|
});
|
|
await execModuleScript(id);
|
|
await expectAgentAPIStarted(id, 3827);
|
|
});
|
|
|
|
test("pre-post-install-scripts", async () => {
|
|
const { id } = await setup({
|
|
moduleVariables: {
|
|
pre_install_script: `#!/bin/bash\necho "pre-install"`,
|
|
install_script: `#!/bin/bash\necho "install"`,
|
|
post_install_script: `#!/bin/bash\necho "post-install"`,
|
|
},
|
|
});
|
|
|
|
await execModuleScript(id);
|
|
await expectAgentAPIStarted(id);
|
|
|
|
const preInstallLog = await readFileContainer(
|
|
id,
|
|
`/home/coder/${moduleDirName}/pre_install.log`,
|
|
);
|
|
const installLog = await readFileContainer(
|
|
id,
|
|
`/home/coder/${moduleDirName}/install.log`,
|
|
);
|
|
const postInstallLog = await readFileContainer(
|
|
id,
|
|
`/home/coder/${moduleDirName}/post_install.log`,
|
|
);
|
|
|
|
expect(preInstallLog).toContain("pre-install");
|
|
expect(installLog).toContain("install");
|
|
expect(postInstallLog).toContain("post-install");
|
|
});
|
|
|
|
test("install-agentapi", async () => {
|
|
const { id } = await setup({ skipAgentAPIMock: true });
|
|
|
|
const respModuleScript = await execModuleScript(id);
|
|
expect(respModuleScript.exitCode).toBe(0);
|
|
|
|
await expectAgentAPIStarted(id);
|
|
const respAgentAPI = await execContainer(id, [
|
|
"bash",
|
|
"-c",
|
|
"agentapi --version",
|
|
]);
|
|
expect(respAgentAPI.exitCode).toBe(0);
|
|
});
|
|
|
|
test("no-subdomain-base-path", async () => {
|
|
const { id } = await setup({
|
|
moduleVariables: {
|
|
agentapi_subdomain: "false",
|
|
},
|
|
});
|
|
|
|
const respModuleScript = await execModuleScript(id);
|
|
expect(respModuleScript.exitCode).toBe(0);
|
|
|
|
await expectAgentAPIStarted(id);
|
|
const agentApiStartLog = await readFileContainer(
|
|
id,
|
|
"/home/coder/test-agentapi-start.log",
|
|
);
|
|
expect(agentApiStartLog).toContain(
|
|
"Using AGENTAPI_CHAT_BASE_PATH: /@default/default.foo/apps/agentapi-web/chat",
|
|
);
|
|
});
|
|
|
|
test("validate-agentapi-version", async () => {
|
|
const cases = [
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.3.2",
|
|
},
|
|
shouldThrow: "",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.3.3",
|
|
},
|
|
shouldThrow: "",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.0.1",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
shouldThrow:
|
|
"Running with subdomain = false is only supported by agentapi >= v0.3.3.",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.3.2",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
shouldThrow:
|
|
"Running with subdomain = false is only supported by agentapi >= v0.3.3.",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.3.3",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
shouldThrow: "",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.3.999",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
shouldThrow: "",
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v0.999.999",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "v999.999.999",
|
|
agentapi_subdomain: "false",
|
|
},
|
|
},
|
|
{
|
|
moduleVariables: {
|
|
agentapi_version: "arbitrary-string-bypasses-validation",
|
|
},
|
|
shouldThrow: "",
|
|
},
|
|
];
|
|
for (const { moduleVariables, shouldThrow } of cases) {
|
|
if (shouldThrow) {
|
|
expect(
|
|
setup({ moduleVariables: moduleVariables as Record<string, string> }),
|
|
).rejects.toThrow(shouldThrow);
|
|
} else {
|
|
expect(
|
|
setup({ moduleVariables: moduleVariables as Record<string, string> }),
|
|
).resolves.toBeDefined();
|
|
}
|
|
}
|
|
});
|
|
|
|
test("agentapi-allowed-hosts", async () => {
|
|
// verify that the agentapi binary has access to the AGENTAPI_ALLOWED_HOSTS environment variable
|
|
// set in main.sh
|
|
const { id } = await setup();
|
|
await execModuleScript(id);
|
|
await expectAgentAPIStarted(id);
|
|
const agentApiStartLog = await readFileContainer(
|
|
id,
|
|
"/home/coder/agentapi-mock.log",
|
|
);
|
|
expect(agentApiStartLog).toContain("AGENTAPI_ALLOWED_HOSTS: *");
|
|
});
|
|
});
|