160 lines
6.8 KiB
TypeScript
160 lines
6.8 KiB
TypeScript
import {data, DescriptionDictionary, isDescriptionDictionary} from "@actions/expressions";
|
|
import {getStepsContext as getDefaultStepsContext} from "@actions/languageservice/context-providers/steps";
|
|
import {Octokit} from "@octokit/rest";
|
|
import fetchMock from "fetch-mock";
|
|
|
|
import {createWorkflowContext} from "../test-utils/workflow-context.js";
|
|
import {TTLCache} from "../utils/cache.js";
|
|
import {getStepsContext} from "./steps.js";
|
|
|
|
const workflow = `
|
|
name: Caching Primes
|
|
|
|
on: push
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
|
|
steps:
|
|
- uses: actions/checkout@v3
|
|
|
|
- name: Cache Primes
|
|
id: cache-primes
|
|
uses: actions/cache@v3
|
|
with:
|
|
path: prime-numbers
|
|
key: \${{ runner.os }}-primes
|
|
|
|
- name: Generate Prime Numbers
|
|
if: steps.cache-primes.outputs.cache-hit != 'true'
|
|
run: /generate-primes.sh -d prime-numbers
|
|
|
|
- name: Use Prime Numbers
|
|
run: /primes.sh -d prime-numbers
|
|
`;
|
|
|
|
// https://api.github.com/repos/actions/cache/contents/action.yml?ref=v3
|
|
const actionMetadata = {
|
|
name: "action.yml",
|
|
path: "action.yml",
|
|
sha: "3e158e3ee33f7e597ccf45c369a6e830dc8d43e3",
|
|
size: 946,
|
|
url: "https://api.github.com/repos/actions/cache/contents/action.yml?ref=v3",
|
|
html_url: "https://github.com/actions/cache/blob/v3/action.yml",
|
|
git_url: "https://api.github.com/repos/actions/cache/git/blobs/3e158e3ee33f7e597ccf45c369a6e830dc8d43e3",
|
|
download_url: "https://raw.githubusercontent.com/actions/cache/v3/action.yml",
|
|
type: "file",
|
|
content:
|
|
"bmFtZTogJ0NhY2hlJwpkZXNjcmlwdGlvbjogJ0NhY2hlIGFydGlmYWN0cyBs\naWtlIGRlcGVuZGVuY2llcyBhbmQgYnVpbGQgb3V0cHV0cyB0byBpbXByb3Zl\nIHdvcmtmbG93IGV4ZWN1dGlvbiB0aW1lJwphdXRob3I6ICdHaXRIdWInCmlu\ncHV0czoKICBwYXRoOgogICAgZGVzY3JpcHRpb246ICdBIGxpc3Qgb2YgZmls\nZXMsIGRpcmVjdG9yaWVzLCBhbmQgd2lsZGNhcmQgcGF0dGVybnMgdG8gY2Fj\naGUgYW5kIHJlc3RvcmUnCiAgICByZXF1aXJlZDogdHJ1ZQogIGtleToKICAg\nIGRlc2NyaXB0aW9uOiAnQW4gZXhwbGljaXQga2V5IGZvciByZXN0b3Jpbmcg\nYW5kIHNhdmluZyB0aGUgY2FjaGUnCiAgICByZXF1aXJlZDogdHJ1ZQogIHJl\nc3RvcmUta2V5czoKICAgIGRlc2NyaXB0aW9uOiAnQW4gb3JkZXJlZCBsaXN0\nIG9mIGtleXMgdG8gdXNlIGZvciByZXN0b3Jpbmcgc3RhbGUgY2FjaGUgaWYg\nbm8gY2FjaGUgaGl0IG9jY3VycmVkIGZvciBrZXkuIE5vdGUgYGNhY2hlLWhp\ndGAgcmV0dXJucyBmYWxzZSBpbiB0aGlzIGNhc2UuJwogICAgcmVxdWlyZWQ6\nIGZhbHNlCiAgdXBsb2FkLWNodW5rLXNpemU6CiAgICBkZXNjcmlwdGlvbjog\nJ1RoZSBjaHVuayBzaXplIHVzZWQgdG8gc3BsaXQgdXAgbGFyZ2UgZmlsZXMg\nZHVyaW5nIHVwbG9hZCwgaW4gYnl0ZXMnCiAgICByZXF1aXJlZDogZmFsc2UK\nb3V0cHV0czoKICBjYWNoZS1oaXQ6CiAgICBkZXNjcmlwdGlvbjogJ0EgYm9v\nbGVhbiB2YWx1ZSB0byBpbmRpY2F0ZSBhbiBleGFjdCBtYXRjaCB3YXMgZm91\nbmQgZm9yIHRoZSBwcmltYXJ5IGtleScKcnVuczoKICB1c2luZzogJ25vZGUx\nNicKICBtYWluOiAnZGlzdC9yZXN0b3JlL2luZGV4LmpzJwogIHBvc3Q6ICdk\naXN0L3NhdmUvaW5kZXguanMnCiAgcG9zdC1pZjogJ3N1Y2Nlc3MoKScKYnJh\nbmRpbmc6CiAgaWNvbjogJ2FyY2hpdmUnCiAgY29sb3I6ICdncmF5LWRhcmsn\nCg==\n",
|
|
encoding: "base64",
|
|
_links: {
|
|
self: "https://api.github.com/repos/actions/cache/contents/action.yml?ref=v3",
|
|
git: "https://api.github.com/repos/actions/cache/git/blobs/3e158e3ee33f7e597ccf45c369a6e830dc8d43e3",
|
|
html: "https://github.com/actions/cache/blob/v3/action.yml"
|
|
}
|
|
};
|
|
|
|
it("returns default context when job is undefined", async () => {
|
|
const workflowContext = await createWorkflowContext(workflow, undefined);
|
|
const defaultContext = getDefaultStepsContext(workflowContext);
|
|
|
|
const stepsContext = await getStepsContext(new Octokit(), new TTLCache(), defaultContext, workflowContext);
|
|
expect(stepsContext).toEqual(defaultContext);
|
|
});
|
|
|
|
it("outputs is an incomplete dictionary to allow dynamic outputs", async () => {
|
|
const mock = fetchMock
|
|
.sandbox()
|
|
.getOnce("https://api.github.com/repos/actions/cache/contents/action.yml?ref=v3", actionMetadata);
|
|
|
|
const workflowContext = await createWorkflowContext(workflow, "build");
|
|
const defaultContext = getDefaultStepsContext(workflowContext);
|
|
|
|
const stepsContext = await getStepsContext(
|
|
new Octokit({
|
|
request: {
|
|
fetch: mock
|
|
}
|
|
}),
|
|
new TTLCache(),
|
|
defaultContext,
|
|
workflowContext
|
|
);
|
|
|
|
// Get the step context
|
|
const stepContext = stepsContext?.get("cache-primes");
|
|
if (!stepContext) {
|
|
throw new Error("Expected stepContext to be defined");
|
|
}
|
|
expect(isDescriptionDictionary(stepContext)).toBe(true);
|
|
|
|
// Get the outputs - should be a dictionary, not null
|
|
const outputs = (stepContext as DescriptionDictionary).get("outputs");
|
|
if (!outputs) {
|
|
throw new Error("Expected outputs to be defined");
|
|
}
|
|
expect(isDescriptionDictionary(outputs)).toBe(true);
|
|
|
|
// Outputs should be marked incomplete to allow dynamic outputs
|
|
const outputsDict = outputs as DescriptionDictionary;
|
|
expect(outputsDict.complete).toBe(false);
|
|
|
|
// Known outputs from action.yml should be present
|
|
expect(outputsDict.get("cache-hit")).toBeDefined();
|
|
});
|
|
|
|
it("adds action outputs", async () => {
|
|
const mock = fetchMock
|
|
.sandbox()
|
|
.getOnce("https://api.github.com/repos/actions/cache/contents/action.yml?ref=v3", actionMetadata);
|
|
|
|
const workflowContext = await createWorkflowContext(workflow, "build");
|
|
const defaultContext = getDefaultStepsContext(workflowContext);
|
|
|
|
const stepsContext = await getStepsContext(
|
|
new Octokit({
|
|
request: {
|
|
fetch: mock
|
|
}
|
|
}),
|
|
new TTLCache(),
|
|
defaultContext,
|
|
workflowContext
|
|
);
|
|
expect(stepsContext).toBeDefined();
|
|
|
|
// Create expected outputs dict with complete = false
|
|
// (actions can have dynamic outputs beyond what's declared in action.yml)
|
|
const expectedOutputs = new DescriptionDictionary({
|
|
key: "cache-hit",
|
|
value: new data.StringData("A boolean value to indicate an exact match was found for the primary key"),
|
|
description: "A boolean value to indicate an exact match was found for the primary key"
|
|
});
|
|
expectedOutputs.complete = false;
|
|
|
|
expect(stepsContext).toEqual(
|
|
new DescriptionDictionary({
|
|
key: "cache-primes",
|
|
value: new DescriptionDictionary(
|
|
{
|
|
key: "outputs",
|
|
value: expectedOutputs
|
|
},
|
|
{
|
|
key: "conclusion",
|
|
value: new data.Null(),
|
|
description:
|
|
"The result of a completed step after [`continue-on-error`](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error) is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
|
},
|
|
{
|
|
key: "outcome",
|
|
value: new data.Null(),
|
|
description:
|
|
"The result of a completed step before [`continue-on-error`](https://docs.github.com/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepscontinue-on-error) is applied. Possible values are `success`, `failure`, `cancelled`, or `skipped`. When a `continue-on-error` step fails, the `outcome` is `failure`, but the final conclusion is `success`."
|
|
}
|
|
)
|
|
})
|
|
);
|
|
});
|