Update determineUserConfig tests to use new framework

This commit is contained in:
Michael B. Gale
2026-07-13 09:45:02 +01:00
parent 2773684c29
commit f181d70282

View File

@@ -13,7 +13,6 @@ import { CachingKind } from "./caching-utils";
import { createStubCodeQL } from "./codeql"; import { createStubCodeQL } from "./codeql";
import { UserConfig } from "./config/db-config"; import { UserConfig } from "./config/db-config";
import * as configUtils from "./config-utils"; import * as configUtils from "./config-utils";
import { ActionsEnvVars } from "./environment";
import * as errorMessages from "./error-messages"; import * as errorMessages from "./error-messages";
import { Feature } from "./feature-flags"; import { Feature } from "./feature-flags";
import { RepositoryProperties } from "./feature-flags/properties"; import { RepositoryProperties } from "./feature-flags/properties";
@@ -37,8 +36,6 @@ import {
mockCodeQLVersion, mockCodeQLVersion,
createTestConfig, createTestConfig,
makeMacro, makeMacro,
RecordingLogger,
DEFAULT_ACTIONS_VARS,
initAllState, initAllState,
callee, callee,
} from "./testing-utils"; } from "./testing-utils";
@@ -2301,45 +2298,40 @@ test("determineUserConfig - empty config when neither input is specified", async
test("determineUserConfig - loads config file", async (t) => { test("determineUserConfig - loads config file", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir);
const logger = new RecordingLogger();
const env = util.getEnv(DEFAULT_ACTIONS_VARS);
const inputs = createTestInitConfigInputs({ const inputs = createTestInitConfigInputs({
configInput: undefined, configInput: undefined,
configFile: configFilePath, configFile: configFilePath,
workspacePath: tmpDir, workspacePath: tmpDir,
}); });
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ logger, env }), .withDefaultActionsEnv()
tmpDir, .withArgs(tmpDir, inputs);
inputs,
); await target
// The path of the input config file should have been logged,
.logs(t, `Using configuration file: ${configFilePath}`)
.notLogs(
t,
// The other two origin messages and the warning about both inputs should
// not have been logged.
"No configuration file was provided",
"Using config from action input:",
"Both a config file and config input were provided. Ignoring config file.",
)
// The loaded configuration should match `simpleConfigFileContents`.
.passes(t.deepEqual, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The loaded configuration should match `simpleConfigFileContents`.
t.deepEqual(result, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The `configFile` input should not have changed. // The `configFile` input should not have changed.
t.is(inputs.configFile, configFilePath); t.is(inputs.configFile, configFilePath);
// And the path of the input config file should have been logged, while the
// other two origin messages should not have been logged.
t.true(logger.hasMessage(`Using configuration file: ${configFilePath}`));
t.false(logger.hasMessage("No configuration file was provided"));
t.false(logger.hasMessage("Using config from action input:"));
// But the warning about both inputs should not have been logged.
t.false(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.",
),
);
}); });
}); });
test("determineUserConfig - loads config input", async (t) => { test("determineUserConfig - loads config input", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const logger = new RecordingLogger();
const env = util.getEnv(DEFAULT_ACTIONS_VARS);
const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir);
const inputs = createTestInitConfigInputs({ const inputs = createTestInitConfigInputs({
@@ -2347,39 +2339,37 @@ test("determineUserConfig - loads config input", async (t) => {
configFile: undefined, configFile: undefined,
workspacePath: tmpDir, workspacePath: tmpDir,
}); });
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ logger, env }), .withDefaultActionsEnv()
tmpDir, .withArgs(tmpDir, inputs);
inputs,
); await target
// The input source and path of the generated config file should have been logged.
.logs(
t,
"Using config from action input:",
`Using configuration file: ${expectedConfigPath}`,
)
// The message about no configuration input and
// the warning about both inputs should not have been logged.
.notLogs(
t,
"No configuration file was provided",
"Both a config file and config input were provided. Ignoring config file.",
)
// The loaded configuration should match `simpleConfigFileContents`.
.passes(t.deepEqual, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The loaded configuration should match `simpleConfigFileContents`.
t.deepEqual(result, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The `configFile` input should have been mutated to the generated path. // The `configFile` input should have been mutated to the generated path.
t.is(inputs.configFile, expectedConfigPath); t.is(inputs.configFile, expectedConfigPath);
// And the input source and path of the generated config file should have been logged,
// while the message about no configuration input should not have been logged.
t.true(logger.hasMessage("Using config from action input:"));
t.true(
logger.hasMessage(`Using configuration file: ${expectedConfigPath}`),
);
t.false(logger.hasMessage("No configuration file was provided"));
// But the warning about both inputs should not have been logged.
t.false(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.",
),
);
}); });
}); });
test("determineUserConfig - ignores config file input when both specified", async (t) => { test("determineUserConfig - ignores config file input when both specified", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const logger = new RecordingLogger();
const env = util.getEnv(DEFAULT_ACTIONS_VARS);
const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); const configFilePath = createConfigFile(otherConfigFileContents, tmpDir);
const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir);
@@ -2388,35 +2378,28 @@ test("determineUserConfig - ignores config file input when both specified", asyn
configFile: configFilePath, configFile: configFilePath,
workspacePath: tmpDir, workspacePath: tmpDir,
}); });
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ logger, env }), .withDefaultActionsEnv()
tmpDir, .withArgs(tmpDir, inputs);
inputs,
); await target
// The path of the generated config file and
// the warning about both inputs should have been logged.
.logs(
t,
`Using config from action input: ${expectedConfigPath}`,
`Using configuration file: ${expectedConfigPath}`,
"Both a config file and config input were provided. Ignoring config file.",
)
.notLogs(t, "No configuration file was provided")
// The loaded configuration should match `simpleConfigFileContents`.
.passes(t.deepEqual, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The loaded configuration should match `simpleConfigFileContents`.
t.deepEqual(result, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
// The `configFile` input should have been mutated to the generated path. // The `configFile` input should have been mutated to the generated path.
t.is(inputs.configFile, expectedConfigPath); t.is(inputs.configFile, expectedConfigPath);
// And the path of the generated config file should have been logged.
t.true(
logger.hasMessage(
`Using config from action input: ${expectedConfigPath}`,
),
);
t.true(
logger.hasMessage(`Using configuration file: ${expectedConfigPath}`),
);
t.false(logger.hasMessage("No configuration file was provided"));
// And the warning about both inputs should have been logged.
t.true(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.",
),
);
}); });
}); });
@@ -2429,11 +2412,6 @@ const defaultSetupConfigInput = `
test("determineUserConfig - merges configs if FF is enabled in Default Setup", async (t) => { test("determineUserConfig - merges configs if FF is enabled in Default Setup", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const logger = new RecordingLogger(true);
const env = util.getEnv({
...DEFAULT_ACTIONS_VARS,
[ActionsEnvVars.GITHUB_EVENT_NAME]: "dynamic",
});
const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir); const configFilePath = createConfigFile(simpleConfigFileContents, tmpDir);
const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir);
@@ -2442,15 +2420,10 @@ test("determineUserConfig - merges configs if FF is enabled in Default Setup", a
configFile: configFilePath, configFile: configFilePath,
workspacePath: tmpDir, workspacePath: tmpDir,
}); });
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ .withDefaultActionsEnv({ GITHUB_EVENT_NAME: "dynamic" })
logger, .withFeatures([Feature.AllowMergeConfigFiles])
env, .withArgs(tmpDir, inputs);
features: createFeatures([Feature.AllowMergeConfigFiles]),
}),
tmpDir,
inputs,
);
// The loaded configuration should match the result of merging // The loaded configuration should match the result of merging
// `defaultSetupConfigInput` and `simpleConfigFileContents`. // `defaultSetupConfigInput` and `simpleConfigFileContents`.
@@ -2464,7 +2437,20 @@ test("determineUserConfig - merges configs if FF is enabled in Default Setup", a
}, },
}, },
} satisfies UserConfig; } satisfies UserConfig;
t.deepEqual(result, expectedConfig);
await target
.logs(
t,
`Using merged configurations from 'config' input with configuration from '${configFilePath}': ${expectedConfigPath}`,
)
.notLogs(
t,
`Using configuration file: ${expectedConfigPath}`,
"No configuration file was provided",
`Using config from action input: ${expectedConfigPath}`,
"Both a config file and config input were provided. Ignoring config file.",
)
.passes(t.deepEqual, expectedConfig);
// The `configFile` input should have been mutated to the generated path. // The `configFile` input should have been mutated to the generated path.
t.is(inputs.configFile, expectedConfigPath); t.is(inputs.configFile, expectedConfigPath);
@@ -2473,120 +2459,73 @@ test("determineUserConfig - merges configs if FF is enabled in Default Setup", a
// also check whether loading the configuration from disk that was written // also check whether loading the configuration from disk that was written
// by `determineUserConfig` matches our expectations. // by `determineUserConfig` matches our expectations.
const loadedFromDisk = configUtils.getLocalConfig( const loadedFromDisk = configUtils.getLocalConfig(
logger, getRunnerLogger(true),
expectedConfigPath, expectedConfigPath,
false, false,
); );
t.deepEqual(loadedFromDisk, expectedConfig); t.deepEqual(loadedFromDisk, expectedConfig);
// And the appropriate origin messages should have been logged.
t.true(
logger.hasMessage(
`Using merged configurations from 'config' input with configuration from '${configFilePath}': ${expectedConfigPath}`,
),
);
t.false(
logger.hasMessage(`Using configuration file: ${expectedConfigPath}`),
);
t.false(logger.hasMessage("No configuration file was provided"));
t.false(
logger.hasMessage(
`Using config from action input: ${expectedConfigPath}`,
),
);
t.false(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.",
),
);
}); });
}); });
test("determineUserConfig - ignores config file input in Default Setup if FF is off", async (t) => { test("determineUserConfig - ignores config file input in Default Setup if FF is off", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const logger = new RecordingLogger(true);
const env = util.getEnv({
...DEFAULT_ACTIONS_VARS,
[ActionsEnvVars.GITHUB_EVENT_NAME]: "dynamic",
});
const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); const configFilePath = createConfigFile(otherConfigFileContents, tmpDir);
const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir);
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ logger, env }), .withDefaultActionsEnv({ GITHUB_EVENT_NAME: "dynamic" })
tmpDir, .withArgs(
createTestInitConfigInputs({ tmpDir,
configInput: simpleConfigFileContents, createTestInitConfigInputs({
configFile: configFilePath, configInput: simpleConfigFileContents,
workspacePath: tmpDir, configFile: configFilePath,
}), workspacePath: tmpDir,
); }),
);
// The loaded configuration should match `simpleConfigFileContents`. await target
t.deepEqual(result, { .logs(
name: "my config", t,
queries: [{ uses: "./foo_file" }],
});
// And the path of the generated config file should have been logged.
t.true(
logger.hasMessage(
`Using config from action input: ${expectedConfigPath}`, `Using config from action input: ${expectedConfigPath}`,
), `Using configuration file: ${expectedConfigPath}`,
);
t.true(
logger.hasMessage(`Using configuration file: ${expectedConfigPath}`),
);
t.false(logger.hasMessage("No configuration file was provided"));
// And the warning about both inputs should have been logged.
t.true(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.", "Both a config file and config input were provided. Ignoring config file.",
), )
); .notLogs(t, "No configuration file was provided")
.passes(t.deepEqual, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
}); });
}); });
test("determineUserConfig - ignores config file input outside Default Setup if FF is on", async (t) => { test("determineUserConfig - ignores config file input outside Default Setup if FF is on", async (t) => {
await withTmpDir(async (tmpDir) => { await withTmpDir(async (tmpDir) => {
const logger = new RecordingLogger(true);
const env = util.getEnv(DEFAULT_ACTIONS_VARS);
const configFilePath = createConfigFile(otherConfigFileContents, tmpDir); const configFilePath = createConfigFile(otherConfigFileContents, tmpDir);
const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir); const expectedConfigPath = configUtils.userConfigFromActionPath(tmpDir);
const result = await configUtils.determineUserConfig( const target = callee(configUtils.determineUserConfig)
initAllState({ .withDefaultActionsEnv()
logger, .withFeatures([Feature.AllowMergeConfigFiles])
env, .withArgs(
features: createFeatures([Feature.AllowMergeConfigFiles]), tmpDir,
}), createTestInitConfigInputs({
tmpDir, configInput: simpleConfigFileContents,
createTestInitConfigInputs({ configFile: configFilePath,
configInput: simpleConfigFileContents, workspacePath: tmpDir,
configFile: configFilePath, }),
workspacePath: tmpDir, );
}),
);
// The loaded configuration should match `simpleConfigFileContents`. await target
t.deepEqual(result, { .logs(
name: "my config", t,
queries: [{ uses: "./foo_file" }],
});
// And the path of the generated config file should have been logged.
t.true(
logger.hasMessage(
`Using config from action input: ${expectedConfigPath}`, `Using config from action input: ${expectedConfigPath}`,
), `Using configuration file: ${expectedConfigPath}`,
);
t.true(
logger.hasMessage(`Using configuration file: ${expectedConfigPath}`),
);
t.false(logger.hasMessage("No configuration file was provided"));
// And the warning about both inputs should have been logged.
t.true(
logger.hasMessage(
"Both a config file and config input were provided. Ignoring config file.", "Both a config file and config input were provided. Ignoring config file.",
), )
); .notLogs(t, "No configuration file was provided")
.passes(t.deepEqual, {
name: "my config",
queries: [{ uses: "./foo_file" }],
});
}); });
}); });