/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/
import assert from "node:assert/strict";
import { createHash } from "node:crypto";
import fs from "node:fs";
import path from "node:path";
import test from "node:test";
import { fileURLToPath } from "node:url";
import {
createNativeClassifierTestFixture,
writeStoredZip,
} from "./create-native-classifier-test-fixture.mjs";
import {
validateNativeClassifierJar,
validatePlaceholderJar,
validateSha256Manifest,
} from "./validate-native-artifact.mjs";
import { validateLocalPublication } from "./validate-local-publication.mjs";
const classifier = "win32-x64";
const artifactName = "copilot-sdk-java-runtime-1.2.3-win32-x64.jar";
const moduleRoot = fileURLToPath(new URL("../", import.meta.url));
test("accepts a matching, complete Windows classifier", (t) => {
const fixture = createFixture(t);
createNativeClassifierTestFixture({
classifier,
outputPath: fixture.jarPath,
repoRoot: fixture.repoRoot,
});
assert.deepEqual(
validateNativeClassifierJar({
classifier,
jarPath: fixture.jarPath,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
{ classifier, nativeVersion: "9.8.7", sha256: undefined },
);
});
test("accepts a matching, complete Windows ARM64 classifier", (t) => {
const fixture = createFixture(t);
const windowsArm64Classifier = "win32-arm64";
const windowsArm64ArtifactName =
"copilot-sdk-java-runtime-1.2.3-win32-arm64.jar";
const windowsArm64JarPath = path.join(
fixture.root,
windowsArm64ArtifactName,
);
createNativeClassifierTestFixture({
classifier: windowsArm64Classifier,
outputPath: windowsArm64JarPath,
repoRoot: fixture.repoRoot,
});
assert.deepEqual(
validateNativeClassifierJar({
classifier: windowsArm64Classifier,
jarPath: windowsArm64JarPath,
expectedFilename: windowsArm64ArtifactName,
repoRoot: fixture.repoRoot,
}),
{
classifier: windowsArm64Classifier,
nativeVersion: "9.8.7",
sha256: undefined,
},
);
});
test("accepts a matching, complete Darwin classifier", (t) => {
const fixture = createFixture(t);
const darwinClassifier = "darwin-arm64";
const darwinArtifactName =
"copilot-sdk-java-runtime-1.2.3-darwin-arm64.jar";
const darwinJarPath = path.join(fixture.root, darwinArtifactName);
createNativeClassifierTestFixture({
classifier: darwinClassifier,
outputPath: darwinJarPath,
repoRoot: fixture.repoRoot,
});
assert.deepEqual(
validateNativeClassifierJar({
classifier: darwinClassifier,
jarPath: darwinJarPath,
expectedFilename: darwinArtifactName,
repoRoot: fixture.repoRoot,
}),
{
classifier: darwinClassifier,
nativeVersion: "9.8.7",
sha256: undefined,
},
);
});
test("accepts a matching, complete Darwin x64 classifier", (t) => {
const fixture = createFixture(t);
const darwinClassifier = "darwin-x64";
const darwinArtifactName =
"copilot-sdk-java-runtime-1.2.3-darwin-x64.jar";
const darwinJarPath = path.join(fixture.root, darwinArtifactName);
createNativeClassifierTestFixture({
classifier: darwinClassifier,
outputPath: darwinJarPath,
repoRoot: fixture.repoRoot,
});
assert.deepEqual(
validateNativeClassifierJar({
classifier: darwinClassifier,
jarPath: darwinJarPath,
expectedFilename: darwinArtifactName,
repoRoot: fixture.repoRoot,
}),
{
classifier: darwinClassifier,
nativeVersion: "9.8.7",
sha256: undefined,
},
);
});
test("accepts a matching, complete Linux ARM64 classifier", (t) => {
const fixture = createFixture(t);
const linuxArm64Classifier = "linux-arm64";
const linuxArm64ArtifactName =
"copilot-sdk-java-runtime-1.2.3-linux-arm64.jar";
const linuxArm64JarPath = path.join(fixture.root, linuxArm64ArtifactName);
createNativeClassifierTestFixture({
classifier: linuxArm64Classifier,
outputPath: linuxArm64JarPath,
repoRoot: fixture.repoRoot,
});
assert.deepEqual(
validateNativeClassifierJar({
classifier: linuxArm64Classifier,
jarPath: linuxArm64JarPath,
expectedFilename: linuxArm64ArtifactName,
repoRoot: fixture.repoRoot,
}),
{
classifier: linuxArm64Classifier,
nativeVersion: "9.8.7",
sha256: undefined,
},
);
});
test("rejects a wrong external filename before attachment", (t) => {
const fixture = createFixture(t);
const wrongName = path.join(fixture.root, "arbitrary.jar");
createNativeClassifierTestFixture({
classifier,
outputPath: wrongName,
repoRoot: fixture.repoRoot,
});
assert.throws(
() =>
validateNativeClassifierJar({
classifier,
jarPath: wrongName,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
/must be named/,
);
});
test("rejects a missing classifier JAR with the expected filename", (t) => {
const fixture = createFixture(t);
assert.throws(
() =>
validateNativeClassifierJar({
classifier,
jarPath: fixture.jarPath,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
/does not exist/,
);
});
test("rejects missing native resources", (t) => {
const fixture = createFixture(t);
writeStoredZip(fixture.jarPath, [
["native/win32-x64/runtime.node", "runtime"],
[
"native/win32-x64/platform.properties",
"classifier=win32-x64\nversion=9.8.7\n",
],
]);
assert.throws(
() =>
validateNativeClassifierJar({
classifier,
jarPath: fixture.jarPath,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
/copilot-runtime\.exe/,
);
});
test("rejects incorrect pinned package metadata", (t) => {
const fixture = createFixture(t);
writeStoredZip(fixture.jarPath, [
["native/win32-x64/runtime.node", "runtime"],
["native/win32-x64/copilot-runtime.exe", "runtime wrapper"],
[
"native/win32-x64/platform.properties",
"classifier=win32-x64\nversion=0.0.1\n",
],
]);
assert.throws(
() =>
validateNativeClassifierJar({
classifier,
jarPath: fixture.jarPath,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
/version=9\.8\.7/,
);
});
test("rejects Linux resources in a Windows classifier", (t) => {
const fixture = createFixture(t);
createNativeClassifierTestFixture({
classifier,
outputPath: fixture.jarPath,
repoRoot: fixture.repoRoot,
});
writeStoredZip(fixture.jarPath, [
["native/win32-x64/runtime.node", "runtime"],
["native/win32-x64/copilot-runtime.exe", "runtime wrapper"],
[
"native/win32-x64/platform.properties",
"classifier=win32-x64\nversion=9.8.7\n",
],
["native/linux-x64/runtime.node", "wrong platform"],
]);
assert.throws(
() =>
validateNativeClassifierJar({
classifier,
jarPath: fixture.jarPath,
expectedFilename: artifactName,
repoRoot: fixture.repoRoot,
}),
/must not contain/,
);
});
test("rejects Windows resources in a Linux classifier", (t) => {
const fixture = createFixture(t);
const linuxClassifier = "linux-x64";
const linuxArtifactName =
"copilot-sdk-java-runtime-1.2.3-linux-x64.jar";
const linuxJarPath = path.join(fixture.root, linuxArtifactName);
writeStoredZip(linuxJarPath, [
["native/linux-x64/runtime.node", "runtime"],
["native/linux-x64/copilot-runtime", "runtime wrapper"],
[
"native/linux-x64/platform.properties",
"classifier=linux-x64\nversion=9.8.7\n",
],
["native/win32-x64/runtime.node", "wrong platform"],
]);
assert.throws(
() =>
validateNativeClassifierJar({
classifier: linuxClassifier,
jarPath: linuxJarPath,
expectedFilename: linuxArtifactName,
repoRoot: fixture.repoRoot,
}),
/must not contain/,
);
});
test("rejects native resources in the placeholder JAR", (t) => {
const fixture = createFixture(t);
writeStoredZip(fixture.jarPath, [
["native/win32-x64/runtime.node", "runtime"],
]);
assert.throws(
() => validatePlaceholderJar(fixture.jarPath),
/must not contain/,
);
});
test("rejects Darwin native resources in the placeholder JAR", (t) => {
const fixture = createFixture(t);
writeStoredZip(fixture.jarPath, [
["native/darwin-arm64/runtime.node", "runtime"],
]);
assert.throws(
() => validatePlaceholderJar(fixture.jarPath),
/must not contain/,
);
});
test("accepts a native-free placeholder JAR", (t) => {
const fixture = createFixture(t);
writeStoredZip(fixture.jarPath, [
["META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n"],
]);
assert.doesNotThrow(() => validatePlaceholderJar(fixture.jarPath));
});
test("rejects an invalid SHA-256 manifest", (t) => {
const fixture = createFixture(t);
createNativeClassifierTestFixture({
classifier,
outputPath: fixture.jarPath,
repoRoot: fixture.repoRoot,
});
const manifestPath = path.join(fixture.root, "classifier.sha256");
fs.writeFileSync(manifestPath, `${"0".repeat(64)} ${artifactName}\n`);
assert.throws(
() =>
validateSha256Manifest({
expectedFilename: artifactName,
jarPath: fixture.jarPath,
manifestPath,
}),
/SHA-256 mismatch/,
);
});
test("accepts a matching SHA-256 manifest", (t) => {
const fixture = createFixture(t);
createNativeClassifierTestFixture({
classifier,
outputPath: fixture.jarPath,
repoRoot: fixture.repoRoot,
});
const manifestPath = path.join(fixture.root, "classifier.sha256");
const digest = createHash("sha256")
.update(fs.readFileSync(fixture.jarPath))
.digest("hex");
fs.writeFileSync(manifestPath, `${digest} ${artifactName}\n`);
assert.doesNotThrow(() =>
validateSha256Manifest({
expectedFilename: artifactName,
jarPath: fixture.jarPath,
manifestPath,
}),
);
});
for (const artifactId of ["copilot-sdk-java-runtime", "copilot-sdk-java"]) {
for (const version of ["0.0.0-ci", "1.2.3-SNAPSHOT"]) {
test(`validates signed ${artifactId} publication at ${version}`, (t) => {
const fixture = createPublicationFixture(t, { artifactId, version });
assert.equal(
validateLocalPublication({ ...fixture, requireSignatures: true }),
fixture.publicationDirectory,
);
});
}
for (const { name, from, to, error } of [
{
name: "unresolved revision",
from: "1.2.3",
to: "${revision}",
error: /unresolved.*revision/,
},
{
name: "the committed snapshot instead of the release version",
from: "1.2.3",
to: "1.2.3-SNAPSHOT",
error: /Unexpected Maven coordinates/,
},
{
name: "an incorrect group",
from: "com.github",
to: "org.example",
error: /Unexpected Maven coordinates/,
},
{
name: "an incorrect artifact",
from: `${artifactId}`,
to: "different-artifact",
error: /Unexpected Maven coordinates/,
},
{
name: "a dependency version in place of the project version",
from: "1.2.3",
to: "1.2.3",
error: /missing flattened project coordinates/,
},
{
name: "a commented-out project version",
from: "1.2.3",
to: "",
error: /missing flattened project coordinates/,
},
{
name: "an unpublished parent reference",
from: "4.0.0",
to: `4.0.0
com.github
copilot-sdk-java-parent
1.2.3
`,
error: /must not depend on a parent/,
},
{
name: "unresolved revision in a dependency",
from: "",
to: "${revision}",
error: /unresolved.*revision/,
},
]) {
test(`${artifactId} publication rejects ${name}`, (t) => {
const fixture = createPublicationFixture(t, { artifactId });
const pom = fs.readFileSync(fixture.pomPath, "utf8");
assert.ok(pom.includes(from));
fs.writeFileSync(fixture.pomPath, pom.replace(from, to));
assert.throws(() => validateLocalPublication(fixture), error);
});
}
}
test("local release publication requires a POM signature", (t) => {
const fixture = createPublicationFixture(t);
fs.rmSync(`${fixture.pomPath}.asc`);
assert.throws(
() => validateLocalPublication({ ...fixture, requireSignatures: true }),
/missing signature.*\.pom\.asc/,
);
});
test("local publication validation rejects cross-classifier contamination", (t) => {
const fixture = createPublicationFixture(t);
writeStoredZip(
path.join(
fixture.publicationDirectory,
`${fixture.artifactId}-${fixture.version}-linux-x64.jar`,
),
[
["native/linux-x64/runtime.node", "runtime"],
["native/linux-x64/copilot-runtime", "runtime wrapper"],
[
"native/linux-x64/platform.properties",
"classifier=linux-x64\nversion=9.8.7\n",
],
["native/win32-x64/runtime.node", "wrong platform"],
],
);
assert.throws(() => validateLocalPublication(fixture), /must not contain/);
});
function createPublicationFixture(
t,
{ artifactId = "copilot-sdk-java-runtime", version = "1.2.3" } = {},
) {
const fixture = createFixture(t);
const repositoryPath = path.join(fixture.root, "repository");
const publicationDirectory = path.join(
repositoryPath,
"com",
"github",
artifactId,
version,
);
fs.mkdirSync(publicationDirectory, { recursive: true });
const pomPath = path.join(
publicationDirectory,
`${artifactId}-${version}.pom`,
);
fs.writeFileSync(
pomPath,
`
4.0.0
com.github
${artifactId}
${version}
`,
);
for (const suffix of ["", "-sources", "-javadoc"]) {
writeStoredZip(
path.join(publicationDirectory, `${artifactId}-${version}${suffix}.jar`),
[["META-INF/MANIFEST.MF", "Manifest-Version: 1.0\n"]],
);
}
if (artifactId === "copilot-sdk-java-runtime") {
for (const nativeClassifier of [
"linux-x64",
"linux-arm64",
"linuxmusl-x64",
"win32-x64",
"win32-arm64",
"darwin-x64",
"darwin-arm64",
]) {
createNativeClassifierTestFixture({
classifier: nativeClassifier,
outputPath: path.join(
publicationDirectory,
`${artifactId}-${version}-${nativeClassifier}.jar`,
),
repoRoot: fixture.repoRoot,
});
}
}
for (const artifact of fs.readdirSync(publicationDirectory)) {
fs.writeFileSync(
path.join(publicationDirectory, `${artifact}.asc`),
"signature",
);
}
return {
...fixture,
artifactId,
version,
repositoryPath,
publicationDirectory,
pomPath,
};
}
function createFixture(t) {
const fixtureParent = path.join(
moduleRoot,
"target",
"validate-native-artifact-test-",
);
fs.mkdirSync(fixtureParent, { recursive: true });
const root = fs.mkdtempSync(path.join(fixtureParent, `${process.pid}-`));
t.after(() =>
fs.rmSync(root, {
recursive: true,
force: true,
maxRetries: 3,
retryDelay: 100,
}),
);
const repoRoot = path.join(root, "repo");
fs.mkdirSync(path.join(repoRoot, "nodejs"), { recursive: true });
fs.writeFileSync(
path.join(repoRoot, "nodejs", "package.json"),
JSON.stringify({ copilotCliVersion: "9.8.7" }),
);
return {
root,
repoRoot,
jarPath: path.join(root, artifactName),
};
}