Add tests for creating model file
This commit is contained in:
@@ -176,7 +176,7 @@ async function pickNewModelFile(
|
||||
return undefined;
|
||||
}
|
||||
|
||||
const dataExtensionPatternsValue = qlpack.dataExtensions ?? [];
|
||||
const dataExtensionPatternsValue = qlpack.dataExtensions;
|
||||
if (
|
||||
!(
|
||||
Array.isArray(dataExtensionPatternsValue) ||
|
||||
@@ -199,16 +199,19 @@ async function pickNewModelFile(
|
||||
title: "Enter the name of the new model file",
|
||||
value: `models/${databaseItem.name.replaceAll("/", ".")}.model.yml`,
|
||||
validateInput: async (value: string): Promise<string | undefined> => {
|
||||
if (value === "") {
|
||||
return "File name must not be empty";
|
||||
}
|
||||
|
||||
const path = resolve(extensionPackPath, value);
|
||||
|
||||
if (await pathExists(path)) {
|
||||
return "File already exists";
|
||||
}
|
||||
|
||||
const notInExtensionPack = !relative(
|
||||
extensionPackPath,
|
||||
path,
|
||||
).startsWith("..");
|
||||
const notInExtensionPack = relative(extensionPackPath, path).startsWith(
|
||||
"..",
|
||||
);
|
||||
if (notInExtensionPack) {
|
||||
return "File must be in the extension pack";
|
||||
}
|
||||
|
||||
@@ -35,6 +35,9 @@ describe("pickExtensionPackModelFile", () => {
|
||||
const progress = jest.fn();
|
||||
let showQuickPickSpy: jest.SpiedFunction<typeof window.showQuickPick>;
|
||||
let showInputBoxSpy: jest.SpiedFunction<typeof window.showInputBox>;
|
||||
let showAndLogErrorMessageSpy: jest.SpiedFunction<
|
||||
typeof helpers.showAndLogErrorMessage
|
||||
>;
|
||||
|
||||
beforeEach(() => {
|
||||
showQuickPickSpy = jest
|
||||
@@ -43,6 +46,11 @@ describe("pickExtensionPackModelFile", () => {
|
||||
showInputBoxSpy = jest
|
||||
.spyOn(window, "showInputBox")
|
||||
.mockRejectedValue(new Error("Unexpected call to showInputBox"));
|
||||
showAndLogErrorMessageSpy = jest
|
||||
.spyOn(helpers, "showAndLogErrorMessage")
|
||||
.mockImplementation((msg) => {
|
||||
throw new Error(`Unexpected call to showAndLogErrorMessage: ${msg}`);
|
||||
});
|
||||
});
|
||||
|
||||
it("allows choosing an existing extension pack and model file", async () => {
|
||||
@@ -107,6 +115,108 @@ describe("pickExtensionPackModelFile", () => {
|
||||
);
|
||||
});
|
||||
|
||||
it("allows choosing an existing extension pack and creating a new model file", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
...qlPacks,
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{
|
||||
models: extensions.models,
|
||||
data: {
|
||||
[tmpDir.path]: [
|
||||
{
|
||||
file: join(tmpDir.path, "models/model.yml"),
|
||||
index: 0,
|
||||
predicate: "sinkModel",
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "create",
|
||||
file: null,
|
||||
} as QuickPickItem);
|
||||
showInputBoxSpy.mockResolvedValue("models/my-model.yml");
|
||||
|
||||
await outputFile(
|
||||
join(tmpDir.path, "codeql-pack.yml"),
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
}),
|
||||
);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(join(tmpDir.path, "models/my-model.yml"));
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(2);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||
[
|
||||
{
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
},
|
||||
{
|
||||
label: "another-extension-pack",
|
||||
extensionPack: "another-extension-pack",
|
||||
},
|
||||
],
|
||||
{
|
||||
title: expect.any(String),
|
||||
},
|
||||
token,
|
||||
);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledWith(
|
||||
[
|
||||
{
|
||||
label: "models/model.yml",
|
||||
file: join(tmpDir.path, "models/model.yml"),
|
||||
},
|
||||
{
|
||||
label: expect.stringMatching(/create/i),
|
||||
file: null,
|
||||
},
|
||||
],
|
||||
{
|
||||
title: expect.any(String),
|
||||
},
|
||||
token,
|
||||
);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledWith(
|
||||
{
|
||||
title: expect.any(String),
|
||||
value: "models/github.vscode-codeql.model.yml",
|
||||
validateInput: expect.any(Function),
|
||||
},
|
||||
token,
|
||||
);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalledTimes(1);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalledWith([], true);
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalledTimes(1);
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalledWith(tmpDir.path, []);
|
||||
});
|
||||
|
||||
it("allows cancelling the extension pack prompt", async () => {
|
||||
const cliServer = mockCliServer(qlPacks, extensions);
|
||||
|
||||
@@ -124,7 +234,7 @@ describe("pickExtensionPackModelFile", () => {
|
||||
expect(cliServer.resolveExtensions).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows create option when there are no extension packs", async () => {
|
||||
it("does not show any options when there are no extension packs", async () => {
|
||||
const cliServer = mockCliServer({}, { models: [], data: {} });
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
@@ -150,10 +260,7 @@ describe("pickExtensionPackModelFile", () => {
|
||||
});
|
||||
|
||||
it("shows an error when an extension pack resolves to more than 1 location", async () => {
|
||||
const showAndLogErrorMessageSpy = jest.spyOn(
|
||||
helpers,
|
||||
"showAndLogErrorMessage",
|
||||
);
|
||||
showAndLogErrorMessageSpy.mockResolvedValue(undefined);
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
@@ -261,6 +368,361 @@ describe("pickExtensionPackModelFile", () => {
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an error when there is no pack YAML file", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showAndLogErrorMessageSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/codeql-pack\.yml/),
|
||||
);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an error when the pack YAML file is invalid", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
await outputFile(join(tmpDir.path, "codeql-pack.yml"), dumpYaml("java"));
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showAndLogErrorMessageSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Could not parse/),
|
||||
);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an error when the pack YAML does not contain dataExtensions", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
await outputFile(
|
||||
join(tmpDir.path, "codeql-pack.yml"),
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showAndLogErrorMessageSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Expected 'dataExtensions' to be/),
|
||||
);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("shows an error when the pack YAML dataExtensions is invalid", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
await outputFile(
|
||||
join(tmpDir.path, "codeql-pack.yml"),
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: {
|
||||
"codeql/java-all": "invalid",
|
||||
},
|
||||
}),
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showAndLogErrorMessageSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).not.toHaveBeenCalled();
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showAndLogErrorMessageSpy).toHaveBeenCalledWith(
|
||||
expect.stringMatching(/Expected 'dataExtensions' to be/),
|
||||
);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("allows cancelling the new file input box", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
await outputFile(
|
||||
join(tmpDir.path, "codeql-pack.yml"),
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml"],
|
||||
}),
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showInputBoxSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
expect(showQuickPickSpy).toHaveBeenCalledTimes(1);
|
||||
expect(showInputBoxSpy).toHaveBeenCalledTimes(1);
|
||||
expect(cliServer.resolveQlpacks).toHaveBeenCalled();
|
||||
expect(cliServer.resolveExtensions).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("validates the file input", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
const qlpackPath = join(tmpDir.path, "codeql-pack.yml");
|
||||
await outputFile(
|
||||
qlpackPath,
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: ["models/**/*.yml", "data/**/*.yml"],
|
||||
}),
|
||||
);
|
||||
await outputFile(
|
||||
join(tmpDir.path, "models", "model.yml"),
|
||||
dumpYaml({
|
||||
extensions: [],
|
||||
}),
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showInputBoxSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
|
||||
const validateFile = showInputBoxSpy.mock.calls[0][0]?.validateInput;
|
||||
expect(validateFile).toBeDefined();
|
||||
if (!validateFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(await validateFile("")).toEqual("File name must not be empty");
|
||||
expect(await validateFile("models/model.yml")).toEqual(
|
||||
"File already exists",
|
||||
);
|
||||
expect(await validateFile("../model.yml")).toEqual(
|
||||
"File must be in the extension pack",
|
||||
);
|
||||
expect(await validateFile("/home/user/model.yml")).toEqual(
|
||||
"File must be in the extension pack",
|
||||
);
|
||||
expect(await validateFile("model.yml")).toEqual(
|
||||
`File must match one of the patterns in 'dataExtensions' in ${qlpackPath}`,
|
||||
);
|
||||
expect(await validateFile("models/model.yaml")).toEqual(
|
||||
`File must match one of the patterns in 'dataExtensions' in ${qlpackPath}`,
|
||||
);
|
||||
expect(await validateFile("models/my-model.yml")).toBeUndefined();
|
||||
expect(await validateFile("models/nested/model.yml")).toBeUndefined();
|
||||
expect(await validateFile("data/model.yml")).toBeUndefined();
|
||||
});
|
||||
|
||||
it("allows the dataExtensions to be a string", async () => {
|
||||
const tmpDir = await dir({
|
||||
unsafeCleanup: true,
|
||||
});
|
||||
|
||||
const cliServer = mockCliServer(
|
||||
{
|
||||
"my-extension-pack": [tmpDir.path],
|
||||
},
|
||||
{ models: [], data: {} },
|
||||
);
|
||||
|
||||
const qlpackPath = join(tmpDir.path, "codeql-pack.yml");
|
||||
await outputFile(
|
||||
qlpackPath,
|
||||
dumpYaml({
|
||||
name: "my-extension-pack",
|
||||
version: "0.0.0",
|
||||
library: true,
|
||||
extensionTargets: {
|
||||
"codeql/java-all": "*",
|
||||
},
|
||||
dataExtensions: "models/**/*.yml",
|
||||
}),
|
||||
);
|
||||
await outputFile(
|
||||
join(tmpDir.path, "models", "model.yml"),
|
||||
dumpYaml({
|
||||
extensions: [],
|
||||
}),
|
||||
);
|
||||
|
||||
showQuickPickSpy.mockResolvedValueOnce({
|
||||
label: "my-extension-pack",
|
||||
extensionPack: "my-extension-pack",
|
||||
} as QuickPickItem);
|
||||
showQuickPickSpy.mockResolvedValueOnce(undefined);
|
||||
showInputBoxSpy.mockResolvedValue(undefined);
|
||||
|
||||
expect(
|
||||
await pickExtensionPackModelFile(
|
||||
cliServer,
|
||||
databaseItem,
|
||||
progress,
|
||||
token,
|
||||
),
|
||||
).toEqual(undefined);
|
||||
|
||||
const validateFile = showInputBoxSpy.mock.calls[0][0]?.validateInput;
|
||||
expect(validateFile).toBeDefined();
|
||||
if (!validateFile) {
|
||||
return;
|
||||
}
|
||||
|
||||
expect(await validateFile("models/my-model.yml")).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
function mockCliServer(
|
||||
|
||||
Reference in New Issue
Block a user