Files
languageservices/languageservice/src/validate.test.ts
T
2023-03-20 13:33:58 -04:00

299 lines
7.0 KiB
TypeScript

import {Diagnostic, DiagnosticSeverity} from "vscode-languageserver-types";
import {createDocument} from "./test-utils/document";
import {validate} from "./validate";
import {defaultValueProviders} from "./value-providers/default";
import {clearCache} from "./utils/workflow-cache";
import {ValueProviderConfig, ValueProviderKind} from "./value-providers/config";
beforeEach(() => {
clearCache();
});
describe("validation", () => {
it("valid workflow", async () => {
const result = await validate(createDocument("wf.yaml", "on: push\njobs:\n build:\n runs-on: ubuntu-latest"));
expect(result.length).toBe(0);
});
it("missing jobs key", async () => {
const result = await validate(createDocument("wf.yaml", "on: push"));
expect(result.length).toBe(1);
expect(result[0]).toEqual({
message: "Required property is missing: jobs",
range: {
start: {
line: 0,
character: 0
},
end: {
line: 0,
character: 8
}
}
} as Diagnostic);
});
it("extraneous key", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on: push
unknown-key: foo
jobs:
build:
runs-on: ubuntu-latest
steps:
- run: echo`
)
);
expect(result.length).toBe(1);
expect(result[0]).toEqual({
message: "Unexpected value 'unknown-key'",
range: {
end: {
character: 11,
line: 1
},
start: {
character: 0,
line: 1
}
}
} as Diagnostic);
});
it("single value not returned by suggested value provider", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on: push
jobs:
build:
runs-on: does-not-exist
steps:
- run: echo`
),
{valueProviderConfig: defaultValueProviders}
);
expect(result.length).toBe(0);
});
it("value in sequence not returned by value provider", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on: push
jobs:
build:
runs-on:
- ubuntu-latest
- does-not-exist
steps:
- run: echo`
),
{valueProviderConfig: defaultValueProviders}
);
expect(result.length).toBe(0);
});
it("single value not returned by allowed value provider", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on: push
jobs:
test:
runs-on: ubuntu-latest
steps:
- run: echo
build:
runs-on: ubuntu-latest
needs: test2
steps:
- run: echo`
),
{valueProviderConfig: defaultValueProviders}
);
expect(result[0]).toEqual({
message: "Value 'test2' is not valid",
severity: DiagnosticSeverity.Error,
range: {
end: {
character: 16,
line: 8
},
start: {
character: 11,
line: 8
}
}
} as Diagnostic);
});
it("unknown event type", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on: [push, check_run, pr]
jobs:
build:
runs-on:
- ubuntu-latest`
),
{valueProviderConfig: defaultValueProviders}
);
expect(result.length).toBe(1);
expect(result[0]).toEqual({
message: "Unexpected value 'pr'",
range: {
end: {
character: 24,
line: 0
},
start: {
character: 22,
line: 0
}
}
} as Diagnostic);
});
it("invalid cron string", async () => {
const result = await validate(
createDocument(
"wf.yaml",
`on:
schedule:
- cron: '0 0 * *'
jobs:
build:
runs-on: ubuntu-latest`
),
{valueProviderConfig: defaultValueProviders}
);
expect(result.length).toBe(1);
expect(result[0]).toEqual({
message: "Invalid cron string",
range: {
end: {
character: 21,
line: 2
},
start: {
character: 12,
line: 2
}
}
} as Diagnostic);
});
it("invalid YAML", async () => {
// This YAML has some mismatched single-quotes, which causes the string to be terminated early
// within the fromJSON() expression.
// Using double-quotes would make it valid:
// "Run a \${{ inputs.test }} one-line script \${{ fromJSON('test') == inputs.name }}"
const workflow = `
on: push
jobs:
build:
runs-on: ubuntu-latest
environment: TEST
steps:
- name: 'Run a \${{ inputs.test }} one-line script \${{ fromJSON('test') == inputs.name }}'
run: echo
`;
const result = await validate(createDocument("wf.yaml", workflow), {valueProviderConfig: defaultValueProviders});
expect(result).toEqual([
{
message:
"Unexpected scalar at node end at line 8, column 73:\n\n…un a ${{ inputs.test }} one-line script ${{ fromJSON('test') == inputs.name }}'\n ^^^^^^^^^^^^^^^^^^^^^^^^^\n",
range: {
start: {
line: 7,
character: 72
},
end: {
line: 7,
character: 97
}
}
}
]);
const cachedResult = await validate(createDocument("wf.yaml", workflow), {
valueProviderConfig: defaultValueProviders
});
expect(cachedResult).toEqual(result);
});
describe("value provider case sensitivity", () => {
it("value with a different case and case sensitive provider", async () => {
const workflow = `
on: push
jobs:
build:
runs-on: ubuntu-latest
environment: TEST
steps:
- run: echo
`;
const valueProviderConfig: ValueProviderConfig = {
"job-environment": {
kind: ValueProviderKind.AllowedValues,
get: () => Promise.resolve([{label: "test"}]),
caseInsensitive: false
}
};
const result = await validate(createDocument("wf.yaml", workflow), {valueProviderConfig});
expect(result).toEqual([
{
message: "Value 'TEST' is not valid",
severity: DiagnosticSeverity.Error,
range: {
start: {
line: 5,
character: 19
},
end: {
line: 5,
character: 23
}
}
}
]);
});
it("value with a different case and case insensitive provider", async () => {
const workflow = `
on: push
jobs:
build:
runs-on: ubuntu-latest
environment: TEST
steps:
- run: echo
`;
const valueProviderConfig: ValueProviderConfig = {
"job-environment": {
kind: ValueProviderKind.AllowedValues,
get: () => Promise.resolve([{label: "test"}]),
caseInsensitive: true
}
};
const result = await validate(createDocument("wf.yaml", workflow), {valueProviderConfig});
expect(result).toEqual([]);
});
});
});