Refactor: Extract another method and test with real data

This commit is contained in:
Lewis Jones
2025-06-19 15:13:55 +01:00
parent 6d56d2b42c
commit 2efc7af7df
2 changed files with 96 additions and 139 deletions
+81 -129
View File
@@ -69,161 +69,113 @@ describe("ComponentDetection.makePackageUrl", () => {
});
});
describe("ComponentDetection.addPackagesToManifests", () => {
describe("ComponentDetection.processComponentsToManifests", () => {
test("adds package as direct dependency when no top level referrers", () => {
const manifests: any[] = [];
const componentsFound = [
{
component: {
name: "test-package",
version: "1.0.0",
packageUrl: {
Scheme: "pkg",
Type: "npm",
Name: "test-package",
Version: "1.0.0"
},
id: "test-package 1.0.0 - npm"
},
isDevelopmentDependency: false,
topLevelReferrers: [], // Empty = direct dependency
locationsFoundAt: ["package.json"]
}
];
const testPackage = {
id: "test-package",
packageUrl: "pkg:npm/test-package@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [], // Empty array = direct dependency
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/test-package@1.0.0",
packageURL: { toString: () => "pkg:npm/test-package@1.0.0" }
};
ComponentDetection.addPackagesToManifests([testPackage] as any, manifests);
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
expect(manifests).toHaveLength(1);
expect(manifests[0].name).toBe("package.json");
// Test the actual manifest state instead of mock calls
expect(manifests[0].directDependencies()).toHaveLength(1);
expect(manifests[0].indirectDependencies()).toHaveLength(0);
expect(manifests[0].countDependencies()).toBe(1);
});
test("adds package as indirect dependency when has top level referrers", () => {
const manifests: any[] = [];
const componentsFound = [
{
component: {
name: "test-package",
version: "1.0.0",
packageUrl: {
Scheme: "pkg",
Type: "npm",
Name: "test-package",
Version: "1.0.0"
},
id: "test-package 1.0.0 - npm"
},
isDevelopmentDependency: false,
topLevelReferrers: [
{
name: "parent-package",
version: "1.0.0",
packageUrl: {
Scheme: "pkg",
Type: "npm",
Name: "parent-package",
Version: "1.0.0"
}
}
],
locationsFoundAt: ["package.json"]
}
];
const testPackage = {
id: "test-package",
packageUrl: "pkg:npm/test-package@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [{ packageUrl: "pkg:npm/parent-package@1.0.0" }], // Has referrers = indirect
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/test-package@1.0.0",
packageURL: { toString: () => "pkg:npm/test-package@1.0.0" }
};
ComponentDetection.addPackagesToManifests([testPackage] as any, manifests);
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
expect(manifests).toHaveLength(1);
expect(manifests[0].name).toBe("package.json");
// Test the actual manifest state - should be indirect dependency
expect(manifests[0].directDependencies()).toHaveLength(0);
expect(manifests[0].indirectDependencies()).toHaveLength(1);
expect(manifests[0].countDependencies()).toBe(1);
});
// Component detection reports some packages as top level referrers of themselves
// We need to mark as direct as causes Dependency Graph to mark the package as transitive without any Direct
test("adds package as indirect dependency when top level referrer is itself", () => {
const manifests: any[] = [];
test("adds package as direct dependency when top level referrer is itself", () => {
const componentsFound = [
{
component: {
name: "test-package",
version: "1.0.0",
packageUrl: {
Scheme: "pkg",
Type: "npm",
Name: "test-package",
Version: "1.0.0"
},
id: "test-package 1.0.0 - npm"
},
isDevelopmentDependency: false,
topLevelReferrers: [
{
name: "test-package",
version: "1.0.0",
packageUrl: {
Scheme: "pkg",
Type: "npm",
Name: "test-package",
Version: "1.0.0"
}
}
],
locationsFoundAt: ["package.json"]
}
];
const testPackage = {
id: "test-package",
packageUrl: "pkg:npm/test-package@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [{ packageUrl: "pkg:npm/test-package@1.0.0" }],
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/test-package@1.0.0",
packageURL: { toString: () => "pkg:npm/test-package@1.0.0" }
};
ComponentDetection.addPackagesToManifests([testPackage] as any, manifests);
const manifests = ComponentDetection.processComponentsToManifests(componentsFound);
expect(manifests).toHaveLength(1);
expect(manifests[0].name).toBe("package.json");
expect(manifests[0].directDependencies()).toHaveLength(1);
expect(manifests[0].indirectDependencies()).toHaveLength(0);
expect(manifests[0].countDependencies()).toBe(1);
});
test("handles multiple packages with mixed dependency types", () => {
const manifests: any[] = [];
const directTestPackage = {
id: "direct-package",
packageUrl: "pkg:npm/direct-package@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [], // Direct
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/direct-package@1.0.0",
packageURL: { toString: () => "pkg:npm/direct-package@1.0.0" }
};
const indirectTestPackage = {
id: "indirect-package",
packageUrl: "pkg:npm/indirect-package@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [{ packageUrl: "pkg:npm/parent@1.0.0" }], // Indirect
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/indirect-package@1.0.0",
packageURL: { toString: () => "pkg:npm/indirect-package@1.0.0" }
};
ComponentDetection.addPackagesToManifests([directTestPackage, indirectTestPackage] as any, manifests);
expect(manifests).toHaveLength(1);
expect(manifests[0].name).toBe("package.json");
expect(manifests[0].directDependencies()).toHaveLength(1);
expect(manifests[0].indirectDependencies()).toHaveLength(1);
expect(manifests[0].countDependencies()).toBe(2);
});
test("creates separate manifests for different locations", () => {
const manifests: any[] = [];
const packageJsonTestPackage = {
id: "package-json-dep",
packageUrl: "pkg:npm/package-json-dep@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [],
locationsFoundAt: ["package.json"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:npm/package-json-dep@1.0.0",
packageURL: { toString: () => "pkg:npm/package-json-dep@1.0.0" }
};
const csprojTestPackage = {
id: "csproj-dep",
packageUrl: "pkg:nuget/csproj-dep@1.0.0",
isDevelopmentDependency: false,
topLevelReferrers: [],
locationsFoundAt: ["project.csproj"],
containerDetailIds: [],
containerLayerIds: [],
packageID: () => "pkg:nuget/csproj-dep@1.0.0",
packageURL: { toString: () => "pkg:nuget/csproj-dep@1.0.0" }
};
ComponentDetection.addPackagesToManifests([packageJsonTestPackage, csprojTestPackage] as any, manifests);
expect(manifests).toHaveLength(2);
const packageJsonManifest = manifests.find(m => m.name === "package.json");
const csprojManifest = manifests.find(m => m.name === "project.csproj");
expect(packageJsonManifest).toBeDefined();
expect(csprojManifest).toBeDefined();
expect(packageJsonManifest.countDependencies()).toBe(1);
expect(csprojManifest.countDependencies()).toBe(1);
});
});
+15 -10
View File
@@ -68,14 +68,17 @@ export default class ComponentDetection {
public static async getManifestsFromResults(): Promise<Manifest[] | undefined> {
core.info("Getting manifests from results");
const results = await fs.readFileSync(this.outputPath, 'utf8');
var json: any = JSON.parse(results);
return this.processComponentsToManifests(json.componentsFound);
}
public static processComponentsToManifests(componentsFound: any[]): Manifest[] {
// Parse the result file and add the packages to the package cache
const packageCache = new PackageCache();
const packages: Array<ComponentDetectionPackage> = [];
const results = await fs.readFileSync(this.outputPath, 'utf8');
var json: any = JSON.parse(results);
json.componentsFound.forEach(async (component: any) => {
componentsFound.forEach(async (component: any) => {
// Skip components without packageUrl
if (!component.component.packageUrl) {
core.debug(`Skipping component detected without packageUrl: ${JSON.stringify({
@@ -113,6 +116,7 @@ export default class ComponentDetection {
}
const referrerUrl = ComponentDetection.makePackageUrl(referrer.packageUrl);
referrer.packageUrlString = referrerUrl
// Skip if the generated packageUrl is empty
if (!referrerUrl) {
@@ -140,9 +144,9 @@ export default class ComponentDetection {
return manifests;
}
public static addPackagesToManifests(packages: Array<ComponentDetectionPackage>, manifests: Array<Manifest>): void {
packages.forEach(async (pkg: ComponentDetectionPackage) => {
pkg.locationsFoundAt.forEach(async (location: any) => {
private static addPackagesToManifests(packages: Array<ComponentDetectionPackage>, manifests: Array<Manifest>): void {
packages.forEach((pkg: ComponentDetectionPackage) => {
pkg.locationsFoundAt.forEach((location: any) => {
if (!manifests.find((manifest: Manifest) => manifest.name == location)) {
const manifest = new Manifest(location, location);
manifests.push(manifest);
@@ -150,9 +154,8 @@ export default class ComponentDetection {
// Filter out self-references from topLevelReferrers
const nonSelfReferrers = pkg.topLevelReferrers.filter((referrer: any) => {
if (!referrer.packageUrl) return false;
const referrerUrl = ComponentDetection.makePackageUrl(referrer.packageUrl);
return referrerUrl !== pkg.packageUrl;
if (!referrer.packageUrlString) return false;
return referrer.packageUrlString !== pkg.packageUrlString;
});
if (nonSelfReferrers.length == 0) {
@@ -249,10 +252,12 @@ export default class ComponentDetection {
}
class ComponentDetectionPackage extends Package {
public packageUrlString: string;
constructor(packageUrl: string, public id: string, public isDevelopmentDependency: boolean, public topLevelReferrers: [],
public locationsFoundAt: [], public containerDetailIds: [], public containerLayerIds: []) {
super(packageUrl);
this.packageUrlString = packageUrl;
}
}