Add some new tests and fix some comments
This commit is contained in:
parent
f79028af27
commit
ad7ca9bf21
22 changed files with 339 additions and 68 deletions
|
|
@ -12,7 +12,12 @@ import * as configUtils from "./config-utils";
|
|||
import { Feature } from "./feature-flags";
|
||||
import { Language } from "./languages";
|
||||
import { getRunnerLogger, Logger } from "./logging";
|
||||
import { setupTests, createFeatures } from "./testing-utils";
|
||||
import { parseRepositoryNwo } from "./repository";
|
||||
import {
|
||||
setupTests,
|
||||
createFeatures,
|
||||
mockLanguagesInRepo as mockLanguagesInRepo,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
||||
setupTests(test);
|
||||
|
|
@ -1761,7 +1766,10 @@ parseInputAndConfigMacro.title = (providedTitle: string) =>
|
|||
|
||||
const mockLogger = {
|
||||
info: (message: string) => {
|
||||
console.log(message);
|
||||
console.log("info:", message);
|
||||
},
|
||||
debug: (message: string) => {
|
||||
console.log("debug:", message);
|
||||
},
|
||||
} as Logger;
|
||||
|
||||
|
|
@ -2449,3 +2457,119 @@ test("downloadPacks-with-registries fails with invalid registries block", async
|
|||
);
|
||||
});
|
||||
});
|
||||
|
||||
// getLanguages
|
||||
|
||||
const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
||||
// eslint-disable-next-line github/array-foreach
|
||||
[
|
||||
{
|
||||
name: "languages from input",
|
||||
codeqlResolvedLanguages: ["javascript", "java", "python"],
|
||||
languagesInput: "jAvAscript, \n jaVa",
|
||||
languagesInRepository: ["SwiFt", "other"],
|
||||
expectedLanguages: ["javascript", "java"],
|
||||
expectedApiCall: false,
|
||||
},
|
||||
{
|
||||
name: "languages from github api",
|
||||
codeqlResolvedLanguages: ["javascript", "java", "python"],
|
||||
languagesInput: "",
|
||||
languagesInRepository: [" jAvAscript\n \t", " jaVa", "SwiFt", "other"],
|
||||
expectedLanguages: ["javascript", "java"],
|
||||
expectedApiCall: true,
|
||||
},
|
||||
{
|
||||
name: "aliases from input",
|
||||
codeqlResolvedLanguages: ["javascript", "csharp", "cpp", "java", "python"],
|
||||
languagesInput: " typEscript\n \t, C#, c , KoTlin",
|
||||
languagesInRepository: ["SwiFt", "other"],
|
||||
expectedLanguages: ["javascript", "csharp", "cpp", "java"],
|
||||
expectedApiCall: false,
|
||||
},
|
||||
{
|
||||
name: "duplicate languages from input",
|
||||
codeqlResolvedLanguages: ["javascript", "java", "python"],
|
||||
languagesInput: "jAvAscript, \n jaVa, kotlin, typescript",
|
||||
languagesInRepository: ["SwiFt", "other"],
|
||||
expectedLanguages: ["javascript", "java"],
|
||||
expectedApiCall: false,
|
||||
},
|
||||
{
|
||||
name: "aliases from github api",
|
||||
codeqlResolvedLanguages: ["javascript", "csharp", "cpp", "java", "python"],
|
||||
languagesInput: "",
|
||||
languagesInRepository: [" typEscript\n \t", " C#", "c", "other"],
|
||||
expectedLanguages: ["javascript", "csharp", "cpp"],
|
||||
expectedApiCall: true,
|
||||
},
|
||||
].forEach((args) => {
|
||||
test(`getLanguages: ${args.name}`, async (t) => {
|
||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||
const languages = args.codeqlResolvedLanguages.reduce(
|
||||
(acc, lang) => ({
|
||||
...acc,
|
||||
[lang]: true,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
const codeQL = setCodeQL({
|
||||
resolveLanguages: () => Promise.resolve(languages),
|
||||
});
|
||||
|
||||
const actualLanguages = await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
mockLogger
|
||||
);
|
||||
t.deepEqual(actualLanguages.sort(), args.expectedLanguages.sort());
|
||||
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
||||
});
|
||||
});
|
||||
|
||||
// eslint-disable-next-line github/array-foreach
|
||||
[
|
||||
{
|
||||
name: "no languages",
|
||||
codeqlResolvedLanguages: ["javascript", "java", "python"],
|
||||
languagesInput: "",
|
||||
languagesInRepository: [],
|
||||
expectedApiCall: true,
|
||||
expectedError: configUtils.getNoLanguagesError(),
|
||||
},
|
||||
{
|
||||
name: "unrecognized languages from input",
|
||||
codeqlResolvedLanguages: ["javascript", "java", "python"],
|
||||
languagesInput: "a, b, c, javascript",
|
||||
languagesInRepository: [],
|
||||
expectedApiCall: false,
|
||||
expectedError: configUtils.getUnknownLanguagesError(["a", "b"]),
|
||||
},
|
||||
].forEach((args) => {
|
||||
test(`getLanguages (error when empty): ${args.name}`, async (t) => {
|
||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||
const languages = args.codeqlResolvedLanguages.reduce(
|
||||
(acc, lang) => ({
|
||||
...acc,
|
||||
[lang]: true,
|
||||
}),
|
||||
{}
|
||||
);
|
||||
const codeQL = setCodeQL({
|
||||
resolveLanguages: () => Promise.resolve(languages),
|
||||
});
|
||||
|
||||
await t.throwsAsync(
|
||||
async () =>
|
||||
await configUtils.getLanguages(
|
||||
codeQL,
|
||||
args.languagesInput,
|
||||
mockRepositoryNwo,
|
||||
mockLogger
|
||||
),
|
||||
{ message: args.expectedError }
|
||||
);
|
||||
t.deepEqual(mockRequest.called, args.expectedApiCall);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -18,8 +18,8 @@ import { Feature, FeatureEnablement } from "./feature-flags";
|
|||
import {
|
||||
Language,
|
||||
LanguageOrAlias,
|
||||
LANGUAGE_ALIASES,
|
||||
parseLanguage,
|
||||
resolveAlias,
|
||||
} from "./languages";
|
||||
import { Logger } from "./logging";
|
||||
import { RepositoryNwo } from "./repository";
|
||||
|
|
@ -857,7 +857,8 @@ export function getUnknownLanguagesError(languages: string[]): string {
|
|||
}
|
||||
|
||||
/**
|
||||
* Gets the set of languages in the current repository
|
||||
* Gets the set of languages in the current repository that are
|
||||
* scannable by CodeQL.
|
||||
*/
|
||||
async function getLanguagesInRepo(
|
||||
repository: RepositoryNwo,
|
||||
|
|
@ -895,7 +896,7 @@ async function getLanguagesInRepo(
|
|||
* If no languages could be detected from either the workflow or the repository
|
||||
* then throw an error.
|
||||
*/
|
||||
async function getLanguages(
|
||||
export async function getLanguages(
|
||||
codeQL: CodeQL,
|
||||
languagesInput: string | undefined,
|
||||
repository: RepositoryNwo,
|
||||
|
|
@ -908,13 +909,13 @@ async function getLanguages(
|
|||
logger
|
||||
);
|
||||
|
||||
let languages: string[];
|
||||
let languages = rawLanguages.map(resolveAlias);
|
||||
|
||||
if (autodetected) {
|
||||
const availableLanguages = await codeQL.resolveLanguages();
|
||||
languages = rawLanguages.filter((value) => value in availableLanguages);
|
||||
languages = languages.filter((value) => value in availableLanguages);
|
||||
logger.info(`Automatically detected languages: ${languages.join(", ")}`);
|
||||
} else {
|
||||
languages = rawLanguages;
|
||||
logger.info(`Languages from configuration: ${languages.join(", ")}`);
|
||||
}
|
||||
|
||||
|
|
@ -928,15 +929,12 @@ async function getLanguages(
|
|||
const parsedLanguages: Language[] = [];
|
||||
const unknownLanguages: string[] = [];
|
||||
for (const language of languages) {
|
||||
const parsedLanguage = parseLanguage(language);
|
||||
const dealiasedLanguage =
|
||||
parsedLanguage && parsedLanguage in LANGUAGE_ALIASES
|
||||
? LANGUAGE_ALIASES[parsedLanguage]
|
||||
: (parsedLanguage as Language);
|
||||
// We know this is not an alias since we resolved it above.
|
||||
const parsedLanguage = parseLanguage(language) as Language;
|
||||
if (parsedLanguage === undefined) {
|
||||
unknownLanguages.push(language);
|
||||
} else if (!parsedLanguages.includes(dealiasedLanguage)) {
|
||||
parsedLanguages.push(dealiasedLanguage);
|
||||
} else if (!parsedLanguages.includes(parsedLanguage)) {
|
||||
parsedLanguages.push(parsedLanguage);
|
||||
}
|
||||
}
|
||||
if (unknownLanguages.length > 0) {
|
||||
|
|
|
|||
|
|
@ -13,12 +13,12 @@ export interface FeatureEnablement {
|
|||
|
||||
export enum Feature {
|
||||
BypassToolcacheEnabled = "bypass_toolcache_enabled",
|
||||
BypassToolcacheKotlinSwiftEnabled = "bypass_toolcache_kotlin_swift_enabled",
|
||||
CliConfigFileEnabled = "cli_config_file_enabled",
|
||||
DisableKotlinAnalysisEnabled = "disable_kotlin_analysis_enabled",
|
||||
FileBaselineInformationEnabled = "file_baseline_information_enabled",
|
||||
MlPoweredQueriesEnabled = "ml_powered_queries_enabled",
|
||||
TrapCachingEnabled = "trap_caching_enabled",
|
||||
BypassToolcacheKotlinSwiftEnabled = "bypass_toolcache_kotlin_switft_enabled",
|
||||
}
|
||||
|
||||
export const featureConfig: Record<
|
||||
|
|
@ -27,6 +27,14 @@ export const featureConfig: Record<
|
|||
> = {
|
||||
[Feature.BypassToolcacheEnabled]: {
|
||||
envVar: "CODEQL_BYPASS_TOOLCACHE",
|
||||
// Cannot specify a minimum version because this flag is checked before we have
|
||||
// access to the CodeQL instance.
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.BypassToolcacheKotlinSwiftEnabled]: {
|
||||
envVar: "CODEQL_BYPASS_TOOLCACHE_KOTLIN_SWIFT",
|
||||
// Cannot specify a minimum version because this flag is checked before we have
|
||||
// access to the CodeQL instance.
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.DisableKotlinAnalysisEnabled]: {
|
||||
|
|
@ -49,10 +57,6 @@ export const featureConfig: Record<
|
|||
envVar: "CODEQL_TRAP_CACHING",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
[Feature.BypassToolcacheKotlinSwiftEnabled]: {
|
||||
envVar: "CODEQL_BYPASS_TOOLCACHE_KOTLIN_SWIFT",
|
||||
minimumVersion: undefined,
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,11 +20,15 @@ test("parseLanguage", async (t) => {
|
|||
t.deepEqual(parseLanguage("python"), Language.python);
|
||||
|
||||
// Aliases
|
||||
t.deepEqual(parseLanguage("c"), Language.cpp);
|
||||
t.deepEqual(parseLanguage("c++"), Language.cpp);
|
||||
t.deepEqual(parseLanguage("c#"), Language.csharp);
|
||||
t.deepEqual(parseLanguage("kotlin"), Language.java);
|
||||
t.deepEqual(parseLanguage("typescript"), Language.javascript);
|
||||
t.deepEqual(parseLanguage("c"), "c");
|
||||
t.deepEqual(parseLanguage("c++"), "c++");
|
||||
t.deepEqual(parseLanguage("c#"), "c#");
|
||||
t.deepEqual(parseLanguage("kotlin"), "kotlin");
|
||||
t.deepEqual(parseLanguage("typescript"), "typescript");
|
||||
|
||||
// spaces and case-insensitivity
|
||||
t.deepEqual(parseLanguage(" \t\nCsHaRp\t\t"), Language.csharp);
|
||||
t.deepEqual(parseLanguage(" \t\nkOtLin\t\t"), "kotlin");
|
||||
|
||||
// Not matches
|
||||
t.deepEqual(parseLanguage("foo"), undefined);
|
||||
|
|
|
|||
|
|
@ -23,17 +23,31 @@ export type LanguageOrAlias = Language | keyof typeof LANGUAGE_ALIASES;
|
|||
|
||||
export const KOTLIN_SWIFT_BYPASS = ["kotlin", "swift"];
|
||||
|
||||
// Translate from user input or GitHub's API names for languages to CodeQL's names for languages
|
||||
export function resolveAlias(lang: LanguageOrAlias): Language {
|
||||
return LANGUAGE_ALIASES[lang] || lang;
|
||||
}
|
||||
|
||||
/**
|
||||
* Translate from user input or GitHub's API names for languages to CodeQL's
|
||||
* names for languages. This does not translate a language alias to the actual
|
||||
* language used by CodeQL.
|
||||
*
|
||||
* @param language The language to translate.
|
||||
* @returns A language supported by CodeQL, an alias for a language, or
|
||||
* `undefined` if the input language cannot be parsed into a langauge supported
|
||||
* by CodeQL.
|
||||
*/
|
||||
export function parseLanguage(language: string): LanguageOrAlias | undefined {
|
||||
// Normalise to lower case
|
||||
language = language.toLowerCase();
|
||||
language = language.trim().toLowerCase();
|
||||
|
||||
// See if it's an exact match
|
||||
if (language in Language) {
|
||||
return language as Language;
|
||||
}
|
||||
|
||||
// Check language aliases
|
||||
// Check language aliases, but return the original language name,
|
||||
// the alias will be resolved later.
|
||||
if (language in LANGUAGE_ALIASES) {
|
||||
return language;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ export function mockFeatureFlagApiEndpoint(
|
|||
sinon.stub(apiClient, "getApiClient").value(() => client);
|
||||
}
|
||||
|
||||
export function mockLangaugesInRepo(languages: string[]) {
|
||||
export function mockLanguagesInRepo(languages: string[]) {
|
||||
const mockClient = sinon.stub(apiClient, "getApiClient");
|
||||
const listLanguages = sinon.stub().resolves({
|
||||
status: 200,
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import { parseRepositoryNwo } from "./repository";
|
|||
import {
|
||||
createFeatures,
|
||||
getRecordingLogger,
|
||||
mockLangaugesInRepo,
|
||||
mockLanguagesInRepo,
|
||||
setupTests,
|
||||
} from "./testing-utils";
|
||||
import * as util from "./util";
|
||||
|
|
@ -543,7 +543,7 @@ const mockRepositoryNwo = parseRepositoryNwo("owner/repo");
|
|||
},
|
||||
].forEach((args) => {
|
||||
test(`shouldBypassToolcache: ${args.name}`, async (t) => {
|
||||
const mockRequest = mockLangaugesInRepo(args.languagesInRepository);
|
||||
const mockRequest = mockLanguagesInRepo(args.languagesInRepository);
|
||||
const mockLogger = getRecordingLogger([]);
|
||||
const featureEnablement = createFeatures(args.features);
|
||||
const codeqlUrl = args.hasCustomCodeQL ? "custom-codeql-url" : undefined;
|
||||
|
|
|
|||
10
src/util.ts
10
src/util.ts
|
|
@ -870,7 +870,15 @@ export async function shouldBypassToolcache(
|
|||
repository,
|
||||
logger
|
||||
);
|
||||
return rawLanguages.some((lang) => KOTLIN_SWIFT_BYPASS.includes(lang));
|
||||
const bypass = rawLanguages.some((lang) =>
|
||||
KOTLIN_SWIFT_BYPASS.includes(lang)
|
||||
);
|
||||
if (bypass) {
|
||||
logger.info(
|
||||
`Bypassing toolcache for kotlin or swift. Languages: ${rawLanguages}`
|
||||
);
|
||||
}
|
||||
return bypass;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue