Skip to content

Commit 3afee05

Browse files
committed
chore: add some tests and align provider implementation
Signed-off-by: Ruben Romero Montes <rromerom@redhat.com>
1 parent d41831c commit 3afee05

6 files changed

Lines changed: 278 additions & 12 deletions

File tree

src/providers/golang_gomodules.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,12 @@ function isSupported(manifestName) {
3535
}
3636

3737
/**
38+
* Go modules have no standard license field in go.mod
3839
* @param {string} manifestPath - path to go.mod
3940
* @returns {string|null}
4041
*/
41-
function readLicenseFromManifest() { return null }
42+
// eslint-disable-next-line no-unused-vars
43+
function readLicenseFromManifest(manifestPath) { return null }
4244

4345
/**
4446
* @param {string} manifestDir - the directory where the manifest lies

src/providers/java_gradle.js

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@ export default class Java_gradle extends Base_java {
6969
*/
7070
validateLockFile() { return true; }
7171

72+
/**
73+
* Gradle manifests (build.gradle, build.gradle.kts) have no standard license field.
74+
* @param {string} manifestPath - path to manifest
75+
* @returns {null}
76+
*/
77+
// eslint-disable-next-line no-unused-vars
78+
readLicenseFromManifest(manifestPath) { return null; }
79+
7280
/**
7381
* Provide content and content type for stack analysis.
7482
* @param {string} manifest - the manifest path or name
@@ -100,13 +108,6 @@ export default class Java_gradle extends Base_java {
100108
}
101109
}
102110

103-
/**
104-
* Gradle manifests (build.gradle, build.gradle.kts) have no standard license field.
105-
* @param {string} manifestPath - path to manifest (unused)
106-
* @returns {null}
107-
*/
108-
readLicenseFromManifest() { return null }
109-
110111
/**
111112
* @param {string} line - the line to parse
112113
* @returns {number} the depth of the dependency in the tree starting from 1. -1 if the line is not a dependency.
@@ -198,7 +199,7 @@ export default class Java_gradle extends Base_java {
198199
let sbom = new Sbom();
199200
let root = `${properties.group}:${properties[ROOT_PROJECT_KEY_NAME].match(/Root project '(.+)'/)[1]}:jar:${properties.version}`
200201
let rootPurl = this.parseDep(root)
201-
const license = null; // Gradle has no standard license field in manifest
202+
const license = this.readLicenseFromManifest(manifestPath);
202203
sbom.addRoot(rootPurl, license)
203204
let ignoredDeps = this.#getIgnoredDeps(manifestPath);
204205

@@ -353,7 +354,7 @@ export default class Java_gradle extends Base_java {
353354
let sbom = new Sbom();
354355
let root = `${properties.group}:${properties[ROOT_PROJECT_KEY_NAME].match(/Root project '(.+)'/)[1]}:jar:${properties.version}`
355356
let rootPurl = this.parseDep(root)
356-
const license = null; // Gradle has no standard license field in manifest
357+
const license = this.readLicenseFromManifest(manifestPath);
357358
sbom.addRoot(rootPurl, license)
358359
let ignoredDeps = this.#getIgnoredDeps(manifestPath);
359360

src/providers/java_maven.js

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,11 @@ export default class Java_maven extends Base_java {
6565
}
6666
}
6767

68+
/**
69+
* Read license from pom.xml manifest
70+
* @param {string} manifestPath - path to pom.xml
71+
* @returns {string|null}
72+
*/
6873
readLicenseFromManifest(manifestPath) {
6974
try {
7075
const xml = fs.readFileSync(manifestPath, 'utf-8');

src/providers/python_pip.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,12 @@ function isSupported(manifestName) {
3232
}
3333

3434
/**
35+
* Python requirements.txt has no standard license field
3536
* @param {string} manifestPath - path to requirements.txt
3637
* @returns {string|null}
3738
*/
38-
function readLicenseFromManifest() { return null }
39+
// eslint-disable-next-line no-unused-vars
40+
function readLicenseFromManifest(manifestPath) { return null }
3941

4042
/**
4143
* @param {string} manifestDir - the directory where the manifest lies

test/provider.test.js

Lines changed: 121 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { expect } from 'chai'
22

3-
import { match } from "../src/provider.js"
3+
import { match, matchForLicense, availableProviders } from "../src/provider.js"
44

55
suite('testing the provider utility function', () => {
66
// create a dummy provider for 'dummy_file.typ'
@@ -21,3 +21,123 @@ suite('testing the provider utility function', () => {
2121
.to.throw('unknown.manifest is not supported')
2222
})
2323
});
24+
25+
suite('testing the matchForLicense utility function', () => {
26+
// create dummy providers with different manifest types
27+
const pomProvider = {
28+
isSupported: nameType => 'pom.xml' === nameType,
29+
validateLockFile: () => true,
30+
readLicenseFromManifest: () => 'Apache-2.0',
31+
provideComponent: () => {},
32+
provideStack: () => {}
33+
};
34+
35+
const packageJsonProvider = {
36+
isSupported: nameType => 'package.json' === nameType,
37+
validateLockFile: () => false, // No lock file - should still match for license
38+
readLicenseFromManifest: () => 'MIT',
39+
provideComponent: () => {},
40+
provideStack: () => {}
41+
};
42+
43+
const goModProvider = {
44+
isSupported: nameType => 'go.mod' === nameType,
45+
validateLockFile: () => true,
46+
readLicenseFromManifest: () => null,
47+
provideComponent: () => {},
48+
provideStack: () => {}
49+
};
50+
51+
const testProviders = [pomProvider, packageJsonProvider, goModProvider];
52+
53+
test('should match pom.xml provider by manifest name', () => {
54+
const provider = matchForLicense('/path/to/pom.xml', testProviders);
55+
expect(provider).to.equal(pomProvider);
56+
});
57+
58+
test('should match package.json provider by manifest name', () => {
59+
const provider = matchForLicense('/path/to/package.json', testProviders);
60+
expect(provider).to.equal(packageJsonProvider);
61+
});
62+
63+
test('should match go.mod provider by manifest name', () => {
64+
const provider = matchForLicense('/path/to/go.mod', testProviders);
65+
expect(provider).to.equal(goModProvider);
66+
});
67+
68+
test('should match provider even when lock file does not exist', () => {
69+
// packageJsonProvider has validateLockFile returning false
70+
// but matchForLicense should not check lock file
71+
const provider = matchForLicense('/no/lock/here/package.json', testProviders);
72+
expect(provider).to.equal(packageJsonProvider);
73+
});
74+
75+
test('should match provider with just filename (no full path)', () => {
76+
const provider = matchForLicense('pom.xml', testProviders);
77+
expect(provider).to.equal(pomProvider);
78+
});
79+
80+
test('should throw error when no provider matches manifest type', () => {
81+
expect(() => matchForLicense('/path/to/unknown.txt', testProviders))
82+
.to.throw('unknown.txt is not supported');
83+
});
84+
85+
test('should throw error for empty manifest name', () => {
86+
expect(() => matchForLicense('', testProviders))
87+
.to.throw('is not supported');
88+
});
89+
90+
suite('real provider matching', () => {
91+
test('should match Java Maven provider for pom.xml', () => {
92+
const provider = matchForLicense('/some/path/pom.xml', availableProviders);
93+
expect(provider).to.exist;
94+
expect(provider.isSupported('pom.xml')).to.be.true;
95+
});
96+
97+
test('should match Java Gradle Groovy provider for build.gradle', () => {
98+
const provider = matchForLicense('/some/path/build.gradle', availableProviders);
99+
expect(provider).to.exist;
100+
expect(provider.isSupported('build.gradle')).to.be.true;
101+
});
102+
103+
test('should match Java Gradle Kotlin provider for build.gradle.kts', () => {
104+
const provider = matchForLicense('/some/path/build.gradle.kts', availableProviders);
105+
expect(provider).to.exist;
106+
expect(provider.isSupported('build.gradle.kts')).to.be.true;
107+
});
108+
109+
test('should match JavaScript provider for package.json', () => {
110+
const provider = matchForLicense('/some/path/package.json', availableProviders);
111+
expect(provider).to.exist;
112+
expect(provider.isSupported('package.json')).to.be.true;
113+
});
114+
115+
test('should match Golang provider for go.mod', () => {
116+
const provider = matchForLicense('/some/path/go.mod', availableProviders);
117+
expect(provider).to.exist;
118+
expect(provider.isSupported('go.mod')).to.be.true;
119+
});
120+
121+
test('should match Python provider for requirements.txt', () => {
122+
const provider = matchForLicense('/some/path/requirements.txt', availableProviders);
123+
expect(provider).to.exist;
124+
expect(provider.isSupported('requirements.txt')).to.be.true;
125+
});
126+
127+
test('all matched providers should have readLicenseFromManifest method', () => {
128+
const manifests = [
129+
'pom.xml',
130+
'build.gradle',
131+
'build.gradle.kts',
132+
'package.json',
133+
'go.mod',
134+
'requirements.txt'
135+
];
136+
137+
manifests.forEach(manifest => {
138+
const provider = matchForLicense(`/test/${manifest}`, availableProviders);
139+
expect(provider.readLicenseFromManifest).to.be.a('function');
140+
});
141+
});
142+
});
143+
});

test/providers/license.test.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import path from 'path'
2+
3+
import { expect } from 'chai'
4+
5+
import golangGomodulesProvider from '../../src/providers/golang_gomodules.js'
6+
import Java_gradle_groovy from '../../src/providers/java_gradle_groovy.js'
7+
import Java_gradle_kotlin from '../../src/providers/java_gradle_kotlin.js'
8+
import Java_maven from '../../src/providers/java_maven.js'
9+
import Javascript_npm from '../../src/providers/javascript_npm.js'
10+
import Javascript_pnpm from '../../src/providers/javascript_pnpm.js'
11+
import Javascript_yarn from '../../src/providers/javascript_yarn.js'
12+
import pythonPipProvider from '../../src/providers/python_pip.js'
13+
14+
suite('testing readLicenseFromManifest with existing test manifests', () => {
15+
16+
suite('Java Maven provider', () => {
17+
const provider = new Java_maven();
18+
19+
test('should read Apache-2.0 license when found', () => {
20+
const pomPath = path.resolve('test/providers/tst_manifests/maven/pom_deps_with_ignore_version_from_property/pom.xml');
21+
const license = provider.readLicenseFromManifest(pomPath);
22+
expect(license).to.equal('Apache-2.0');
23+
});
24+
25+
test('should return null when license not present', () => {
26+
const pomPath = path.resolve('test/providers/tst_manifests/maven/pom_deps_with_no_ignore/pom.xml');
27+
const license = provider.readLicenseFromManifest(pomPath);
28+
expect(license).to.be.null;
29+
});
30+
});
31+
32+
suite('Java Gradle Groovy provider', () => {
33+
const provider = new Java_gradle_groovy();
34+
35+
test('should always return null (no standard license field)', () => {
36+
const gradlePath = path.resolve('test/providers/tst_manifests/gradle/deps_with_no_ignore_common_paths/build.gradle');
37+
const license = provider.readLicenseFromManifest(gradlePath);
38+
expect(license).to.be.null;
39+
});
40+
});
41+
42+
suite('Java Gradle Kotlin provider', () => {
43+
const provider = new Java_gradle_kotlin();
44+
45+
test('should always return null (no standard license field)', () => {
46+
const gradlePath = path.resolve('test/providers/tst_manifests/gradle/deps_with_no_ignore_common_paths/build.gradle.kts');
47+
const license = provider.readLicenseFromManifest(gradlePath);
48+
expect(license).to.be.null;
49+
});
50+
});
51+
52+
suite('JavaScript npm provider', () => {
53+
const provider = new Javascript_npm();
54+
55+
test('should read ISC license when found', () => {
56+
const packagePath = path.resolve('test/providers/tst_manifests/npm/package_json_deps_with_exhortignore_object/package.json');
57+
const license = provider.readLicenseFromManifest(packagePath);
58+
expect(license).to.equal('ISC');
59+
});
60+
61+
test('should return null for non-existent file', () => {
62+
const license = provider.readLicenseFromManifest('/fake/path/package.json');
63+
expect(license).to.be.null;
64+
});
65+
});
66+
67+
suite('JavaScript pnpm provider', () => {
68+
const provider = new Javascript_pnpm();
69+
70+
test('should read ISC license when found', () => {
71+
const packagePath = path.resolve('test/providers/tst_manifests/pnpm/package_json_deps_with_exhortignore_object/package.json');
72+
const license = provider.readLicenseFromManifest(packagePath);
73+
expect(license).to.equal('ISC');
74+
});
75+
76+
test('should return null for non-existent file', () => {
77+
const license = provider.readLicenseFromManifest('/fake/path/package.json');
78+
expect(license).to.be.null;
79+
});
80+
});
81+
82+
suite('JavaScript yarn provider', () => {
83+
const provider = new Javascript_yarn();
84+
85+
test('should read ISC license when found', () => {
86+
const packagePath = path.resolve('test/providers/tst_manifests/yarn-classic/package_json_deps_with_exhortignore_object/package.json');
87+
const license = provider.readLicenseFromManifest(packagePath);
88+
expect(license).to.equal('ISC');
89+
});
90+
91+
test('should return null for non-existent file', () => {
92+
const license = provider.readLicenseFromManifest('/fake/path/package.json');
93+
expect(license).to.be.null;
94+
});
95+
});
96+
97+
suite('Golang go.mod provider', () => {
98+
test('should always return null (no standard license field)', () => {
99+
const goModPath = path.resolve('test/providers/tst_manifests/golang/go_mod_no_ignore/go.mod');
100+
const license = golangGomodulesProvider.readLicenseFromManifest(goModPath);
101+
expect(license).to.be.null;
102+
});
103+
});
104+
105+
suite('Python requirements.txt provider', () => {
106+
test('should always return null (no standard license field)', () => {
107+
const reqPath = path.resolve('test/providers/tst_manifests/pip/pip_requirements_txt_no_ignore/requirements.txt');
108+
const license = pythonPipProvider.readLicenseFromManifest(reqPath);
109+
expect(license).to.be.null;
110+
});
111+
});
112+
113+
suite('All providers have readLicenseFromManifest method', () => {
114+
const allProviders = [
115+
{ name: 'Java Maven', instance: new Java_maven() },
116+
{ name: 'Java Gradle Groovy', instance: new Java_gradle_groovy() },
117+
{ name: 'Java Gradle Kotlin', instance: new Java_gradle_kotlin() },
118+
{ name: 'JavaScript npm', instance: new Javascript_npm() },
119+
{ name: 'JavaScript pnpm', instance: new Javascript_pnpm() },
120+
{ name: 'JavaScript yarn', instance: new Javascript_yarn() },
121+
{ name: 'Golang', instance: golangGomodulesProvider },
122+
{ name: 'Python', instance: pythonPipProvider }
123+
];
124+
125+
allProviders.forEach(({ name, instance }) => {
126+
test(`${name} provider exports readLicenseFromManifest`, () => {
127+
expect(instance.readLicenseFromManifest).to.be.a('function');
128+
});
129+
130+
test(`${name} provider readLicenseFromManifest accepts manifestPath parameter`, () => {
131+
// Should not throw when called with a path argument
132+
expect(() => instance.readLicenseFromManifest('/test/path')).to.not.throw();
133+
});
134+
});
135+
});
136+
});

0 commit comments

Comments
 (0)