Files
Munitor/tests/lean_java_workflow.bats
james.maesandCodex d7c278b344 fix(ci): preserve required Java PR check name
Give the differently parameterized production build a distinct name so CircleCI retains build-and-test for pull requests.

Co-Authored-By: Codex <noreply@openai.com>
Signed-off-by: James Maes <james@kof22.com>
2026-10-19 10:46:38 -05:00

85 lines
4.7 KiB
Bash

#!/usr/bin/env bats
setup() {
ROOT="${BATS_TEST_DIRNAME}/.."
CONFIG="${BATS_TEST_TMPDIR}/rendered.yml"
}
check_workflows() {
local expected="$1"
yq -o=json '.' "$CONFIG" | EXPECTED_COVERAGE="$expected" node -e '
const assert = require("node:assert/strict");
const config = JSON.parse(require("node:fs").readFileSync(0,"utf8"));
for (const [workflow, definition] of Object.entries(config.workflows)) {
const jobs = definition.jobs.map(entry => {
const [type, options] = Object.entries(entry)[0];
assert.ok(!["munitor/mvn_code_quality", "munitor/mvn_coverage"].includes(type));
return {type, ...options};
});
const names = jobs.map(job => job.name);
assert.equal(new Set(names).size, names.length, workflow);
const buildName = workflow === "production" ? "production-build-and-test" : "build-and-test";
const build = jobs.find(job => job.name === buildName);
assert.ok(build, workflow);
assert.equal(jobs.filter(job => job.type === "munitor/mvn_build_and_test").length, 1);
assert.ok(!names.includes(workflow === "production" ? "build-and-test" : "production-build-and-test"));
const expectedFilter = {
"pr-checks": ["/feature\\/.*/", "/hotfix\\/.*/"],
develop: "develop", staging: "staging",
"release-candidate": ["/release\\/.*/"], production: "main"
}[workflow];
assert.deepEqual(build.filters.branches.only, expectedFilter, workflow);
if (workflow === "production") {
assert.equal(build.quality_checks, undefined);
assert.equal(build.min_instruction, undefined);
} else {
assert.equal(build.quality_checks, true, workflow);
assert.equal(String(build.min_instruction), process.env.EXPECTED_COVERAGE, workflow);
for (const gate of ["secrets-scan", "sast-scan", "security-scan"]) assert.ok(names.includes(gate), workflow);
}
for (const job of jobs) {
assert.deepEqual(job.filters, build.filters, `${workflow}/${job.name} filters`);
const dependencies = job.requires || [];
assert.equal(new Set(dependencies).size, dependencies.length, job.name);
for (const name of dependencies) assert.ok(names.includes(name), `${workflow}/${job.name}: ${name}`);
if (["image-scan", "security-scan", "e2e-tests", "sonar-scan"].includes(job.name)) assert.ok(dependencies.includes(buildName));
if (["docker-build-push", "sbom"].includes(job.name)) {
assert.ok(dependencies.includes(buildName));
if (workflow !== "production") for (const gate of ["secrets-scan", "sast-scan", "security-scan", "e2e-tests", "sonar-scan"].filter(name => names.includes(name))) assert.ok(dependencies.includes(gate), `${workflow}/${job.name}/${gate}`);
}
if (["update-cd-repo", "github-release"].includes(job.name)) assert.ok(dependencies.includes("docker-build-push"));
}
if (workflow === "pr-checks") assert.ok(names.includes("image-scan"));
}
for (const workflow of ["pr-checks", "develop", "staging", "release-candidate", "production"]) assert.ok(config.workflows[workflow]);
'
}
@test "full Java workflow folds quality into build and preserves optional gates" {
MUNITOR_CONFIG="${ROOT}/tests/fixtures/java-webapp.munitor.yml" MUNITOR_OUTPUT_FILE="$CONFIG" bash "${ROOT}/src/scripts/generate_config.sh"
check_workflows 80
[ "$(yq '.workflows.develop.jobs[] | .[] | select(.name == "sonar-scan") | .name' "$CONFIG")" = "sonar-scan" ]
}
@test "minimal Java workflow retains default coverage and production behavior" {
MUNITOR_CONFIG="${ROOT}/tests/fixtures/java-webapp-minimal.munitor.yml" MUNITOR_OUTPUT_FILE="$CONFIG" bash "${ROOT}/src/scripts/generate_config.sh"
check_workflows 70
}
@test "quality steps finish before workspace persistence and forward coverage threshold" {
yq -o=json '.' "${ROOT}/src/jobs/mvn_build_and_test.yml" | node -e '
const assert = require("node:assert/strict");
const job = JSON.parse(require("node:fs").readFileSync(0,"utf8"));
assert.equal(job.parameters.quality_checks.default, false);
assert.equal(job.parameters.min_instruction.default, "70");
const index = name => job.steps.findIndex(step => step === name || typeof step === "object" && name in step);
assert.ok(index("mvn_test") < index("when"));
assert.ok(index("when") < index("persist_to_workspace"));
const block = job.steps[index("when")].when;
assert.equal(block.condition, "<< parameters.quality_checks >>");
const commands = block.steps.map(step => typeof step === "string" ? step : Object.keys(step)[0]);
assert.deepEqual(commands.slice(0,4), ["mvn_checkstyle", "mvn_spotbugs", "mvn_pmd", "mvn_jacoco"]);
assert.equal(block.steps[3].mvn_jacoco.min_instruction, "<< parameters.min_instruction >>");
'
}