Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions test/fixtures/maven/dependency-tree.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.2.0:compile
[INFO] | \- org.springframework:spring-core:jar:6.1.0:compile
[INFO] \- junit:junit:jar:4.13.2:test
14 changes: 14 additions & 0 deletions test/fixtures/maven/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<project>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>3.2.0</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
</project>
29 changes: 29 additions & 0 deletions test/fixtures/npm/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions test/fixtures/npm/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
{
"name": "fixture-app",
"version": "1.0.0",
"dependencies": {
"express": "^4.18.2",
"@scope/pkg": "^1.0.0"
}
}
20 changes: 20 additions & 0 deletions test/fixtures/npm/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

10 changes: 10 additions & 0 deletions test/fixtures/npm/yarn.lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
express@^4.18.2:
version "4.18.2"
dependencies:
accepts "~1.3.8"

accepts@~1.3.8:
version "1.3.8"

"@scope/pkg@^1.0.0":
version "1.0.0"
12 changes: 12 additions & 0 deletions test/fixtures/python/Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

14 changes: 14 additions & 0 deletions test/fixtures/python/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 15 additions & 0 deletions test/fixtures/python/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
[tool.poetry]
name = "fixture-python"
version = "0.1.0"

[tool.poetry.dependencies]
python = "^3.11"
# Web
flask = "^2.3.0"
requests = { version = "^2.28.0", optional = true } # client

[project]
name = "fixture-python"
dependencies = [
"fastapi==0.111.0", # API
]
2 changes: 2 additions & 0 deletions test/fixtures/python/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
flask==2.3.0
requests>=2.28.0
18 changes: 18 additions & 0 deletions test/fixtures/python/uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

46 changes: 46 additions & 0 deletions test/helpers/fixtureWorkspace.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
const fs = require("fs");
const os = require("os");
const path = require("path");

async function makeTempWorkspace(prefix = "cloudsmith-lockfile-") {
return fs.promises.mkdtemp(path.join(os.tmpdir(), prefix));
}

async function copyFixtureDir(fixtureName, targetDir) {
const sourceDir = path.join(__dirname, "..", "fixtures", fixtureName);
await copyDirectory(sourceDir, targetDir);
}

async function copyDirectory(sourceDir, targetDir) {
await fs.promises.mkdir(targetDir, { recursive: true });
const entries = await fs.promises.readdir(sourceDir, { withFileTypes: true });

for (const entry of entries) {
const sourcePath = path.join(sourceDir, entry.name);
const targetPath = path.join(targetDir, entry.name);

if (entry.isDirectory()) {
await copyDirectory(sourcePath, targetPath);
continue;
}

await fs.promises.copyFile(sourcePath, targetPath);
}
}

async function writeTextFile(targetPath, content) {
await fs.promises.mkdir(path.dirname(targetPath), { recursive: true });
await fs.promises.writeFile(targetPath, content, "utf8");
}

async function removeDirectory(targetDir) {
await fs.promises.rm(targetDir, { recursive: true, force: true });
}

module.exports = {
copyDirectory,
copyFixtureDir,
makeTempWorkspace,
removeDirectory,
writeTextFile,
};
81 changes: 81 additions & 0 deletions test/lockfileParsers/mavenParser.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
const assert = require("assert");
const path = require("path");
const mavenParser = require("../../util/lockfileParsers/mavenParser");
const {
makeTempWorkspace,
removeDirectory,
writeTextFile,
} = require("../helpers/fixtureWorkspace");

suite("mavenParser Test Suite", () => {
const fixtureDir = path.join(__dirname, "..", "fixtures", "maven");
const tempDirs = [];

async function createWorkspace() {
const workspace = await makeTempWorkspace("cloudsmith-maven-parser-");
tempDirs.push(workspace);
return workspace;
}

suiteTeardown(async () => {
await Promise.all(tempDirs.map((tempDir) => removeDirectory(tempDir)));
});

test("hydrates direct dependencies from pom.xml and transitives from dependency-tree.txt", async () => {
const tree = await mavenParser.resolve({
lockfilePath: path.join(fixtureDir, "dependency-tree.txt"),
manifestPath: path.join(fixtureDir, "pom.xml"),
});

assert.strictEqual(tree.sourceFile, "pom.xml");
assert.strictEqual(tree.dependencies.length, 3);

const starter = tree.dependencies.find((dependency) => (
dependency.name === "org.springframework.boot:spring-boot-starter-web"
));
const springCore = tree.dependencies.find((dependency) => dependency.name === "org.springframework:spring-core");
const junit = tree.dependencies.find((dependency) => dependency.name === "junit:junit");

assert.ok(starter);
assert.ok(springCore);
assert.ok(junit);
assert.strictEqual(starter.isDirect, true);
assert.strictEqual(springCore.isDirect, false);
assert.strictEqual(junit.version, "4.13.2");
assert.strictEqual(junit.isDevelopmentDependency, true);
});

test("detect returns no matches when pom.xml is missing", async () => {
const workspace = await createWorkspace();

const matches = await mavenParser.detect(workspace);

assert.deepStrictEqual(matches, []);
assert.strictEqual(await mavenParser.canResolve(workspace), false);
});

test("ignores malformed dependency tree lines and still returns manifest dependencies", async () => {
const workspace = await createWorkspace();
const manifestPath = path.join(workspace, "pom.xml");
const lockfilePath = path.join(workspace, "dependency-tree.txt");
await writeTextFile(manifestPath, [
"<project>",
" <dependencies>",
" <dependency>",
" <groupId>org.springframework.boot</groupId>",
" <artifactId>spring-boot-starter</artifactId>",
" <version>3.2.0</version>",
" </dependency>",
" </dependencies>",
"</project>",
"",
].join("\n"));
await writeTextFile(lockfilePath, "this is not a Maven dependency tree\n");

const tree = await mavenParser.resolve({ lockfilePath, manifestPath });

assert.strictEqual(tree.dependencies.length, 1);
assert.strictEqual(tree.dependencies[0].name, "org.springframework.boot:spring-boot-starter");
assert.strictEqual(tree.dependencies[0].isDirect, true);
});
});
Loading
Loading