2023-11-05 02:12:52 +00:00
|
|
|
// This golden test requires local Tabby server to be running on port 8087.
|
|
|
|
|
// The server should use tabby linux image version 0.4.0, with model TabbyML/StarCoder-1B,
|
|
|
|
|
// cuda backend, and without any repository specified for RAG.
|
|
|
|
|
|
|
|
|
|
import { spawn } from "child_process";
|
|
|
|
|
import readline from "readline";
|
|
|
|
|
import path from "path";
|
|
|
|
|
import * as fs from "fs-extra";
|
|
|
|
|
import { expect } from "chai";
|
|
|
|
|
|
|
|
|
|
describe("agent golden test", () => {
|
|
|
|
|
const agent = spawn("node", [path.join(__dirname, "../dist/cli.js")]);
|
|
|
|
|
const output: any[] = [];
|
|
|
|
|
readline.createInterface({ input: agent.stdout }).on("line", (line) => {
|
|
|
|
|
output.push(JSON.parse(line));
|
|
|
|
|
});
|
|
|
|
|
|
2023-12-06 14:06:01 +00:00
|
|
|
const waitForResponse = (requestId: number, timeout = 1000) => {
|
2023-11-05 02:12:52 +00:00
|
|
|
return new Promise<void>((resolve, reject) => {
|
|
|
|
|
const start = Date.now();
|
|
|
|
|
const interval = setInterval(() => {
|
|
|
|
|
if (output.find((item) => item[0] === requestId)) {
|
|
|
|
|
clearInterval(interval);
|
|
|
|
|
resolve();
|
|
|
|
|
} else if (Date.now() - start > timeout) {
|
|
|
|
|
clearInterval(interval);
|
|
|
|
|
reject(new Error("Timeout"));
|
|
|
|
|
}
|
|
|
|
|
}, 10);
|
|
|
|
|
});
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-06 14:06:01 +00:00
|
|
|
const createGoldenTest = async (goldenFilepath: string) => {
|
2023-11-05 02:12:52 +00:00
|
|
|
const content = await fs.readFile(goldenFilepath, "utf8");
|
|
|
|
|
const language = path.basename(goldenFilepath, path.extname(goldenFilepath)).replace(/^\d+-/g, "");
|
|
|
|
|
const replaceStart = content.indexOf("⏩");
|
|
|
|
|
const insertStart = content.indexOf("⏭");
|
|
|
|
|
const insertEnd = content.indexOf("⏮");
|
|
|
|
|
const replaceEnd = content.indexOf("⏪");
|
|
|
|
|
const prefix = content.slice(0, replaceStart);
|
|
|
|
|
const replacePrefix = content.slice(replaceStart + 1, insertStart);
|
|
|
|
|
const suggestion = content.slice(insertStart + 1, insertEnd);
|
|
|
|
|
const replaceSuffix = content.slice(insertEnd + 1, replaceEnd);
|
|
|
|
|
const suffix = content.slice(replaceEnd + 1);
|
|
|
|
|
const request = {
|
|
|
|
|
filepath: goldenFilepath,
|
|
|
|
|
language,
|
|
|
|
|
text: prefix + replacePrefix + replaceSuffix + suffix,
|
|
|
|
|
position: prefix.length + replacePrefix.length,
|
|
|
|
|
manually: true,
|
|
|
|
|
};
|
|
|
|
|
const text = replacePrefix + suggestion;
|
|
|
|
|
const expected =
|
|
|
|
|
text.length == 0
|
|
|
|
|
? { choices: [] }
|
|
|
|
|
: {
|
|
|
|
|
choices: [
|
|
|
|
|
{
|
|
|
|
|
index: 0,
|
|
|
|
|
text,
|
|
|
|
|
replaceRange: {
|
|
|
|
|
start: prefix.length,
|
|
|
|
|
end: prefix.length + replacePrefix.length + replaceSuffix.length,
|
|
|
|
|
},
|
|
|
|
|
},
|
|
|
|
|
],
|
|
|
|
|
};
|
|
|
|
|
return { request, expected };
|
|
|
|
|
};
|
|
|
|
|
|
2023-11-11 06:45:38 +00:00
|
|
|
const config = {
|
|
|
|
|
server: {
|
|
|
|
|
endpoint: "http://127.0.0.1:8087",
|
|
|
|
|
token: "",
|
|
|
|
|
requestHeaders: {},
|
|
|
|
|
requestTimeout: 30000,
|
|
|
|
|
},
|
|
|
|
|
completion: {
|
|
|
|
|
prompt: { experimentalStripAutoClosingCharacters: false, maxPrefixLines: 20, maxSuffixLines: 20 },
|
|
|
|
|
debounce: { mode: "adaptive", interval: 250 },
|
|
|
|
|
timeout: 4000,
|
|
|
|
|
},
|
|
|
|
|
postprocess: {
|
2023-11-12 18:11:31 +00:00
|
|
|
limitScope: {
|
|
|
|
|
experimentalSyntax: false,
|
|
|
|
|
indentation: { experimentalKeepBlockScopeWhenCompletingLine: false },
|
|
|
|
|
},
|
2023-11-13 04:53:20 +00:00
|
|
|
calculateReplaceRange: { experimentalSyntax: false },
|
2023-11-11 06:45:38 +00:00
|
|
|
},
|
|
|
|
|
logs: { level: "debug" },
|
|
|
|
|
anonymousUsageTracking: { disable: true },
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let requestId = 0;
|
2023-11-05 02:12:52 +00:00
|
|
|
it("initialize", async () => {
|
2023-11-11 06:45:38 +00:00
|
|
|
requestId++;
|
2023-11-05 02:12:52 +00:00
|
|
|
const initRequest = [
|
|
|
|
|
requestId,
|
|
|
|
|
{
|
|
|
|
|
func: "initialize",
|
|
|
|
|
args: [{ config }],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
agent.stdin.write(JSON.stringify(initRequest) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
expect(output.shift()).to.deep.equal([0, { event: "statusChanged", status: "ready" }]);
|
|
|
|
|
expect(output.shift()).to.deep.equal([0, { event: "configUpdated", config }]);
|
|
|
|
|
expect(output.shift()).to.deep.equal([requestId, true]);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const goldenFiles = fs.readdirSync(path.join(__dirname, "golden")).map((file) => {
|
|
|
|
|
return {
|
2023-11-11 06:45:38 +00:00
|
|
|
path: path.join("golden", file),
|
|
|
|
|
absolutePath: path.join(__dirname, "golden", file),
|
2023-11-05 02:12:52 +00:00
|
|
|
};
|
|
|
|
|
});
|
2023-11-12 18:11:31 +00:00
|
|
|
goldenFiles.forEach((goldenFile) => {
|
2023-11-11 06:45:38 +00:00
|
|
|
it(goldenFile.path, async () => {
|
|
|
|
|
const test = await createGoldenTest(goldenFile.absolutePath);
|
|
|
|
|
requestId++;
|
2023-11-05 02:12:52 +00:00
|
|
|
const request = [requestId, { func: "provideCompletions", args: [test.request] }];
|
|
|
|
|
agent.stdin.write(JSON.stringify(request) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
const response = output.shift();
|
|
|
|
|
expect(response[0]).to.equal(requestId);
|
|
|
|
|
expect(response[1].choices).to.deep.equal(test.expected.choices);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-11-11 06:45:38 +00:00
|
|
|
const badCasesFiles = fs.readdirSync(path.join(__dirname, "bad_cases")).map((file) => {
|
|
|
|
|
return {
|
|
|
|
|
path: path.join("bad_cases", file),
|
|
|
|
|
absolutePath: path.join(__dirname, "bad_cases", file),
|
|
|
|
|
};
|
|
|
|
|
});
|
2023-11-12 18:11:31 +00:00
|
|
|
badCasesFiles.forEach((goldenFile) => {
|
2023-11-11 06:45:38 +00:00
|
|
|
it(goldenFile.path, async () => {
|
|
|
|
|
const test = await createGoldenTest(goldenFile.absolutePath);
|
|
|
|
|
requestId++;
|
|
|
|
|
const request = [requestId, { func: "provideCompletions", args: [test.request] }];
|
|
|
|
|
agent.stdin.write(JSON.stringify(request) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
const response = output.shift();
|
|
|
|
|
expect(response[0]).to.equal(requestId);
|
|
|
|
|
expect(response[1].choices).not.to.deep.equal(test.expected.choices);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-11-12 18:11:31 +00:00
|
|
|
it("updateConfig experimental", async () => {
|
|
|
|
|
const expectedConfig = { ...config };
|
2023-11-13 04:53:20 +00:00
|
|
|
{
|
|
|
|
|
requestId++;
|
|
|
|
|
const updateConfigRequest = [
|
|
|
|
|
requestId,
|
|
|
|
|
{
|
|
|
|
|
func: "updateConfig",
|
|
|
|
|
args: ["postprocess.limitScope.experimentalSyntax", true],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
agent.stdin.write(JSON.stringify(updateConfigRequest) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
expectedConfig.postprocess.limitScope.experimentalSyntax = true;
|
|
|
|
|
expect(output.shift()).to.deep.equal([0, { event: "configUpdated", config: expectedConfig }]);
|
|
|
|
|
expect(output.shift()).to.deep.equal([requestId, true]);
|
|
|
|
|
}
|
|
|
|
|
{
|
|
|
|
|
requestId++;
|
|
|
|
|
const updateConfigRequest = [
|
|
|
|
|
requestId,
|
|
|
|
|
{
|
|
|
|
|
func: "updateConfig",
|
|
|
|
|
args: ["postprocess.calculateReplaceRange.experimentalSyntax", true],
|
|
|
|
|
},
|
|
|
|
|
];
|
|
|
|
|
agent.stdin.write(JSON.stringify(updateConfigRequest) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
expectedConfig.postprocess.calculateReplaceRange.experimentalSyntax = true;
|
|
|
|
|
expect(output.shift()).to.deep.equal([0, { event: "configUpdated", config: expectedConfig }]);
|
|
|
|
|
expect(output.shift()).to.deep.equal([requestId, true]);
|
|
|
|
|
}
|
2023-11-12 18:11:31 +00:00
|
|
|
});
|
|
|
|
|
badCasesFiles.forEach((goldenFile) => {
|
|
|
|
|
it("experimental: " + goldenFile.path, async () => {
|
|
|
|
|
const test = await createGoldenTest(goldenFile.absolutePath);
|
|
|
|
|
requestId++;
|
|
|
|
|
const request = [requestId, { func: "provideCompletions", args: [test.request] }];
|
|
|
|
|
agent.stdin.write(JSON.stringify(request) + "\n");
|
|
|
|
|
await waitForResponse(requestId);
|
|
|
|
|
const response = output.shift();
|
|
|
|
|
expect(response[0]).to.equal(requestId);
|
|
|
|
|
expect(response[1].choices).to.deep.equal(test.expected.choices);
|
|
|
|
|
});
|
|
|
|
|
});
|
|
|
|
|
|
2023-11-05 02:12:52 +00:00
|
|
|
after(() => {
|
|
|
|
|
agent.kill();
|
|
|
|
|
});
|
|
|
|
|
});
|