From 35c91ef0afef839b9565599adfb9269fdfd7c086 Mon Sep 17 00:00:00 2001 From: "Michael B. Gale" Date: Sun, 9 Nov 2025 10:40:18 +0000 Subject: [PATCH] Add tests for `getCsharpHashPatterns` - Make the function more easily testable by allowing `makePatternCheck` to be stubbed. - Use `makePatternCheck` for base patterns as well. --- lib/analyze-action.js | 34 +++++++++------- lib/init-action.js | 38 ++++++++++-------- src/dependency-caching.test.ts | 71 +++++++++++++++++++++++++++++++++- src/dependency-caching.ts | 66 +++++++++++++++++-------------- 4 files changed, 147 insertions(+), 62 deletions(-) diff --git a/lib/analyze-action.js b/lib/analyze-action.js index 6238973f8..f8d825858 100644 --- a/lib/analyze-action.js +++ b/lib/analyze-action.js @@ -91075,30 +91075,31 @@ async function makePatternCheck(patterns) { } return patterns; } +var CSHARP_BASE_PATTERNS = [ + // NuGet + "**/packages.lock.json", + // Paket + "**/paket.lock" +]; +var CSHARP_EXTRA_PATTERNS = [ + "**/*.csproj", + "**/packages.config", + "**/nuget.config" +]; async function getCsharpHashPatterns(codeql, features) { - const basePatterns = [ - // NuGet - "**/packages.lock.json", - // Paket - "**/paket.lock" - ]; - const globber = await makeGlobber(basePatterns); - if ((await globber.glob()).length > 0) { + const basePatterns = await internal.makePatternCheck(CSHARP_BASE_PATTERNS); + if (basePatterns !== void 0) { return basePatterns; } if (await features.getValue("csharp_new_cache_key" /* CsharpNewCacheKey */, codeql)) { - return makePatternCheck([ - "**/*.csproj", - "**/packages.config", - "**/nuget.config" - ]); + return internal.makePatternCheck(CSHARP_EXTRA_PATTERNS); } return void 0; } var defaultCacheConfigs = { java: { getDependencyPaths: getJavaDependencyDirs, - getHashPatterns: async () => makePatternCheck([ + getHashPatterns: async () => internal.makePatternCheck([ // Maven "**/pom.xml", // Gradle @@ -91116,7 +91117,7 @@ var defaultCacheConfigs = { }, go: { getDependencyPaths: () => [(0, import_path.join)(os3.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => makePatternCheck(["**/go.sum"]) + getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]) } }; async function makeGlobber(patterns) { @@ -91228,6 +91229,9 @@ async function cachePrefix2(codeql, features, language) { const featurePrefix = await getFeaturePrefix(codeql, features, language); return `${featurePrefix}${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`; } +var internal = { + makePatternCheck +}; // src/diagnostics.ts var import_fs = require("fs"); diff --git a/lib/init-action.js b/lib/init-action.js index 7a5a844fe..2bec5c64c 100644 --- a/lib/init-action.js +++ b/lib/init-action.js @@ -87262,30 +87262,31 @@ async function makePatternCheck(patterns) { } return patterns; } +var CSHARP_BASE_PATTERNS = [ + // NuGet + "**/packages.lock.json", + // Paket + "**/paket.lock" +]; +var CSHARP_EXTRA_PATTERNS = [ + "**/*.csproj", + "**/packages.config", + "**/nuget.config" +]; async function getCsharpHashPatterns(codeql, features) { - const basePatterns = [ - // NuGet - "**/packages.lock.json", - // Paket - "**/paket.lock" - ]; - const globber = await makeGlobber(basePatterns); - if ((await globber.glob()).length > 0) { + const basePatterns = await internal.makePatternCheck(CSHARP_BASE_PATTERNS); + if (basePatterns !== void 0) { return basePatterns; } if (await features.getValue("csharp_new_cache_key" /* CsharpNewCacheKey */, codeql)) { - return makePatternCheck([ - "**/*.csproj", - "**/packages.config", - "**/nuget.config" - ]); + return internal.makePatternCheck(CSHARP_EXTRA_PATTERNS); } return void 0; } var defaultCacheConfigs = { java: { getDependencyPaths: getJavaDependencyDirs, - getHashPatterns: async () => makePatternCheck([ + getHashPatterns: async () => internal.makePatternCheck([ // Maven "**/pom.xml", // Gradle @@ -87303,7 +87304,7 @@ var defaultCacheConfigs = { }, go: { getDependencyPaths: () => [(0, import_path.join)(os2.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => makePatternCheck(["**/go.sum"]) + getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]) } }; async function makeGlobber(patterns) { @@ -87403,6 +87404,9 @@ async function cachePrefix2(codeql, features, language) { const featurePrefix = await getFeaturePrefix(codeql, features, language); return `${featurePrefix}${prefix}-${CODEQL_DEPENDENCY_CACHE_VERSION}-${runnerOs}-${language}-`; } +var internal = { + makePatternCheck +}; // src/diagnostics.ts var import_fs = require("fs"); @@ -89766,7 +89770,7 @@ async function getWorkflowAbsolutePath(logger) { async function checkWorkflow(logger, codeql) { if (!isDynamicWorkflow() && process.env["CODEQL_ACTION_SKIP_WORKFLOW_VALIDATION" /* SKIP_WORKFLOW_VALIDATION */] !== "true") { core12.startGroup("Validating workflow"); - const validateWorkflowResult = await internal.validateWorkflow( + const validateWorkflowResult = await internal2.validateWorkflow( codeql, logger ); @@ -89780,7 +89784,7 @@ async function checkWorkflow(logger, codeql) { core12.endGroup(); } } -var internal = { +var internal2 = { validateWorkflow }; diff --git a/src/dependency-caching.test.ts b/src/dependency-caching.test.ts index 87501c995..18b660d89 100644 --- a/src/dependency-caching.test.ts +++ b/src/dependency-caching.test.ts @@ -2,16 +2,19 @@ import * as fs from "fs"; import path from "path"; import test from "ava"; - -// import * as sinon from "sinon"; +import * as sinon from "sinon"; import { cacheKeyHashLength } from "./caching-utils"; import { createStubCodeQL } from "./codeql"; import { CacheConfig, checkHashPatterns, + getCsharpHashPatterns, getFeaturePrefix, makePatternCheck, + internal, + CSHARP_BASE_PATTERNS, + CSHARP_EXTRA_PATTERNS, } from "./dependency-caching"; import { Feature } from "./feature-flags"; import { KnownLanguage } from "./languages"; @@ -49,6 +52,70 @@ test("makePatternCheck - returns all patterns if any pattern matches", async (t) }); }); +test("getCsharpHashPatterns - returns base patterns if any pattern matches", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([]); + const makePatternCheckStub = sinon.stub(internal, "makePatternCheck"); + + makePatternCheckStub + .withArgs(CSHARP_BASE_PATTERNS) + .resolves(CSHARP_BASE_PATTERNS); + makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).rejects(); + + await t.notThrowsAsync(async () => { + const result = await getCsharpHashPatterns(codeql, features); + t.deepEqual(result, CSHARP_BASE_PATTERNS); + }); +}); + +test("getCsharpHashPatterns - returns base patterns if any base pattern matches and CsharpNewCacheKey is enabled", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpNewCacheKey]); + const makePatternCheckStub = sinon.stub(internal, "makePatternCheck"); + + makePatternCheckStub + .withArgs(CSHARP_BASE_PATTERNS) + .resolves(CSHARP_BASE_PATTERNS); + makePatternCheckStub + .withArgs(CSHARP_EXTRA_PATTERNS) + .resolves(CSHARP_EXTRA_PATTERNS); + + await t.notThrowsAsync(async () => { + const result = await getCsharpHashPatterns(codeql, features); + t.deepEqual(result, CSHARP_BASE_PATTERNS); + }); +}); + +test("getCsharpHashPatterns - returns extra patterns if any extra pattern matches and CsharpNewCacheKey is enabled", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpNewCacheKey]); + const makePatternCheckStub = sinon.stub(internal, "makePatternCheck"); + + makePatternCheckStub.withArgs(CSHARP_BASE_PATTERNS).resolves(undefined); + makePatternCheckStub + .withArgs(CSHARP_EXTRA_PATTERNS) + .resolves(CSHARP_EXTRA_PATTERNS); + + await t.notThrowsAsync(async () => { + const result = await getCsharpHashPatterns(codeql, features); + t.deepEqual(result, CSHARP_EXTRA_PATTERNS); + }); +}); + +test("getCsharpHashPatterns - returns undefined if neither base nor extra patterns match", async (t) => { + const codeql = createStubCodeQL({}); + const features = createFeatures([Feature.CsharpNewCacheKey]); + const makePatternCheckStub = sinon.stub(internal, "makePatternCheck"); + + makePatternCheckStub.withArgs(CSHARP_BASE_PATTERNS).resolves(undefined); + makePatternCheckStub.withArgs(CSHARP_EXTRA_PATTERNS).resolves(undefined); + + await t.notThrowsAsync(async () => { + const result = await getCsharpHashPatterns(codeql, features); + t.deepEqual(result, undefined); + }); +}); + test("checkHashPatterns - logs when no patterns match", async (t) => { const codeql = createStubCodeQL({}); const features = createFeatures([]); diff --git a/src/dependency-caching.ts b/src/dependency-caching.ts index cc9e0de97..f42ab4d45 100644 --- a/src/dependency-caching.ts +++ b/src/dependency-caching.ts @@ -85,49 +85,55 @@ export async function makePatternCheck( return patterns; } +/** These files contain accurate information about dependencies, including the exact versions + * that the relevant package manager has determined for the project. Using these gives us + * stable hashes unless the dependencies change. + */ +export const CSHARP_BASE_PATTERNS = [ + // NuGet + "**/packages.lock.json", + // Paket + "**/paket.lock", +]; + +/** These are less accurate for use in cache key calculations, because they: + * + * - Don't contain the exact versions used. They may only contain version ranges or none at all. + * - They contain information unrelated to dependencies, which we don't care about. + * + * As a result, the hash we compute from these files may change, even if + * the dependencies haven't changed. + */ +export const CSHARP_EXTRA_PATTERNS = [ + "**/*.csproj", + "**/packages.config", + "**/nuget.config", +]; + /** * Returns the list of glob patterns that should be used to calculate the cache key hash - * for a C# dependency cache. + * for a C# dependency cache. This will try to use `CSHARP_BASE_PATTERNS` whenever possible. + * As a fallback, it will also use `CSHARP_EXTRA_PATTERNS` if the corresponding FF is enabled. * * @param codeql The CodeQL instance to use. * @param features Information about which FFs are enabled. * @returns A list of glob patterns to use for hashing. */ -async function getCsharpHashPatterns( +export async function getCsharpHashPatterns( codeql: CodeQL, features: FeatureEnablement, ): Promise { - // These files contain accurate information about dependencies, including the exact versions - // that the relevant package manager has determined for the project. Using these gives us - // stable hashes unless the dependencies change. - const basePatterns = [ - // NuGet - "**/packages.lock.json", - // Paket - "**/paket.lock", - ]; - const globber = await makeGlobber(basePatterns); + const basePatterns = await internal.makePatternCheck(CSHARP_BASE_PATTERNS); - if ((await globber.glob()).length > 0) { + if (basePatterns !== undefined) { return basePatterns; } if (await features.getValue(Feature.CsharpNewCacheKey, codeql)) { - // These are less accurate for use in cache key calculations, because they: - // - // - Don't contain the exact versions used. They may only contain version ranges or none at all. - // - They contain information unrelated to dependencies, which we don't care about. - // - // As a result, the hash we compute from these files may change, even if - // the dependencies haven't changed. - return makePatternCheck([ - "**/*.csproj", - "**/packages.config", - "**/nuget.config", - ]); + return internal.makePatternCheck(CSHARP_EXTRA_PATTERNS); } - // If we get to this point, the `basePatterns` didn't find any files, + // If we get to this point, we didn't find any files with `CSHARP_BASE_PATTERNS`, // and `Feature.CsharpNewCacheKey` is not enabled. return undefined; } @@ -139,7 +145,7 @@ const defaultCacheConfigs: { [language: string]: CacheConfig } = { java: { getDependencyPaths: getJavaDependencyDirs, getHashPatterns: async () => - makePatternCheck([ + internal.makePatternCheck([ // Maven "**/pom.xml", // Gradle @@ -157,7 +163,7 @@ const defaultCacheConfigs: { [language: string]: CacheConfig } = { }, go: { getDependencyPaths: () => [join(os.homedir(), "go", "pkg", "mod")], - getHashPatterns: async () => makePatternCheck(["**/go.sum"]), + getHashPatterns: async () => internal.makePatternCheck(["**/go.sum"]), }, }; @@ -547,3 +553,7 @@ export async function getDependencyCacheUsage( return undefined; } + +export const internal = { + makePatternCheck, +};