mirror of
https://github.com/github/codeql-action.git
synced 2026-10-03 17:41:28 +00:00
495 lines
14 KiB
TypeScript
495 lines
14 KiB
TypeScript
import test from "ava";
|
|
import * as sinon from "sinon";
|
|
import * as uuid from "uuid";
|
|
|
|
import * as actionsUtil from "./actions-util";
|
|
import { Config } from "./config-utils";
|
|
import { EnvVar, RegistryProxyVars } from "./environment";
|
|
import { BuiltInLanguage } from "./languages";
|
|
import { getRunnerLogger } from "./logging";
|
|
import { ToolsSource } from "./setup-codeql";
|
|
import type { Registry } from "./start-proxy";
|
|
import {
|
|
ActionName,
|
|
createInitWithConfigStatusReport,
|
|
createStatusReportBase,
|
|
getActionsStatus,
|
|
getRegistryTypesFromEnv,
|
|
getJobUUID,
|
|
InitStatusReport,
|
|
InitWithConfigStatusReport,
|
|
} from "./status-report";
|
|
import {
|
|
setupTests,
|
|
setupActionsVars,
|
|
createTestConfig,
|
|
makeMacro,
|
|
getTestEnv,
|
|
RecordingLogger,
|
|
callee,
|
|
} from "./testing-utils";
|
|
import { BuildMode, ConfigurationError, withTmpDir, wrapError } from "./util";
|
|
|
|
setupTests(test);
|
|
|
|
test("getRegistryTypesFromEnv - gets unique registry types from environment", async (t) => {
|
|
const logger = new RecordingLogger(true);
|
|
const env = getTestEnv({
|
|
[RegistryProxyVars.PROXY_URLS]: JSON.stringify([
|
|
{ type: "git_source", url: "https://example.com" },
|
|
{ type: "git_source", url: "https://github.com" },
|
|
{ type: "docker_registry", url: "https://registry.example.com" },
|
|
] satisfies Array<Partial<Registry>>),
|
|
});
|
|
|
|
const result = getRegistryTypesFromEnv(logger, env);
|
|
t.deepEqual(result, ["git_source", "docker_registry"].sort().join(","));
|
|
});
|
|
|
|
test("getRegistryTypesFromEnv - returns undefined if the env var is not set", async (t) => {
|
|
const logger = new RecordingLogger(true);
|
|
const env = getTestEnv({});
|
|
|
|
const result = getRegistryTypesFromEnv(logger, env);
|
|
t.is(result, undefined);
|
|
});
|
|
|
|
test("getRegistryTypesFromEnv - returns undefined if the env var is not valid JSON", async (t) => {
|
|
const logger = new RecordingLogger(true);
|
|
const env = getTestEnv({ [RegistryProxyVars.PROXY_URLS]: "[" });
|
|
|
|
const result = getRegistryTypesFromEnv(logger, env);
|
|
t.is(result, undefined);
|
|
});
|
|
|
|
test("getRegistryTypesFromEnv - returns undefined if the env var is unexpected JSON", async (t) => {
|
|
const logger = new RecordingLogger(true);
|
|
|
|
t.is(
|
|
getRegistryTypesFromEnv(
|
|
logger,
|
|
getTestEnv({
|
|
// Top-level object rather than an array of objects.
|
|
[RegistryProxyVars.PROXY_URLS]: JSON.stringify({ type: "git_source" }),
|
|
}),
|
|
),
|
|
undefined,
|
|
);
|
|
t.is(
|
|
getRegistryTypesFromEnv(
|
|
logger,
|
|
getTestEnv({
|
|
// Object has no "type" key.
|
|
[RegistryProxyVars.PROXY_URLS]: JSON.stringify([{}]),
|
|
}),
|
|
),
|
|
undefined,
|
|
);
|
|
});
|
|
|
|
test("getJobUUID - generates valid UUIDs", async (t) => {
|
|
await callee(getJobUUID)
|
|
.withArgs()
|
|
.logs(t, "Job run UUID is ")
|
|
.hasEnv(t, (val) => {
|
|
return {
|
|
[EnvVar.JOB_RUN_UUID]: val,
|
|
};
|
|
})
|
|
.passes((val) => {
|
|
t.true(uuid.validate(val));
|
|
});
|
|
});
|
|
|
|
test("getJobUUID - retrieves existing job UUIDs", async (t) => {
|
|
const existingJobUuid = uuid.v4();
|
|
await callee(getJobUUID)
|
|
.withArgs()
|
|
.withEnv((env) => {
|
|
env.set(EnvVar.JOB_RUN_UUID, existingJobUuid);
|
|
})
|
|
.logs(t, `Existing job run UUID is ${existingJobUuid}.`)
|
|
.passes(t.deepEqual, existingJobUuid);
|
|
});
|
|
|
|
test("getJobUUID - doesn't retrieve invalid UUIDs", async (t) => {
|
|
const existingJobUuid = "not-a-uuid";
|
|
await callee(getJobUUID)
|
|
.withArgs()
|
|
.withEnv((env) => {
|
|
env.set(EnvVar.JOB_RUN_UUID, existingJobUuid);
|
|
})
|
|
.logs(t, `Job run UUID is `)
|
|
.notLogs(t, `Existing job run UUID is ${existingJobUuid}.`)
|
|
.passes(t.notDeepEqual, existingJobUuid);
|
|
});
|
|
|
|
function setupEnvironmentAndStub(tmpDir: string) {
|
|
setupActionsVars(tmpDir, tmpDir, {
|
|
GITHUB_EVENT_NAME: "dynamic",
|
|
GITHUB_RUN_ATTEMPT: "2",
|
|
GITHUB_RUN_ID: "100",
|
|
});
|
|
|
|
process.env[EnvVar.ANALYSIS_KEY] = "analysis-key";
|
|
process.env["ImageVersion"] = "2023.05.19.1";
|
|
process.env[RegistryProxyVars.PROXY_URLS] = JSON.stringify([
|
|
{ type: "maven_repository" },
|
|
] satisfies Array<Partial<Registry>>);
|
|
|
|
const getRequiredInput = sinon.stub(actionsUtil, "getRequiredInput");
|
|
getRequiredInput.withArgs("matrix").resolves("input/matrix");
|
|
}
|
|
|
|
test.serial("createStatusReportBase", async (t) => {
|
|
await withTmpDir(async (tmpDir: string) => {
|
|
setupEnvironmentAndStub(tmpDir);
|
|
|
|
const statusReport = await createStatusReportBase(
|
|
ActionName.Init,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({
|
|
buildMode: BuildMode.None,
|
|
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
|
|
}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
);
|
|
t.truthy(statusReport);
|
|
|
|
if (statusReport !== undefined) {
|
|
t.is(statusReport.action_name, ActionName.Init);
|
|
t.is(statusReport.action_oid, "unknown");
|
|
t.is(typeof statusReport.action_version, "string");
|
|
t.is(
|
|
statusReport.action_started_at,
|
|
new Date("May 19, 2023 05:19:00").toISOString(),
|
|
);
|
|
t.is(statusReport.actions_event_name, "dynamic");
|
|
t.is(statusReport.analysis_key, "analysis-key");
|
|
t.is(statusReport.build_mode, BuildMode.None);
|
|
t.is(statusReport.cause, "failure cause");
|
|
t.is(statusReport.commit_oid, process.env["GITHUB_SHA"]!);
|
|
t.deepEqual(statusReport.computed_inputs, {});
|
|
t.is(statusReport.exception, "exception stack trace");
|
|
t.is(statusReport.job_name, process.env["GITHUB_JOB"] || "");
|
|
t.is(typeof statusReport.job_run_uuid, "string");
|
|
t.is(statusReport.languages, "java,swift");
|
|
t.is(statusReport.ref, process.env["GITHUB_REF"]!);
|
|
t.is(statusReport.registry_types, "maven_repository");
|
|
t.is(statusReport.runner_available_disk_space_bytes, 100);
|
|
t.is(statusReport.runner_image_version, process.env["ImageVersion"]);
|
|
t.is(statusReport.runner_os, process.env["RUNNER_OS"]!);
|
|
t.is(statusReport.started_at, process.env[EnvVar.WORKFLOW_STARTED_AT]!);
|
|
t.is(statusReport.status, "failure");
|
|
t.is(statusReport.steady_state_default_setup, false);
|
|
t.is(statusReport.workflow_name, process.env["GITHUB_WORKFLOW"] || "");
|
|
t.is(statusReport.workflow_run_attempt, 2);
|
|
t.is(statusReport.workflow_run_id, 100);
|
|
}
|
|
});
|
|
});
|
|
|
|
test.serial("createStatusReportBase - empty configuration", async (t) => {
|
|
await withTmpDir(async (tmpDir: string) => {
|
|
setupEnvironmentAndStub(tmpDir);
|
|
|
|
const statusReport = await createStatusReportBase(
|
|
ActionName.StartProxy,
|
|
"success",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
{},
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
);
|
|
|
|
if (t.truthy(statusReport)) {
|
|
t.is(statusReport.action_name, ActionName.StartProxy);
|
|
t.is(statusReport.status, "success");
|
|
}
|
|
});
|
|
});
|
|
|
|
test.serial("createStatusReportBase - partial configuration", async (t) => {
|
|
await withTmpDir(async (tmpDir: string) => {
|
|
setupEnvironmentAndStub(tmpDir);
|
|
|
|
const statusReport = await createStatusReportBase(
|
|
ActionName.StartProxy,
|
|
"success",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
{
|
|
languages: ["go"],
|
|
},
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
);
|
|
|
|
if (t.truthy(statusReport)) {
|
|
t.is(statusReport.action_name, ActionName.StartProxy);
|
|
t.is(statusReport.status, "success");
|
|
t.is(statusReport.languages, "go");
|
|
}
|
|
});
|
|
});
|
|
|
|
test.serial("createStatusReportBase_firstParty", async (t) => {
|
|
await withTmpDir(async (tmpDir: string) => {
|
|
setupEnvironmentAndStub(tmpDir);
|
|
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.UploadSarif,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
false,
|
|
);
|
|
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.Autobuild,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
true,
|
|
);
|
|
|
|
process.env["CODEQL_ACTION_INIT_HAS_RUN"] = "foobar";
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.UploadSarif,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
false,
|
|
);
|
|
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.Init,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
true,
|
|
);
|
|
|
|
process.env["CODEQL_ACTION_INIT_HAS_RUN"] = "true";
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.UploadSarif,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
true,
|
|
);
|
|
|
|
t.is(
|
|
(
|
|
await createStatusReportBase(
|
|
ActionName.Analyze,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
createTestConfig({}),
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
)
|
|
)?.first_party_analysis,
|
|
true,
|
|
);
|
|
});
|
|
});
|
|
|
|
test.serial(
|
|
"getActionStatus handling correctly various types of errors",
|
|
(t) => {
|
|
t.is(
|
|
getActionsStatus(new Error("arbitrary error")),
|
|
"failure",
|
|
"We categorise an arbitrary error as a failure",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(new ConfigurationError("arbitrary error")),
|
|
"user-error",
|
|
"We categorise a ConfigurationError as a user error",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(new Error("exit code 1"), "multiple things went wrong"),
|
|
"failure",
|
|
"getActionsStatus should return failure if passed an arbitrary error and an additional failure cause",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(
|
|
new ConfigurationError("exit code 1"),
|
|
"multiple things went wrong",
|
|
),
|
|
"user-error",
|
|
"getActionsStatus should return user-error if passed a configuration error and an additional failure cause",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(),
|
|
"success",
|
|
"getActionsStatus should return success if no error is passed",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(new Object()),
|
|
"failure",
|
|
"getActionsStatus should return failure if passed an arbitrary object",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(null, "an error occurred"),
|
|
"failure",
|
|
"getActionsStatus should return failure if passed null and an additional failure cause",
|
|
);
|
|
|
|
t.is(
|
|
getActionsStatus(wrapError(new ConfigurationError("arbitrary error"))),
|
|
"user-error",
|
|
"We still recognise a wrapped ConfigurationError as a user error",
|
|
);
|
|
},
|
|
);
|
|
|
|
const testCreateInitWithConfigStatusReport = makeMacro({
|
|
exec: async (
|
|
t,
|
|
config: Config,
|
|
expectedReportProperties: Partial<InitWithConfigStatusReport>,
|
|
) => {
|
|
await withTmpDir(async (tmpDir: string) => {
|
|
setupEnvironmentAndStub(tmpDir);
|
|
|
|
const statusReportBase = await createStatusReportBase(
|
|
ActionName.Init,
|
|
"failure",
|
|
new Date("May 19, 2023 05:19:00"),
|
|
config,
|
|
{ numAvailableBytes: 100, numTotalBytes: 500 },
|
|
getRunnerLogger(false),
|
|
"failure cause",
|
|
"exception stack trace",
|
|
);
|
|
|
|
if (t.truthy(statusReportBase)) {
|
|
const initStatusReport: InitStatusReport = {
|
|
...statusReportBase,
|
|
tools_input: "",
|
|
tools_resolved_version: "foo",
|
|
tools_source: ToolsSource.Unknown,
|
|
workflow_languages: "actions",
|
|
};
|
|
|
|
const initWithConfigStatusReport =
|
|
await createInitWithConfigStatusReport(
|
|
config,
|
|
initStatusReport,
|
|
undefined,
|
|
1024,
|
|
undefined,
|
|
undefined,
|
|
);
|
|
|
|
if (t.truthy(initWithConfigStatusReport)) {
|
|
t.like(initWithConfigStatusReport, expectedReportProperties);
|
|
}
|
|
}
|
|
});
|
|
},
|
|
title: (title) => `createInitWithConfigStatusReport: ${title}`,
|
|
});
|
|
|
|
testCreateInitWithConfigStatusReport.serial(
|
|
"returns a value",
|
|
createTestConfig({
|
|
buildMode: BuildMode.None,
|
|
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
|
|
}),
|
|
{
|
|
trap_cache_download_size_bytes: 1024,
|
|
registries: "[]",
|
|
query_filters: "[]",
|
|
packs: "{}",
|
|
},
|
|
);
|
|
|
|
testCreateInitWithConfigStatusReport.serial(
|
|
"includes packs for a single language",
|
|
createTestConfig({
|
|
buildMode: BuildMode.None,
|
|
languages: [BuiltInLanguage.java],
|
|
computedConfig: {
|
|
packs: ["foo", "bar"],
|
|
},
|
|
}),
|
|
{
|
|
registries: "[]",
|
|
query_filters: "[]",
|
|
packs: JSON.stringify({ java: ["foo", "bar"] }),
|
|
},
|
|
);
|
|
|
|
testCreateInitWithConfigStatusReport.serial(
|
|
"includes packs for multiple languages",
|
|
createTestConfig({
|
|
buildMode: BuildMode.None,
|
|
languages: [BuiltInLanguage.java, BuiltInLanguage.swift],
|
|
computedConfig: {
|
|
packs: { java: ["java-foo", "java-bar"], swift: ["swift-bar"] },
|
|
},
|
|
}),
|
|
{
|
|
registries: "[]",
|
|
query_filters: "[]",
|
|
packs: JSON.stringify({
|
|
java: ["java-foo", "java-bar"],
|
|
swift: ["swift-bar"],
|
|
}),
|
|
},
|
|
);
|