goldenChat base source add
This commit is contained in:
@@ -0,0 +1,341 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# test_prepare_release.sh
|
||||
#
|
||||
# Tests the responsibilities of prepare-release.sh:
|
||||
# - CHANGELOG transformation
|
||||
# - README version updates
|
||||
# - Release notes file creation
|
||||
# - Output variables
|
||||
# =============================================================================
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/prepare-release.sh"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_contains() {
|
||||
local haystack="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq "$needle" <<<"$haystack"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_matches() {
|
||||
local file="$1"
|
||||
local regex="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Eq -- "$regex" "$file"; then
|
||||
fail "$msg (regex mismatch: '$regex')"
|
||||
fi
|
||||
}
|
||||
|
||||
run_test() {
|
||||
TMP="$(mktemp -d "${TMPDIR:-/tmp}/prepare-release-test.XXXXXX")"
|
||||
mkdir -p "$TMP/scripts"
|
||||
cp "$SCRIPT" "$TMP/scripts/prepare-release.sh"
|
||||
chmod +x "$TMP/scripts/prepare-release.sh"
|
||||
|
||||
pushd "$TMP" >/dev/null || exit 1
|
||||
}
|
||||
|
||||
finish_test() {
|
||||
popd >/dev/null || exit 1
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 1: Basic Release Flow
|
||||
# -----------------------------------------------------------------------------
|
||||
test_basic_flow() {
|
||||
echo "Running test_basic_flow"
|
||||
|
||||
run_test
|
||||
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## Unreleased
|
||||
|
||||
- Added new Indicator ABC.
|
||||
|
||||
## 1.2.3 (2024-01-01)
|
||||
- Older note
|
||||
EOF
|
||||
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-core</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-core</artifactId>
|
||||
<version>0.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-examples</artifactId>
|
||||
<version>0.0.1</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-examples</artifactId>
|
||||
<version>0.0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:end -->
|
||||
EOF
|
||||
|
||||
OUT="$(scripts/prepare-release.sh 1.3.0)"
|
||||
|
||||
expect_contains "$OUT" "release_version=1.3.0" "script should print release version"
|
||||
expect_contains "$OUT" "release_notes_file=release/1.3.0.md" "script should print release notes file path"
|
||||
|
||||
expect_file_contains CHANGELOG.md "## Unreleased" "Unreleased should be preserved"
|
||||
expect_file_contains CHANGELOG.md "- _No changes yet._" "Unreleased should be reset with placeholder"
|
||||
|
||||
expect_file_matches CHANGELOG.md "## 1\\.3\\.0 \\([0-9]{4}-[0-9]{2}-[0-9]{2}\\)" "new release header should exist"
|
||||
expect_file_contains CHANGELOG.md "Added new Indicator ABC." "notes should move into release section"
|
||||
|
||||
expect_file_contains release/1.3.0.md "Added new Indicator ABC." "release notes file should include notes"
|
||||
expect_file_matches release/1.3.0.md "^## 1\\.3\\.0 \\([0-9]{4}-[0-9]{2}-[0-9]{2}\\)" "release notes should have correct header"
|
||||
|
||||
expect_file_contains README.md "<version>1.3.0</version>" "README version should be updated"
|
||||
expect_file_contains README.md "<version>1.3.1-SNAPSHOT</version>" "README snapshot version should be incremented and preserved"
|
||||
|
||||
finish_test
|
||||
pass "test_basic_flow"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 2: Missing Unreleased Section
|
||||
# -----------------------------------------------------------------------------
|
||||
test_missing_unreleased() {
|
||||
echo "Running test_missing_unreleased"
|
||||
|
||||
run_test
|
||||
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## 1.2.0 (2023-12-01)
|
||||
- Old release
|
||||
EOF
|
||||
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<version>0.0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:begin -->
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:begin -->
|
||||
<version>0.0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:begin -->
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:end -->
|
||||
EOF
|
||||
|
||||
OUT="$(scripts/prepare-release.sh 1.3.0)"
|
||||
|
||||
expect_file_contains CHANGELOG.md "## Unreleased" "should create Unreleased"
|
||||
expect_file_contains CHANGELOG.md "## 1.3.0" "should add release section"
|
||||
|
||||
finish_test
|
||||
pass "test_missing_unreleased"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 3: Empty CHANGELOG
|
||||
# -----------------------------------------------------------------------------
|
||||
test_empty_changelog() {
|
||||
echo "Running test_empty_changelog"
|
||||
|
||||
run_test
|
||||
|
||||
touch CHANGELOG.md
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<version>0.0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:begin -->
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:begin -->
|
||||
<version>0.0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:begin -->
|
||||
<version>0.0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:end -->
|
||||
EOF
|
||||
|
||||
OUT="$(scripts/prepare-release.sh 1.0.0)"
|
||||
|
||||
expect_file_contains CHANGELOG.md "## Unreleased" "should add Unreleased"
|
||||
expect_file_contains CHANGELOG.md "## 1.0.0" "should add release section"
|
||||
expect_file_contains CHANGELOG.md "- _No changes yet._" "should add placeholder"
|
||||
|
||||
finish_test
|
||||
pass "test_empty_changelog"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 4: Snapshot Version Increment (major.minor.patch)
|
||||
# -----------------------------------------------------------------------------
|
||||
test_snapshot_patch_increment() {
|
||||
echo "Running test_snapshot_patch_increment"
|
||||
|
||||
run_test
|
||||
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## Unreleased
|
||||
|
||||
- Some change.
|
||||
EOF
|
||||
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-core</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-core</artifactId>
|
||||
<version>0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-examples</artifactId>
|
||||
<version>0.1</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:end -->
|
||||
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:begin -->
|
||||
<dependency>
|
||||
<artifactId>ta4j-examples</artifactId>
|
||||
<version>0.2-SNAPSHOT</version>
|
||||
</dependency>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:end -->
|
||||
EOF
|
||||
|
||||
scripts/prepare-release.sh 2.7.4 >/dev/null
|
||||
|
||||
expect_file_contains README.md "<version>2.7.4</version>" "README stable version should be updated"
|
||||
expect_file_contains README.md "<version>2.7.5-SNAPSHOT</version>" "README snapshot version should increment patch and preserve suffix"
|
||||
|
||||
finish_test
|
||||
pass "test_snapshot_patch_increment"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 5: Missing README Sentinel
|
||||
# -----------------------------------------------------------------------------
|
||||
test_missing_readme_sentinel() {
|
||||
echo "Running test_missing_readme_sentinel"
|
||||
|
||||
run_test
|
||||
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## Unreleased
|
||||
|
||||
- Some change.
|
||||
EOF
|
||||
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<version>0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
EOF
|
||||
|
||||
if scripts/prepare-release.sh 1.0.0 >/dev/null 2>&1; then
|
||||
fail "script should fail when README sentinels are missing"
|
||||
fi
|
||||
|
||||
finish_test
|
||||
pass "test_missing_readme_sentinel"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
# TEST 6: Invalid Release Version
|
||||
# -----------------------------------------------------------------------------
|
||||
test_invalid_release_version() {
|
||||
echo "Running test_invalid_release_version"
|
||||
|
||||
run_test
|
||||
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## Unreleased
|
||||
|
||||
- Some change.
|
||||
EOF
|
||||
|
||||
cat > README.md <<'EOF'
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:begin -->
|
||||
<version>0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:begin -->
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:core:snapshot:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:begin -->
|
||||
<version>0.1</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:stable:end -->
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:begin -->
|
||||
<version>0.1-SNAPSHOT</version>
|
||||
<!-- TA4J_VERSION_BLOCK:examples:snapshot:end -->
|
||||
EOF
|
||||
|
||||
if scripts/prepare-release.sh 1.3 >/dev/null 2>&1; then
|
||||
fail "script should fail when release version is not major.minor.patch"
|
||||
fi
|
||||
|
||||
expect_file_contains CHANGELOG.md "## Unreleased" "changelog should remain unchanged on version error"
|
||||
expect_file_contains CHANGELOG.md "- Some change." "changelog entries should remain unchanged on version error"
|
||||
|
||||
finish_test
|
||||
pass "test_invalid_release_version"
|
||||
}
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
main() {
|
||||
test_basic_flow
|
||||
test_missing_unreleased
|
||||
test_empty_changelog
|
||||
test_snapshot_patch_increment
|
||||
test_missing_readme_sentinel
|
||||
test_invalid_release_version
|
||||
|
||||
echo "All tests passed."
|
||||
}
|
||||
|
||||
main "$@"
|
||||
@@ -0,0 +1,241 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
WORKFLOW="$ROOT/.github/workflows/prepare-release.yml"
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
line_of() {
|
||||
local needle="$1"
|
||||
local line
|
||||
line="$(grep -nF -- "$needle" "$WORKFLOW" | head -n1 | cut -d: -f1)"
|
||||
if [[ -z "$line" ]]; then
|
||||
fail "missing workflow line: '$needle'"
|
||||
fi
|
||||
printf '%s\n' "$line"
|
||||
}
|
||||
|
||||
workflow_section() {
|
||||
local start="$1"
|
||||
local end="$2"
|
||||
awk -v start="$start" -v end="$end" '
|
||||
index($0, start) { active = 1 }
|
||||
active { print }
|
||||
index($0, end) { active = 0 }
|
||||
' "$WORKFLOW"
|
||||
}
|
||||
|
||||
test_issue_permissions_declared() {
|
||||
echo "Running test_issue_permissions_declared"
|
||||
|
||||
local permissions
|
||||
permissions="$(ruby -e 'require "yaml"; workflow = YAML.load_file(ARGV[0]); puts workflow.fetch("permissions").fetch("issues")' "$WORKFLOW")"
|
||||
if [[ "$permissions" != "write" ]]; then
|
||||
fail "prepare-release workflow should request issues: write permission"
|
||||
fi
|
||||
|
||||
pass "test_issue_permissions_declared"
|
||||
}
|
||||
|
||||
test_issue_sync_uses_targeted_search() {
|
||||
echo "Running test_issue_sync_uses_targeted_search"
|
||||
|
||||
expect_file_contains "$WORKFLOW" "search.issuesAndPullRequests" "issue sync should use targeted issue search"
|
||||
expect_file_contains "$WORKFLOW" "is:issue in:body" "issue sync should search by marker in the body"
|
||||
if grep -Fq "issues.listForRepo" "$WORKFLOW"; then
|
||||
fail "issue sync should not paginate every repo issue"
|
||||
fi
|
||||
|
||||
pass "test_issue_sync_uses_targeted_search"
|
||||
}
|
||||
|
||||
test_deprecation_scan_uses_java_scanner() {
|
||||
echo "Running test_deprecation_scan_uses_java_scanner"
|
||||
|
||||
expect_file_contains "$WORKFLOW" "exec:java" "deprecation scan should run through Maven exec"
|
||||
expect_file_contains "$WORKFLOW" "-pl ta4j-examples -am -DskipTests install" \
|
||||
"deprecation scan should install reactor dependencies before execution"
|
||||
expect_file_contains "$WORKFLOW" "-pl ta4j-examples exec:java" \
|
||||
"deprecation scan should run exec:java only on ta4j-examples"
|
||||
expect_file_contains "$WORKFLOW" "ta4jexamples.doc.RemovalReadyDeprecationScanner" \
|
||||
"deprecation scan should invoke the Java scanner"
|
||||
if grep -Fq "scan-removal-ready-deprecations.py" "$WORKFLOW"; then
|
||||
fail "deprecation scan should not depend on the removed Python scanner"
|
||||
fi
|
||||
|
||||
pass "test_deprecation_scan_uses_java_scanner"
|
||||
}
|
||||
|
||||
test_prepare_release_sets_up_jdk25_before_maven() {
|
||||
echo "Running test_prepare_release_sets_up_jdk25_before_maven"
|
||||
|
||||
local setup_line
|
||||
local read_version_line
|
||||
local gate_line
|
||||
setup_line="$(line_of "Set up JDK 25")"
|
||||
read_version_line="$(line_of "Read current version from POM")"
|
||||
gate_line="$(line_of "Gate release-ready deprecations")"
|
||||
if (( setup_line >= read_version_line )); then
|
||||
fail "prepare-release should configure JDK 25 before reading the Maven project version"
|
||||
fi
|
||||
if (( setup_line >= gate_line )); then
|
||||
fail "prepare-release should configure JDK 25 before the deprecation release gate"
|
||||
fi
|
||||
|
||||
expect_file_contains "$WORKFLOW" "uses: actions/setup-java@v5" \
|
||||
"prepare-release should use the standard setup-java action"
|
||||
expect_file_contains "$WORKFLOW" "distribution: temurin" \
|
||||
"prepare-release should use the same Temurin distribution as other workflows"
|
||||
expect_file_contains "$WORKFLOW" "java-version: 25" \
|
||||
"prepare-release should satisfy the Maven enforcer Java version"
|
||||
expect_file_contains "$WORKFLOW" "cache: maven" \
|
||||
"prepare-release should enable Maven cache for Java setup"
|
||||
|
||||
pass "test_prepare_release_sets_up_jdk25_before_maven"
|
||||
}
|
||||
|
||||
test_release_gate_runs_before_next_snapshot() {
|
||||
echo "Running test_release_gate_runs_before_next_snapshot"
|
||||
|
||||
local gate_line
|
||||
local next_snapshot_line
|
||||
gate_line="$(line_of "Gate release-ready deprecations")"
|
||||
next_snapshot_line="$(line_of "Set Maven version to next snapshot")"
|
||||
if (( gate_line >= next_snapshot_line )); then
|
||||
fail "release deprecation gate should run before next snapshot version is applied"
|
||||
fi
|
||||
|
||||
expect_file_contains "$WORKFLOW" "--target-removal-version \${RELEASE_VERSION}" \
|
||||
"release gate should scan against the release version"
|
||||
expect_file_contains "$WORKFLOW" "--include-overdue --fail-on-due" \
|
||||
"release gate should include overdue removals and fail on due removals"
|
||||
expect_file_contains "$WORKFLOW" "release-ready-deprecations-\${{ steps.versions.outputs.release }}" \
|
||||
"release gate report should be uploaded as its own artifact"
|
||||
|
||||
pass "test_release_gate_runs_before_next_snapshot"
|
||||
}
|
||||
|
||||
test_next_snapshot_issue_sync_runs_after_snapshot_commit() {
|
||||
echo "Running test_next_snapshot_issue_sync_runs_after_snapshot_commit"
|
||||
|
||||
local snapshot_commit_line
|
||||
local scan_line
|
||||
local sync_line
|
||||
local push_line
|
||||
snapshot_commit_line="$(line_of "Commit next snapshot version")"
|
||||
scan_line="$(line_of "Scan removal-ready deprecations")"
|
||||
sync_line="$(line_of "Create or update removal-ready deprecation issues")"
|
||||
push_line="$(line_of "Push release branch")"
|
||||
if (( scan_line <= snapshot_commit_line )); then
|
||||
fail "next-snapshot deprecation scan should run after the next snapshot commit"
|
||||
fi
|
||||
if (( sync_line <= scan_line )); then
|
||||
fail "deprecation issue sync should run after the next-snapshot scan"
|
||||
fi
|
||||
if (( sync_line >= push_line )); then
|
||||
fail "deprecation issue sync should finish before the release branch is pushed"
|
||||
fi
|
||||
expect_file_contains "$WORKFLOW" "NEXT_REMOVAL_VERSION=\"\${NEXT_VERSION%-SNAPSHOT}\"" \
|
||||
"next-snapshot scan should derive the planned removal target from nextVersion"
|
||||
expect_file_contains "$WORKFLOW" "--target-removal-version \${NEXT_REMOVAL_VERSION}" \
|
||||
"next-snapshot scan should not rely on a dry-run POM mutation"
|
||||
expect_file_contains "$WORKFLOW" "--target-removal-version \${NEXT_REMOVAL_VERSION} --include-overdue" \
|
||||
"next-snapshot scan should include overdue removal versions when release versions jump"
|
||||
|
||||
pass "test_next_snapshot_issue_sync_runs_after_snapshot_commit"
|
||||
}
|
||||
|
||||
test_release_audit_includes_deprecation_counts() {
|
||||
echo "Running test_release_audit_includes_deprecation_counts"
|
||||
|
||||
expect_file_contains "$WORKFLOW" "\"removalIssuePlans\"" \
|
||||
"release audit JSON should include removal issue plan counts"
|
||||
expect_file_contains "$WORKFLOW" "\"removalIssuesCreated\"" \
|
||||
"release audit JSON should include created removal issue counts"
|
||||
expect_file_contains "$WORKFLOW" "\"removalIssuesClosedStale\"" \
|
||||
"release audit JSON should include stale issue closure counts"
|
||||
expect_file_contains "$WORKFLOW" "removal-ready deprecation issue plans" \
|
||||
"workflow summary should include removal-ready deprecation plan counts"
|
||||
expect_file_contains "$WORKFLOW" "prepare-release-audit-\${{ github.run_id }}" \
|
||||
"prepare-release audit artifact should still be uploaded"
|
||||
|
||||
pass "test_release_audit_includes_deprecation_counts"
|
||||
}
|
||||
|
||||
test_issue_sync_reconciles_stale_managed_issues() {
|
||||
echo "Running test_issue_sync_reconciles_stale_managed_issues"
|
||||
|
||||
expect_file_contains "$WORKFLOW" "ta4j:deprecation-removal version=\${removalVersion}" \
|
||||
"issue sync should search managed deprecation issues by removal version"
|
||||
expect_file_contains "$WORKFLOW" "closedStaleCount" \
|
||||
"issue sync should report stale managed issue closures"
|
||||
expect_file_contains "$WORKFLOW" "state_reason: \"completed\"" \
|
||||
"stale managed issues should be closed as completed"
|
||||
expect_file_contains "$WORKFLOW" "closed_stale_count" \
|
||||
"closed stale issue count should be exposed as an output"
|
||||
|
||||
pass "test_issue_sync_reconciles_stale_managed_issues"
|
||||
}
|
||||
|
||||
test_deprecation_issue_sync_does_not_add_metadata_to_cleanup_issues() {
|
||||
echo "Running test_deprecation_issue_sync_does_not_add_metadata_to_cleanup_issues"
|
||||
|
||||
local sync_section
|
||||
sync_section="$(workflow_section "Create or update removal-ready deprecation issues" "Upload removal-ready deprecation report")"
|
||||
if grep -Fq "labels:" <<<"$sync_section"; then
|
||||
fail "generated deprecation cleanup issues should not receive labels"
|
||||
fi
|
||||
if grep -Fq "assignees:" <<<"$sync_section"; then
|
||||
fail "generated deprecation cleanup issues should not receive assignees"
|
||||
fi
|
||||
|
||||
pass "test_deprecation_issue_sync_does_not_add_metadata_to_cleanup_issues"
|
||||
}
|
||||
|
||||
test_deprecation_scans_run_during_dry_runs() {
|
||||
echo "Running test_deprecation_scans_run_during_dry_runs"
|
||||
|
||||
local gate_section
|
||||
local scan_section
|
||||
local issue_section
|
||||
gate_section="$(workflow_section "Gate release-ready deprecations" "Upload release-ready deprecation gate report")"
|
||||
scan_section="$(workflow_section "Scan removal-ready deprecations" "Create or update removal-ready deprecation issues")"
|
||||
issue_section="$(workflow_section "Create or update removal-ready deprecation issues" "Upload removal-ready deprecation report")"
|
||||
if grep -Fq "if: steps.dry_run.outputs.dryRun != 'true'" <<<"$gate_section"; then
|
||||
fail "release deprecation gate should run during dry-run"
|
||||
fi
|
||||
if grep -Fq "if: steps.dry_run.outputs.dryRun != 'true'" <<<"$scan_section"; then
|
||||
fail "next-snapshot deprecation scan should run during dry-run"
|
||||
fi
|
||||
expect_file_contains "$WORKFLOW" "scan_status=\$scan_status" \
|
||||
"release gate should expose scan status for delayed dry-run failure"
|
||||
expect_file_contains "$WORKFLOW" "if: steps.dry_run.outputs.dryRun == 'true' && steps.deprecation_release_gate.outputs.scan_status != '0'" \
|
||||
"dry-run should fail after report uploads when release-ready deprecations are found"
|
||||
if ! grep -Fq "if: steps.dry_run.outputs.dryRun != 'true'" <<<"$issue_section"; then
|
||||
fail "deprecation issue sync should skip dry-run mutations"
|
||||
fi
|
||||
|
||||
pass "test_deprecation_scans_run_during_dry_runs"
|
||||
}
|
||||
|
||||
test_issue_permissions_declared
|
||||
test_issue_sync_uses_targeted_search
|
||||
test_deprecation_scan_uses_java_scanner
|
||||
test_prepare_release_sets_up_jdk25_before_maven
|
||||
test_release_gate_runs_before_next_snapshot
|
||||
test_next_snapshot_issue_sync_runs_after_snapshot_commit
|
||||
test_release_audit_includes_deprecation_counts
|
||||
test_issue_sync_reconciles_stale_managed_issues
|
||||
test_deprecation_issue_sync_does_not_add_metadata_to_cleanup_issues
|
||||
test_deprecation_scans_run_during_dry_runs
|
||||
+147
@@ -0,0 +1,147 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
POM="$ROOT/pom.xml"
|
||||
CORE_POM="$ROOT/ta4j-core/pom.xml"
|
||||
EXAMPLES_POM="$ROOT/ta4j-examples/pom.xml"
|
||||
WORKFLOW="$ROOT/.github/workflows/test.yml"
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_matches() {
|
||||
local file="$1"
|
||||
local pattern="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Eq -- "$pattern" "$file"; then
|
||||
fail "$msg (pattern: '$pattern')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_not_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (unexpected: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_execution_contains() {
|
||||
local file="$1"
|
||||
local execution_id="$2"
|
||||
local needle="$3"
|
||||
local msg="$4"
|
||||
local block
|
||||
block="$(awk -v id="$execution_id" '
|
||||
/<execution>/ { in_execution=1; block=$0 ORS; next }
|
||||
in_execution { block = block $0 ORS }
|
||||
/<\/execution>/ {
|
||||
if (block ~ "<id>" id "</id>") {
|
||||
print block
|
||||
found=1
|
||||
exit
|
||||
}
|
||||
in_execution=0
|
||||
block=""
|
||||
}
|
||||
END {
|
||||
if (!found) {
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
' "$file")" || fail "$msg (execution not found: '$execution_id')"
|
||||
|
||||
if ! grep -Fq -- "$needle" <<<"$block"; then
|
||||
fail "$msg (missing: '$needle' in execution '$execution_id')"
|
||||
fi
|
||||
}
|
||||
|
||||
test_parent_declares_advisory_quality_defaults() {
|
||||
echo "Running test_parent_declares_advisory_quality_defaults"
|
||||
|
||||
expect_file_matches "$POM" "<spotbugs.version>[0-9]+(\\.[0-9]+)+</spotbugs.version>" "parent pom should pin SpotBugs with an explicit version"
|
||||
expect_file_matches "$POM" "<jacoco.version>[0-9]+(\\.[0-9]+)+</jacoco.version>" "parent pom should pin JaCoCo with an explicit version"
|
||||
expect_file_contains "$POM" "<ta4j.jacoco.line.minimum>0.80</ta4j.jacoco.line.minimum>" "line coverage threshold should be declared"
|
||||
expect_file_contains "$POM" "<ta4j.jacoco.branch.minimum>0.80</ta4j.jacoco.branch.minimum>" "branch coverage threshold should be declared"
|
||||
expect_file_not_contains "$POM" "<ta4j.spotbugs.failOnError>" "SpotBugs advisory mode should not rely on a shared property"
|
||||
expect_file_not_contains "$POM" "<ta4j.jacoco.haltOnFailure>" "JaCoCo advisory mode should not rely on a shared property"
|
||||
|
||||
pass "test_parent_declares_advisory_quality_defaults"
|
||||
}
|
||||
|
||||
test_parent_manages_quality_plugins_for_verify() {
|
||||
echo "Running test_parent_manages_quality_plugins_for_verify"
|
||||
|
||||
expect_file_contains "$POM" "<artifactId>spotbugs-maven-plugin</artifactId>" "parent pom should manage SpotBugs"
|
||||
expect_file_contains "$POM" "<artifactId>jacoco-maven-plugin</artifactId>" "parent pom should manage JaCoCo"
|
||||
expect_file_contains "$POM" "<artifactId>exec-maven-plugin</artifactId>" "parent pom should skip root exec:java runs"
|
||||
expect_file_contains "$POM" "<skip>true</skip>" "parent pom should skip exec:java on the aggregator"
|
||||
expect_file_contains "$POM" "<quiet>true</quiet>" "SpotBugs should stay compact in verify logs"
|
||||
expect_file_contains "$POM" "@{argLine}" "Surefire should late-bind the JaCoCo agent argLine"
|
||||
expect_execution_contains "$POM" "spotbugs-check" "<phase>verify</phase>" "SpotBugs should stay wired into verify"
|
||||
expect_execution_contains "$POM" "spotbugs-check" "<failOnError>false</failOnError>" "SpotBugs should stay advisory only for the verify-bound execution"
|
||||
expect_execution_contains "$POM" "jacoco-check" "<phase>verify</phase>" "JaCoCo should stay wired into verify"
|
||||
expect_execution_contains "$POM" "jacoco-check" "<haltOnFailure>false</haltOnFailure>" "JaCoCo should stay advisory only for the verify-bound execution"
|
||||
|
||||
pass "test_parent_manages_quality_plugins_for_verify"
|
||||
}
|
||||
|
||||
test_modules_opt_in_to_managed_quality_plugins() {
|
||||
echo "Running test_modules_opt_in_to_managed_quality_plugins"
|
||||
|
||||
expect_file_contains "$CORE_POM" "<artifactId>spotbugs-maven-plugin</artifactId>" "ta4j-core should opt into SpotBugs"
|
||||
expect_file_contains "$CORE_POM" "<artifactId>jacoco-maven-plugin</artifactId>" "ta4j-core should opt into JaCoCo"
|
||||
expect_file_contains "$EXAMPLES_POM" "<artifactId>spotbugs-maven-plugin</artifactId>" "ta4j-examples should opt into SpotBugs"
|
||||
expect_file_contains "$EXAMPLES_POM" "<artifactId>jacoco-maven-plugin</artifactId>" "ta4j-examples should opt into JaCoCo"
|
||||
expect_file_contains "$EXAMPLES_POM" "<exec.mainClass>ta4jexamples.Quickstart</exec.mainClass>" "ta4j-examples should default exec:java to Quickstart"
|
||||
expect_file_contains "$EXAMPLES_POM" "<mainClass>\${exec.mainClass}</mainClass>" "ta4j-examples should allow exec:java main-class overrides"
|
||||
|
||||
pass "test_modules_opt_in_to_managed_quality_plugins"
|
||||
}
|
||||
|
||||
test_ci_runs_verify_for_both_jobs() {
|
||||
echo "Running test_ci_runs_verify_for_both_jobs"
|
||||
|
||||
expect_file_contains "$WORKFLOW" "run: xvfb-run mvn -B verify" "default CI job should run verify"
|
||||
expect_file_contains "$WORKFLOW" "run: xvfb-run mvn -B verify -Dta4j.excludedTestTags=analysis-demo,elliott-macro-cycle-replay" "non-demo CI job should run verify"
|
||||
|
||||
pass "test_ci_runs_verify_for_both_jobs"
|
||||
}
|
||||
|
||||
test_docs_point_to_real_maven_commands() {
|
||||
echo "Running test_docs_point_to_real_maven_commands"
|
||||
|
||||
expect_file_contains "$ROOT/README.md" "Run \`mvn verify\` before opening or updating a pull request." "README should point contributors at the real verify command"
|
||||
expect_file_contains "$ROOT/README.md" "mvn -pl ta4j-core -am spotbugs:check" "README should document the standalone SpotBugs loop"
|
||||
expect_file_contains "$ROOT/README.md" "mvn -pl ta4j-core -am test jacoco:report jacoco:check" "README should document the standalone JaCoCo gate"
|
||||
expect_file_contains "$ROOT/README.md" "mvn -pl ta4j-core -am -Dtest=BarSeriesManagerTest -Dsurefire.failIfNoSpecifiedTests=false test jacoco:report" "README should document a focused JaCoCo report-only loop"
|
||||
expect_file_contains "$ROOT/README.md" "- [Build commands: Maven](#build-commands-maven)" "README table of contents should link to the renamed build section"
|
||||
expect_file_contains "$ROOT/README.md" "mvn -pl ta4j-examples exec:java -Dexec.mainClass=ta4jexamples.backtesting.TradingRecordParityBacktest" "README should demonstrate overriding exec:java with a non-default example"
|
||||
expect_file_contains "$ROOT/.github/CONTRIBUTING.md" "**Run this before opening or updating a PR:** \`mvn -B verify\`" "contributing guide should use verify as the canonical PR command"
|
||||
expect_file_contains "$ROOT/.github/CONTRIBUTING.md" "mvn -pl ta4j-core -am spotbugs:check" "contributing guide should document the standalone SpotBugs loop"
|
||||
expect_file_contains "$ROOT/.github/CONTRIBUTING.md" "mvn -pl ta4j-core -am test jacoco:report jacoco:check" "contributing guide should document the standalone JaCoCo gate"
|
||||
expect_file_contains "$ROOT/.github/CONTRIBUTING.md" "mvn -B license:format formatter:format" "contributing guide should keep the formatter and license fix command"
|
||||
expect_file_not_contains "$ROOT/README.md" "./mvnw" "README should not advertise a missing Maven Wrapper"
|
||||
|
||||
pass "test_docs_point_to_real_maven_commands"
|
||||
}
|
||||
|
||||
test_parent_declares_advisory_quality_defaults
|
||||
test_parent_manages_quality_plugins_for_verify
|
||||
test_modules_opt_in_to_managed_quality_plugins
|
||||
test_ci_runs_verify_for_both_jobs
|
||||
test_docs_point_to_real_maven_commands
|
||||
|
||||
echo
|
||||
echo "All quality scan config tests passed."
|
||||
@@ -0,0 +1,305 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# test_release_helpers.sh
|
||||
#
|
||||
# Validates release workflow helper behavior used by GitHub Actions.
|
||||
# =============================================================================
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/release/release_helpers.py"
|
||||
PYTHON="${PYTHON:-python3}"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
run_test() {
|
||||
TMP="$(mktemp -d "${TMPDIR:-/tmp}/release-helpers.XXXXXX")"
|
||||
pushd "$TMP" >/dev/null || exit 1
|
||||
}
|
||||
|
||||
finish_test() {
|
||||
popd >/dev/null || exit 1
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_json_value() {
|
||||
local file="$1"
|
||||
local filter="$2"
|
||||
local expected="$3"
|
||||
local actual
|
||||
actual="$($PYTHON - "$file" "$filter" <<'PY'
|
||||
import json
|
||||
import sys
|
||||
|
||||
path, key = sys.argv[1:3]
|
||||
with open(path, encoding="utf-8") as handle:
|
||||
data = json.load(handle)
|
||||
value = data
|
||||
for part in key.split("."):
|
||||
value = value[part]
|
||||
if isinstance(value, bool):
|
||||
print("true" if value else "false")
|
||||
else:
|
||||
print(value)
|
||||
PY
|
||||
)"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
fail "expected ${filter}=${expected}, got ${actual:-<missing>}"
|
||||
fi
|
||||
}
|
||||
|
||||
write_catalog_fixture() {
|
||||
cat > catalog.json <<'EOF'
|
||||
[
|
||||
{
|
||||
"id": "openai/gpt-4.1",
|
||||
"name": "OpenAI GPT-4.1",
|
||||
"publisher": "OpenAI",
|
||||
"summary": "Large-context model",
|
||||
"rate_limit_tier": "high",
|
||||
"limits": {
|
||||
"max_input_tokens": 1048576,
|
||||
"max_output_tokens": 32768
|
||||
},
|
||||
"html_url": "https://github.com/marketplace/models/azure-openai/gpt-4-1"
|
||||
}
|
||||
]
|
||||
EOF
|
||||
}
|
||||
|
||||
test_catalog_preflight_accepts_configured_model() {
|
||||
echo "Running test_catalog_preflight_accepts_configured_model"
|
||||
run_test
|
||||
write_catalog_fixture
|
||||
|
||||
GITHUB_OUTPUT=outputs.txt "$PYTHON" "$SCRIPT" catalog-preflight \
|
||||
--model openai/gpt-4.1 \
|
||||
--catalog-file catalog.json \
|
||||
--output release-ai-model.json
|
||||
|
||||
expect_json_value release-ai-model.json id openai/gpt-4.1
|
||||
expect_json_value release-ai-model.json max_input_tokens 1048576
|
||||
expect_file_contains outputs.txt "model_id=openai/gpt-4.1" "catalog preflight should emit model id"
|
||||
|
||||
finish_test
|
||||
pass "test_catalog_preflight_accepts_configured_model"
|
||||
}
|
||||
|
||||
test_catalog_preflight_rejects_missing_model() {
|
||||
echo "Running test_catalog_preflight_rejects_missing_model"
|
||||
run_test
|
||||
write_catalog_fixture
|
||||
|
||||
if "$PYTHON" "$SCRIPT" catalog-preflight \
|
||||
--model openai/missing \
|
||||
--catalog-file catalog.json \
|
||||
--output release-ai-model.json >preflight.log 2>&1; then
|
||||
fail "catalog preflight should reject an unavailable model"
|
||||
fi
|
||||
expect_file_contains preflight.log "openai/missing" "failure should name missing model"
|
||||
|
||||
finish_test
|
||||
pass "test_catalog_preflight_rejects_missing_model"
|
||||
}
|
||||
|
||||
test_parse_decision_normalizes_major_and_invalid_json() {
|
||||
echo "Running test_parse_decision_normalizes_major_and_invalid_json"
|
||||
run_test
|
||||
|
||||
cat > ai-content.txt <<'EOF'
|
||||
```json
|
||||
{"should_release":true,"bump":"major","confidence":0.91,"reason":"Breaking but pre-1.0 change","evidence":["public API changed"]}
|
||||
```
|
||||
EOF
|
||||
"$PYTHON" "$SCRIPT" parse-decision --raw-file ai-content.txt --output decision.json --github-output outputs.txt
|
||||
expect_json_value decision.json should_release true
|
||||
expect_json_value decision.json bump minor
|
||||
expect_file_contains outputs.txt "bump=minor" "major bumps should be downgraded in outputs"
|
||||
|
||||
cat > ai-content.txt <<'EOF'
|
||||
{"should_release":"false","bump":"minor","confidence":0.7,"reason":"No release needed"}
|
||||
EOF
|
||||
"$PYTHON" "$SCRIPT" parse-decision --raw-file ai-content.txt --output string-false.json
|
||||
expect_json_value string-false.json should_release false
|
||||
expect_json_value string-false.json bump patch
|
||||
|
||||
cat > ai-content.txt <<'EOF'
|
||||
{"should_release":"1","bump":"patch","confidence":0.7,"reason":"Release needed"}
|
||||
EOF
|
||||
"$PYTHON" "$SCRIPT" parse-decision --raw-file ai-content.txt --output string-true.json
|
||||
expect_json_value string-true.json should_release true
|
||||
|
||||
cat > ai-content.txt <<'EOF'
|
||||
{"should_release":"maybe","bump":"minor","confidence":0.7,"reason":"Ambiguous response"}
|
||||
EOF
|
||||
"$PYTHON" "$SCRIPT" parse-decision --raw-file ai-content.txt --output invalid-flag.json
|
||||
expect_json_value invalid-flag.json should_release false
|
||||
expect_json_value invalid-flag.json bump patch
|
||||
expect_file_contains invalid-flag.json "invalid should_release 'maybe'" "invalid flag should be called out"
|
||||
|
||||
printf 'not-json' > ai-content.txt
|
||||
"$PYTHON" "$SCRIPT" parse-decision --raw-file ai-content.txt --output invalid.json
|
||||
expect_json_value invalid.json should_release false
|
||||
expect_json_value invalid.json bump patch
|
||||
expect_json_value invalid.json warning "Invalid AI JSON"
|
||||
|
||||
finish_test
|
||||
pass "test_parse_decision_normalizes_major_and_invalid_json"
|
||||
}
|
||||
|
||||
test_build_dossier_groups_and_truncates_diff() {
|
||||
echo "Running test_build_dossier_groups_and_truncates_diff"
|
||||
run_test
|
||||
|
||||
git init -q -b master
|
||||
git config user.name "Test User"
|
||||
git config user.email "test@example.com"
|
||||
mkdir -p ta4j-core/src/main/java/org/ta4j/core scripts
|
||||
cat > CHANGELOG.md <<'EOF'
|
||||
## Unreleased
|
||||
|
||||
- Added release helper coverage.
|
||||
EOF
|
||||
cat > pom.xml <<'EOF'
|
||||
<project><version>1.0.0-SNAPSHOT</version></project>
|
||||
EOF
|
||||
cat > ta4j-core/src/main/java/org/ta4j/core/Fixture.java <<'EOF'
|
||||
package org.ta4j.core;
|
||||
|
||||
public class Fixture {
|
||||
}
|
||||
EOF
|
||||
git add .
|
||||
git commit -q -m "Initial"
|
||||
git tag -a 1.0.0 -m "Release 1.0.0"
|
||||
|
||||
cat > ta4j-core/src/main/java/org/ta4j/core/Fixture.java <<'EOF'
|
||||
package org.ta4j.core;
|
||||
|
||||
/**
|
||||
* Fixture API.
|
||||
*
|
||||
* @since 1.0.1
|
||||
*/
|
||||
public class Fixture {
|
||||
public String value() {
|
||||
return "release-helper-dossier-with-enough-content-to-trigger-truncation";
|
||||
}
|
||||
}
|
||||
EOF
|
||||
git add .
|
||||
git commit -q -m "Add fixture API"
|
||||
|
||||
"$PYTHON" "$SCRIPT" build-dossier \
|
||||
--last-tag 1.0.0 \
|
||||
--current-version 1.0.1-SNAPSHOT \
|
||||
--pom-base 1.0.1 \
|
||||
--max-diff-chars 120 \
|
||||
--output release-dossier.md \
|
||||
--audit-output release-audit.json
|
||||
|
||||
expect_file_contains release-dossier.md "production code" "dossier should group production code"
|
||||
expect_file_contains release-dossier.md "Public API Signals" "dossier should include API signal section"
|
||||
expect_file_contains release-dossier.md "[TRUNCATED" "dossier should make diff truncation explicit"
|
||||
expect_json_value release-audit.json selected_diff_truncated true
|
||||
|
||||
finish_test
|
||||
pass "test_build_dossier_groups_and_truncates_diff"
|
||||
}
|
||||
|
||||
test_artifact_manifest_validates_expected_release_jars() {
|
||||
echo "Running test_artifact_manifest_validates_expected_release_jars"
|
||||
run_test
|
||||
|
||||
version=1.2.3
|
||||
for file in \
|
||||
"ta4j-core/target/ta4j-core-${version}.jar" \
|
||||
"ta4j-core/target/ta4j-core-${version}-sources.jar" \
|
||||
"ta4j-core/target/ta4j-core-${version}-javadoc.jar" \
|
||||
"ta4j-core/target/ta4j-core-${version}-tests.jar" \
|
||||
"ta4j-examples/target/ta4j-examples-${version}.jar" \
|
||||
"ta4j-examples/target/ta4j-examples-${version}-sources.jar" \
|
||||
"ta4j-examples/target/ta4j-examples-${version}-javadoc.jar"; do
|
||||
mkdir -p "$(dirname "$file")"
|
||||
: > "$file"
|
||||
done
|
||||
|
||||
"$PYTHON" "$SCRIPT" artifact-manifest --version "$version" --output artifact-manifest.txt --strict
|
||||
expect_file_contains artifact-manifest.txt "Missing release artifacts:" "manifest should include missing section"
|
||||
expect_file_contains artifact-manifest.txt "- (none)" "manifest should report no missing artifacts"
|
||||
|
||||
: > "ta4j-core/target/unexpected.jar"
|
||||
if "$PYTHON" "$SCRIPT" artifact-manifest --version "$version" --output artifact-manifest.txt --strict >manifest.log 2>&1; then
|
||||
fail "strict artifact manifest should reject unexpected jars"
|
||||
fi
|
||||
expect_file_contains manifest.log "Unexpected target jars" "strict failure should name unexpected jars"
|
||||
|
||||
finish_test
|
||||
pass "test_artifact_manifest_validates_expected_release_jars"
|
||||
}
|
||||
|
||||
test_javadoc_warning_baseline_rejects_new_warnings() {
|
||||
echo "Running test_javadoc_warning_baseline_rejects_new_warnings"
|
||||
run_test
|
||||
|
||||
cat > baseline.txt <<'EOF'
|
||||
ta4j-core/src/main/java/org/ta4j/core/Foo.java: warning: no @param for value
|
||||
EOF
|
||||
cat > release.log <<'EOF'
|
||||
[WARNING] /home/runner/work/ta4j/ta4j/ta4j-core/src/main/java/org/ta4j/core/Foo.java: warning: no @param for value
|
||||
[WARNING] /home/runner/work/ta4j/ta4j/ta4j-core/src/test/java/org/ta4j/core/BaseTradingRecordTest.java:[61,15] recordFill(org.ta4j.core.Trade) has been deprecated
|
||||
EOF
|
||||
|
||||
"$PYTHON" "$SCRIPT" javadoc-warnings \
|
||||
--baseline baseline.txt \
|
||||
--output javadoc-warnings.txt \
|
||||
--github-output outputs.txt \
|
||||
--fail-on-new \
|
||||
release.log
|
||||
expect_file_contains outputs.txt "javadoc_warning_new_count=0" "baseline match should not report new warnings"
|
||||
expect_file_contains javadoc-warnings.txt "current_count=1" "compiler warnings should not count as Javadoc warnings"
|
||||
|
||||
cat >> release.log <<'EOF'
|
||||
[WARNING] /home/runner/work/ta4j/ta4j/ta4j-examples/src/main/java/ta4jexamples/FooExample.java: warning: no @return
|
||||
EOF
|
||||
if "$PYTHON" "$SCRIPT" javadoc-warnings \
|
||||
--baseline baseline.txt \
|
||||
--output javadoc-warnings.txt \
|
||||
--fail-on-new \
|
||||
release.log >javadoc.log 2>&1; then
|
||||
fail "new Javadoc warning should fail the baseline check"
|
||||
fi
|
||||
expect_file_contains javadoc.log "New Javadoc warning" "baseline failure should name new warning"
|
||||
|
||||
finish_test
|
||||
pass "test_javadoc_warning_baseline_rejects_new_warnings"
|
||||
}
|
||||
|
||||
test_catalog_preflight_accepts_configured_model
|
||||
test_catalog_preflight_rejects_missing_model
|
||||
test_parse_decision_normalizes_major_and_invalid_json
|
||||
test_build_dossier_groups_and_truncates_diff
|
||||
test_artifact_manifest_validates_expected_release_jars
|
||||
test_javadoc_warning_baseline_rejects_new_warnings
|
||||
|
||||
echo
|
||||
echo "All release helper tests passed."
|
||||
+377
@@ -0,0 +1,377 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
WORKFLOWS="$ROOT/.github/workflows"
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle' in ${file#"$ROOT"/})"
|
||||
fi
|
||||
}
|
||||
|
||||
line_of() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local line
|
||||
line="$(grep -nFm1 -- "$needle" "$file" || true)"
|
||||
line="${line%%:*}"
|
||||
if [[ -z "$line" ]]; then
|
||||
fail "missing expected workflow line '$needle' in ${file#"$ROOT"/}"
|
||||
fi
|
||||
printf '%s\n' "$line"
|
||||
}
|
||||
|
||||
input_section() {
|
||||
local file="$1"
|
||||
local input="$2"
|
||||
awk -v needle=" ${input}:" '
|
||||
index($0, needle) { active = 1 }
|
||||
active { print }
|
||||
active && index($0, "type: boolean") { exit }
|
||||
' "$file"
|
||||
}
|
||||
|
||||
workflow_section() {
|
||||
local file="$1"
|
||||
local start="$2"
|
||||
local end="$3"
|
||||
awk -v start="$start" -v end="$end" '
|
||||
index($0, start) { active = 1 }
|
||||
active { print }
|
||||
active && index($0, end) { exit }
|
||||
' "$file"
|
||||
}
|
||||
|
||||
expect_section_contains() {
|
||||
local section="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" <<<"$section"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
assert_no_github_script_injected_binding_redeclarations() {
|
||||
local workflow="$1"
|
||||
awk -v file="${workflow#"$ROOT"/}" '
|
||||
/uses: actions\/github-script@/ {
|
||||
pending_script = 1
|
||||
in_script = 0
|
||||
next
|
||||
}
|
||||
pending_script && /^[[:space:]]*script: \|/ {
|
||||
in_script = 1
|
||||
next
|
||||
}
|
||||
in_script && /^[[:space:]]{6}- name:/ {
|
||||
pending_script = 0
|
||||
in_script = 0
|
||||
}
|
||||
in_script && /^[[:space:]]*(const|let|var)[[:space:]]+(core|github|context|glob|io|exec)[[:space:]]*=/ {
|
||||
printf("[FAIL] %s redeclares actions/github-script injected binding at line %d: %s\n", file, NR, $0) > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
' "$workflow"
|
||||
}
|
||||
|
||||
test_maven_workflow_jobs_setup_jdk25_before_maven() {
|
||||
echo "Running test_maven_workflow_jobs_setup_jdk25_before_maven"
|
||||
|
||||
local workflow
|
||||
for workflow in "$WORKFLOWS"/*.yml; do
|
||||
if ! grep -Eq '(^|[[:space:]"({;])xvfb-run[[:space:]]+mvn[[:space:]-]|(^|[[:space:]"({;])mvn[[:space:]-]' "$workflow"; then
|
||||
continue
|
||||
fi
|
||||
|
||||
awk -v file="${workflow#"$ROOT"/}" '
|
||||
/^jobs:/ { in_jobs = 1; next }
|
||||
in_jobs && /^ [A-Za-z0-9_-]+:$/ {
|
||||
job = $1
|
||||
sub(/:$/, "", job)
|
||||
setup = 0
|
||||
temurin = 0
|
||||
jdk25 = 0
|
||||
next
|
||||
}
|
||||
/^[^[:space:]]/ && $0 !~ /^jobs:/ {
|
||||
in_jobs = 0
|
||||
}
|
||||
/uses: actions\/setup-java@v5/ {
|
||||
setup = 1
|
||||
temurin = 0
|
||||
jdk25 = 0
|
||||
}
|
||||
setup && /distribution: temurin/ { temurin = 1 }
|
||||
setup && /java-version: 25/ { jdk25 = 1 }
|
||||
/^[[:space:]]*#/ { next }
|
||||
/(^|[[:space:]"({;])xvfb-run[[:space:]]+mvn[[:space:]-]|(^|[[:space:]"({;])mvn[[:space:]-]/ {
|
||||
if (!setup || !temurin || !jdk25) {
|
||||
printf("[FAIL] %s job %s runs Maven before Temurin JDK 25 setup at line %d\n", file, job ? job : "(unknown)", NR) > "/dev/stderr"
|
||||
exit 1
|
||||
}
|
||||
}
|
||||
' "$workflow"
|
||||
done
|
||||
|
||||
pass "test_maven_workflow_jobs_setup_jdk25_before_maven"
|
||||
}
|
||||
|
||||
test_mutating_manual_workflows_default_to_dry_run() {
|
||||
echo "Running test_mutating_manual_workflows_default_to_dry_run"
|
||||
|
||||
local workflow
|
||||
for workflow in \
|
||||
release-scheduler.yml \
|
||||
prepare-release.yml \
|
||||
publish-release.yml \
|
||||
github-release.yml \
|
||||
snapshot.yml \
|
||||
release-health.yml; do
|
||||
local file="$WORKFLOWS/$workflow"
|
||||
local section
|
||||
section="$(input_section "$file" dryRun)"
|
||||
expect_section_contains "$section" "default: true" "${workflow} manual dryRun input should default true"
|
||||
expect_section_contains "$section" "type: boolean" "${workflow} manual dryRun input should be typed as boolean"
|
||||
done
|
||||
|
||||
pass "test_mutating_manual_workflows_default_to_dry_run"
|
||||
}
|
||||
|
||||
test_official_triggers_normalize_to_non_dry_run() {
|
||||
echo "Running test_official_triggers_normalize_to_non_dry_run"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/release-scheduler.yml" 'if [ "$event" = "workflow_dispatch" ]; then' \
|
||||
"release scheduler should branch normalization by event"
|
||||
expect_file_contains "$WORKFLOWS/release-scheduler.yml" "raw=\"\${DRY_RUN_INPUT:-true}\"" \
|
||||
"release scheduler manual default should normalize to dry-run"
|
||||
expect_file_contains "$WORKFLOWS/release-scheduler.yml" "dry_run=false" \
|
||||
"release scheduler scheduled runs should normalize to non-dry-run"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" 'if [ "${{ github.event_name }}" = "pull_request" ]; then' \
|
||||
"publish-release should normalize merged release PRs to non-dry-run"
|
||||
expect_file_contains "$WORKFLOWS/github-release.yml" 'if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then' \
|
||||
"github-release should keep tag pushes non-dry-run"
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" 'if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then' \
|
||||
"snapshot should keep master pushes non-dry-run"
|
||||
expect_file_contains "$WORKFLOWS/release-health.yml" 'if [ "${GITHUB_EVENT_NAME}" = "workflow_dispatch" ]; then' \
|
||||
"release-health should keep schedule/push/workflow-run triggers non-dry-run"
|
||||
|
||||
pass "test_official_triggers_normalize_to_non_dry_run"
|
||||
}
|
||||
|
||||
test_downstream_dispatches_explicitly_pass_dry_run() {
|
||||
echo "Running test_downstream_dispatches_explicitly_pass_dry_run"
|
||||
|
||||
local scheduler_dispatch
|
||||
scheduler_dispatch="$(workflow_section "$WORKFLOWS/release-scheduler.yml" 'workflow_id: "prepare-release.yml"' "dryRun")"
|
||||
expect_section_contains "$scheduler_dispatch" "dryRun" \
|
||||
"release scheduler dispatch should pass dryRun to prepare-release"
|
||||
|
||||
local prepare_dispatch
|
||||
prepare_dispatch="$(workflow_section "$WORKFLOWS/prepare-release.yml" 'workflow_id: "publish-release.yml"' "dryRun")"
|
||||
expect_section_contains "$prepare_dispatch" "dryRun" \
|
||||
"prepare-release dispatch should pass dryRun to publish-release"
|
||||
|
||||
local publish_dispatch
|
||||
publish_dispatch="$(workflow_section "$WORKFLOWS/publish-release.yml" 'workflow_id: "snapshot.yml"' 'dryRun: "false"')"
|
||||
expect_section_contains "$publish_dispatch" 'dryRun: "false"' \
|
||||
"publish-release should explicitly dispatch snapshot publication as non-dry-run"
|
||||
|
||||
pass "test_downstream_dispatches_explicitly_pass_dry_run"
|
||||
}
|
||||
|
||||
test_mutating_steps_remain_dry_run_gated() {
|
||||
echo "Running test_mutating_steps_remain_dry_run_gated"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/release-scheduler.yml" "if: always() && needs.analyze.result != 'skipped' && needs.analyze.outputs.dryRun != 'true'" \
|
||||
"release scheduler discussion mutation should skip dry-run and skipped scheduled runs"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/prepare-release.yml" "if: steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"prepare-release mutations should be dry-run gated"
|
||||
expect_file_contains "$WORKFLOWS/prepare-release.yml" "Create or update removal-ready deprecation issues" \
|
||||
"prepare-release should still have managed issue sync"
|
||||
expect_file_contains "$WORKFLOWS/prepare-release.yml" "Create or update release PR" \
|
||||
"prepare-release should still have release PR mutation"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "Deploy Release to Maven Central" \
|
||||
"publish-release should still have Maven Central deployment"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "if: steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"publish-release mutations should be dry-run gated"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "if: always() && steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"publish-release discussion mutation should skip dry-run"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/github-release.yml" "Create GitHub Release" \
|
||||
"github-release should still publish GitHub releases"
|
||||
expect_file_contains "$WORKFLOWS/github-release.yml" "if: steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"github-release publication should skip dry-run"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" "Deploy Snapshot" \
|
||||
"snapshot workflow should still publish snapshots"
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" "if: steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"snapshot deployment and publishing-secret steps should skip dry-run"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/release-health.yml" "if: always() && steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"release-health discussion mutation should skip dry-run"
|
||||
|
||||
pass "test_mutating_steps_remain_dry_run_gated"
|
||||
}
|
||||
|
||||
test_dry_run_summaries_and_audits_show_rerun_guidance() {
|
||||
echo "Running test_dry_run_summaries_and_audits_show_rerun_guidance"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/release-scheduler.yml" "release-scheduler-mutation-plan.txt" \
|
||||
"release scheduler audit artifact should include the dry-run mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/prepare-release.yml" '"mutationPlan"' \
|
||||
"prepare-release audit JSON should include mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" '"mutationPlan"' \
|
||||
"publish-release audit JSON should include mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/github-release.yml" '"mutationPlan"' \
|
||||
"github-release audit JSON should include mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" '"snapshotVersion"' \
|
||||
"snapshot audit JSON should include computed snapshot version"
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" '"mutationPlan"' \
|
||||
"snapshot audit JSON should include mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/release-health.yml" '"mutationPlan"' \
|
||||
"release-health audit JSON should include mutation plan"
|
||||
expect_file_contains "$WORKFLOWS/prepare-release.yml" "rerun guidance" \
|
||||
"prepare-release summary should include rerun guidance"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "rerun guidance" \
|
||||
"publish-release summary should include rerun guidance"
|
||||
expect_file_contains "$WORKFLOWS/snapshot.yml" "rerun guidance" \
|
||||
"snapshot summary should include rerun guidance"
|
||||
|
||||
pass "test_dry_run_summaries_and_audits_show_rerun_guidance"
|
||||
}
|
||||
|
||||
test_snapshot_and_health_manual_dry_runs_do_not_mutate() {
|
||||
echo "Running test_snapshot_and_health_manual_dry_runs_do_not_mutate"
|
||||
|
||||
local deploy_section
|
||||
deploy_section="$(workflow_section "$WORKFLOWS/snapshot.yml" "Deploy Snapshot" "Snapshot publication summary")"
|
||||
expect_section_contains "$deploy_section" "if: steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"snapshot deployment should skip dry-run"
|
||||
|
||||
local health_post_section
|
||||
health_post_section="$(workflow_section "$WORKFLOWS/release-health.yml" "Post to Release Scheduler discussion" "with:")"
|
||||
expect_section_contains "$health_post_section" "if: always() && steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"release-health discussion post should skip dry-run"
|
||||
|
||||
pass "test_snapshot_and_health_manual_dry_runs_do_not_mutate"
|
||||
}
|
||||
|
||||
test_line_of_reports_missing_needles_cleanly() {
|
||||
echo "Running test_line_of_reports_missing_needles_cleanly"
|
||||
|
||||
local tmp
|
||||
local output
|
||||
tmp="$(mktemp "${TMPDIR:-/tmp}/release-workflow-safety.XXXXXX")"
|
||||
printf 'present\n' > "$tmp"
|
||||
|
||||
if output="$( (line_of "$tmp" "missing") 2>&1 )"; then
|
||||
rm -f "$tmp"
|
||||
fail "line_of should fail when the workflow line is missing"
|
||||
fi
|
||||
|
||||
rm -f "$tmp"
|
||||
expect_section_contains "$output" "missing expected workflow line 'missing'" \
|
||||
"line_of should surface the explicit missing-line failure message"
|
||||
|
||||
pass "test_line_of_reports_missing_needles_cleanly"
|
||||
}
|
||||
|
||||
test_publish_release_existing_tag_only_fails_real_runs() {
|
||||
echo "Running test_publish_release_existing_tag_only_fails_real_runs"
|
||||
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" \
|
||||
"if: steps.check_tag.outputs.exists == 'true' && steps.dry_run.outputs.dryRun != 'true'" \
|
||||
"publish-release should fail existing tags only during real runs"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "Report existing tag in dry-run mode" \
|
||||
"publish-release should keep a dry-run audit path for existing tags"
|
||||
expect_file_contains "$WORKFLOWS/publish-release.yml" "audit:existing_tag_dry_run" \
|
||||
"publish-release dry-run tag detection should emit an audit line"
|
||||
|
||||
pass "test_publish_release_existing_tag_only_fails_real_runs"
|
||||
}
|
||||
|
||||
test_github_release_preserves_workflow_support_checkout() {
|
||||
echo "Running test_github_release_preserves_workflow_support_checkout"
|
||||
|
||||
local full_checkout_line
|
||||
local support_checkout_line
|
||||
local manifest_line
|
||||
full_checkout_line="$(line_of "$WORKFLOWS/github-release.yml" "Checkout full history")"
|
||||
support_checkout_line="$(line_of "$WORKFLOWS/github-release.yml" "Checkout workflow support files")"
|
||||
manifest_line="$(line_of "$WORKFLOWS/github-release.yml" "workflow-support/scripts/release/release_helpers.py artifact-manifest")"
|
||||
|
||||
if (( support_checkout_line <= full_checkout_line )); then
|
||||
fail "github-release should checkout workflow support after the release tag checkout"
|
||||
fi
|
||||
if (( manifest_line <= support_checkout_line )); then
|
||||
fail "github-release should validate artifacts after workflow support checkout"
|
||||
fi
|
||||
expect_file_contains "$WORKFLOWS/github-release.yml" "path: workflow-support" \
|
||||
"github-release should keep support helpers outside the release tag checkout"
|
||||
|
||||
pass "test_github_release_preserves_workflow_support_checkout"
|
||||
}
|
||||
|
||||
test_github_script_blocks_do_not_redeclare_injected_bindings() {
|
||||
echo "Running test_github_script_blocks_do_not_redeclare_injected_bindings"
|
||||
|
||||
local workflow
|
||||
for workflow in "$WORKFLOWS"/*.yml; do
|
||||
assert_no_github_script_injected_binding_redeclarations "$workflow"
|
||||
done
|
||||
|
||||
pass "test_github_script_blocks_do_not_redeclare_injected_bindings"
|
||||
}
|
||||
|
||||
test_github_script_binding_scan_rejects_bad_fixture() {
|
||||
echo "Running test_github_script_binding_scan_rejects_bad_fixture"
|
||||
|
||||
local tmp
|
||||
local output
|
||||
tmp="$(mktemp "${TMPDIR:-/tmp}/release-workflow-safety.XXXXXX")"
|
||||
cat > "$tmp" <<'YAML'
|
||||
name: Bad fixture
|
||||
jobs:
|
||||
bad:
|
||||
steps:
|
||||
- name: Bad GitHub Script
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const core = require("@actions/core");
|
||||
YAML
|
||||
|
||||
if output="$(assert_no_github_script_injected_binding_redeclarations "$tmp" 2>&1)"; then
|
||||
rm -f "$tmp"
|
||||
fail "github-script injected binding scan should fail on a core redeclaration"
|
||||
fi
|
||||
|
||||
rm -f "$tmp"
|
||||
expect_section_contains "$output" "redeclares actions/github-script injected binding" \
|
||||
"github-script injected binding scan should report the redeclaration"
|
||||
|
||||
pass "test_github_script_binding_scan_rejects_bad_fixture"
|
||||
}
|
||||
|
||||
test_maven_workflow_jobs_setup_jdk25_before_maven
|
||||
test_mutating_manual_workflows_default_to_dry_run
|
||||
test_official_triggers_normalize_to_non_dry_run
|
||||
test_downstream_dispatches_explicitly_pass_dry_run
|
||||
test_mutating_steps_remain_dry_run_gated
|
||||
test_dry_run_summaries_and_audits_show_rerun_guidance
|
||||
test_snapshot_and_health_manual_dry_runs_do_not_mutate
|
||||
test_line_of_reports_missing_needles_cleanly
|
||||
test_publish_release_existing_tag_only_fails_real_runs
|
||||
test_github_release_preserves_workflow_support_checkout
|
||||
test_github_script_blocks_do_not_redeclare_injected_bindings
|
||||
test_github_script_binding_scan_rejects_bad_fixture
|
||||
+218
@@ -0,0 +1,218 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# test_resolve_release_tags.sh
|
||||
#
|
||||
# Validates release tag baseline selection and first-parent diagnostics.
|
||||
# =============================================================================
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/resolve-release-tags.sh"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_output_value() {
|
||||
local output="$1"
|
||||
local key="$2"
|
||||
local expected="$3"
|
||||
local actual=""
|
||||
|
||||
actual="$(printf '%s\n' "$output" | awk -F= -v key="$key" '$1 == key { print substr($0, length($1) + 2); exit }')"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
fail "expected ${key}=${expected}, got ${actual:-<missing>}"
|
||||
fi
|
||||
}
|
||||
|
||||
run_test() {
|
||||
TMP="$(mktemp -d)"
|
||||
mkdir -p "$TMP/scripts"
|
||||
cp "$SCRIPT" "$TMP/scripts/resolve-release-tags.sh"
|
||||
chmod +x "$TMP/scripts/resolve-release-tags.sh"
|
||||
|
||||
pushd "$TMP" >/dev/null || exit 1
|
||||
git init -q -b master
|
||||
git config user.name "Test User"
|
||||
git config user.email "test@example.com"
|
||||
}
|
||||
|
||||
finish_test() {
|
||||
popd >/dev/null || exit 1
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
commit_file() {
|
||||
local path="$1"
|
||||
local content="$2"
|
||||
local message="$3"
|
||||
local date="$4"
|
||||
|
||||
mkdir -p "$(dirname "$path")"
|
||||
printf '%s\n' "$content" > "$path"
|
||||
git add "$path"
|
||||
GIT_AUTHOR_DATE="$date" GIT_COMMITTER_DATE="$date" git commit -q -m "$message"
|
||||
}
|
||||
|
||||
tag_release() {
|
||||
local tag="$1"
|
||||
local date="$2"
|
||||
|
||||
GIT_COMMITTER_DATE="$date" git tag -a "$tag" -m "Release $tag"
|
||||
}
|
||||
|
||||
merge_branch() {
|
||||
local branch="$1"
|
||||
local message="$2"
|
||||
local date="$3"
|
||||
|
||||
GIT_AUTHOR_DATE="$date" GIT_COMMITTER_DATE="$date" git merge --no-ff -m "$message" "$branch" >/dev/null
|
||||
}
|
||||
|
||||
test_returns_none_when_no_release_tags_exist() {
|
||||
echo "Running test_returns_none_when_no_release_tags_exist"
|
||||
run_test
|
||||
|
||||
commit_file README.md "fixture" "Initial commit" "2024-01-01T00:00:00Z"
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh HEAD)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "none"
|
||||
expect_output_value "$out" "last_reachable_tag" "none"
|
||||
expect_output_value "$out" "last_first_parent_tag" "none"
|
||||
expect_output_value "$out" "latest_tag_reachable" "n/a"
|
||||
|
||||
finish_test
|
||||
pass "test_returns_none_when_no_release_tags_exist"
|
||||
}
|
||||
|
||||
test_prefers_reachable_tag_for_release_baseline() {
|
||||
echo "Running test_prefers_reachable_tag_for_release_baseline"
|
||||
run_test
|
||||
|
||||
commit_file README.md "base" "Initial commit" "2024-01-01T00:00:00Z"
|
||||
tag_release "0.21.0" "2024-01-02T00:00:00Z"
|
||||
commit_file main.txt "after 0.21.0" "Master commit after 0.21.0" "2024-01-03T00:00:00Z"
|
||||
|
||||
git checkout -q -b release/0.22.4
|
||||
commit_file release.txt "release payload" "Release 0.22.4" "2024-01-04T00:00:00Z"
|
||||
tag_release "0.22.4" "2024-01-05T00:00:00Z"
|
||||
|
||||
git checkout -q master
|
||||
merge_branch "release/0.22.4" "Merge release/0.22.4" "2024-01-06T00:00:00Z"
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh HEAD)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "0.22.4"
|
||||
expect_output_value "$out" "last_reachable_tag" "0.22.4"
|
||||
expect_output_value "$out" "last_first_parent_tag" "0.21.0"
|
||||
expect_output_value "$out" "latest_tag_reachable" "true"
|
||||
|
||||
finish_test
|
||||
pass "test_prefers_reachable_tag_for_release_baseline"
|
||||
}
|
||||
|
||||
test_reports_unreachable_latest_tag() {
|
||||
echo "Running test_reports_unreachable_latest_tag"
|
||||
run_test
|
||||
|
||||
commit_file README.md "base" "Initial commit" "2024-02-01T00:00:00Z"
|
||||
tag_release "0.22.3" "2024-02-02T00:00:00Z"
|
||||
commit_file main.txt "master work" "Master commit" "2024-02-03T00:00:00Z"
|
||||
|
||||
git checkout -q -b release/0.22.4
|
||||
commit_file release.txt "detached release" "Detached release commit" "2024-02-04T00:00:00Z"
|
||||
tag_release "0.22.4" "2024-02-05T00:00:00Z"
|
||||
|
||||
git checkout -q master
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh HEAD)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "0.22.4"
|
||||
expect_output_value "$out" "last_reachable_tag" "0.22.3"
|
||||
expect_output_value "$out" "last_first_parent_tag" "0.22.3"
|
||||
expect_output_value "$out" "latest_tag_reachable" "false"
|
||||
|
||||
finish_test
|
||||
pass "test_reports_unreachable_latest_tag"
|
||||
}
|
||||
|
||||
test_falls_back_to_v_prefixed_tags() {
|
||||
echo "Running test_falls_back_to_v_prefixed_tags"
|
||||
run_test
|
||||
|
||||
commit_file README.md "base" "Initial commit" "2024-03-01T00:00:00Z"
|
||||
tag_release "v1.2.3" "2024-03-02T00:00:00Z"
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh HEAD)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "v1.2.3"
|
||||
expect_output_value "$out" "last_reachable_tag" "v1.2.3"
|
||||
expect_output_value "$out" "last_first_parent_tag" "v1.2.3"
|
||||
expect_output_value "$out" "latest_tag_reachable" "true"
|
||||
|
||||
finish_test
|
||||
pass "test_falls_back_to_v_prefixed_tags"
|
||||
}
|
||||
|
||||
test_prefers_numeric_tags_over_v_prefixed_tags() {
|
||||
echo "Running test_prefers_numeric_tags_over_v_prefixed_tags"
|
||||
run_test
|
||||
|
||||
commit_file README.md "base" "Initial commit" "2024-04-01T00:00:00Z"
|
||||
tag_release "1.2.3" "2024-04-02T00:00:00Z"
|
||||
commit_file main.txt "master work" "Master commit" "2024-04-03T00:00:00Z"
|
||||
tag_release "v9.9.9" "2024-04-04T00:00:00Z"
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh HEAD)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "1.2.3"
|
||||
expect_output_value "$out" "last_reachable_tag" "1.2.3"
|
||||
expect_output_value "$out" "last_first_parent_tag" "1.2.3"
|
||||
expect_output_value "$out" "latest_tag_reachable" "true"
|
||||
|
||||
finish_test
|
||||
pass "test_prefers_numeric_tags_over_v_prefixed_tags"
|
||||
}
|
||||
|
||||
test_returns_head_values_when_target_ref_is_missing() {
|
||||
echo "Running test_returns_head_values_when_target_ref_is_missing"
|
||||
run_test
|
||||
|
||||
commit_file README.md "base" "Initial commit" "2024-05-01T00:00:00Z"
|
||||
tag_release "0.30.0" "2024-05-02T00:00:00Z"
|
||||
|
||||
local out
|
||||
out="$(scripts/resolve-release-tags.sh refs/remotes/origin/master)"
|
||||
|
||||
expect_output_value "$out" "latest_tag" "0.30.0"
|
||||
expect_output_value "$out" "last_reachable_tag" "0.30.0"
|
||||
expect_output_value "$out" "last_first_parent_tag" "0.30.0"
|
||||
expect_output_value "$out" "latest_tag_reachable" "true"
|
||||
|
||||
finish_test
|
||||
pass "test_returns_head_values_when_target_ref_is_missing"
|
||||
}
|
||||
|
||||
test_returns_none_when_no_release_tags_exist
|
||||
test_prefers_reachable_tag_for_release_baseline
|
||||
test_reports_unreachable_latest_tag
|
||||
test_falls_back_to_v_prefixed_tags
|
||||
test_prefers_numeric_tags_over_v_prefixed_tags
|
||||
test_returns_head_values_when_target_ref_is_missing
|
||||
|
||||
echo
|
||||
echo "All resolve-release-tags tests passed."
|
||||
+222
@@ -0,0 +1,222 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/release/release_helpers.py"
|
||||
PYTHON="${PYTHON:-python3}"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
new_temp_dir() {
|
||||
mktemp -d "${TMPDIR:-/tmp}/snapshot-publication.XXXXXX"
|
||||
}
|
||||
|
||||
expect_output_value() {
|
||||
local file="$1"
|
||||
local key="$2"
|
||||
local expected="$3"
|
||||
local actual=""
|
||||
|
||||
actual="$(awk -F= -v key="$key" '$1 == key { print substr($0, length($1) + 2); exit }' "$file")"
|
||||
if [[ "$actual" != "$expected" ]]; then
|
||||
fail "expected ${key}=${expected}, got ${actual:-<missing>}"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local expected="$2"
|
||||
|
||||
if ! grep -Fq -- "$expected" "$file"; then
|
||||
fail "expected ${file} to contain: ${expected}"
|
||||
fi
|
||||
}
|
||||
|
||||
write_metadata_fixture() {
|
||||
local path="$1"
|
||||
local latest="$2"
|
||||
local versions="$3"
|
||||
local last_updated="$4"
|
||||
|
||||
cat > "$path" <<EOF
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<metadata>
|
||||
<groupId>org.ta4j</groupId>
|
||||
<artifactId>ta4j-parent</artifactId>
|
||||
<versioning>
|
||||
<latest>${latest}</latest>
|
||||
<versions>
|
||||
${versions}
|
||||
</versions>
|
||||
<lastUpdated>${last_updated}</lastUpdated>
|
||||
</versioning>
|
||||
</metadata>
|
||||
EOF
|
||||
}
|
||||
|
||||
test_snapshot_version_present() {
|
||||
echo "Running test_snapshot_version_present"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local metadata_file="$TMP/metadata.xml"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication.json"
|
||||
write_metadata_fixture "$metadata_file" "0.22.7-SNAPSHOT" $' <version>0.22.6-SNAPSHOT</version>\n <version>0.22.7-SNAPSHOT</version>' "20260506001534"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication \
|
||||
--version "0.22.7-SNAPSHOT" \
|
||||
--metadata-file "$metadata_file" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication" "true"
|
||||
expect_output_value "$github_output" "snapshot_publication_latest" "0.22.7-SNAPSHOT"
|
||||
expect_output_value "$github_output" "snapshot_publication_last_updated" "20260506001534"
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_snapshot_version_present"
|
||||
}
|
||||
|
||||
test_snapshot_version_missing() {
|
||||
echo "Running test_snapshot_version_missing"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local metadata_file="$TMP/metadata.xml"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication.json"
|
||||
write_metadata_fixture "$metadata_file" "0.22.7-SNAPSHOT" $' <version>0.22.6-SNAPSHOT</version>\n <version>0.22.7-SNAPSHOT</version>' "20260506001534"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication \
|
||||
--version "0.22.8-SNAPSHOT" \
|
||||
--metadata-file "$metadata_file" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication" "false"
|
||||
expect_output_value "$github_output" "snapshot_publication_latest" "0.22.7-SNAPSHOT"
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_snapshot_version_missing"
|
||||
}
|
||||
|
||||
test_non_snapshot_version_returns_na() {
|
||||
echo "Running test_non_snapshot_version_returns_na"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local metadata_file="$TMP/metadata.xml"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication.json"
|
||||
write_metadata_fixture "$metadata_file" "0.22.7-SNAPSHOT" $' <version>0.22.7-SNAPSHOT</version>' "20260506001534"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication \
|
||||
--version "0.22.7" \
|
||||
--metadata-file "$metadata_file" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication" "n/a"
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_non_snapshot_version_returns_na"
|
||||
}
|
||||
|
||||
test_malformed_metadata_returns_unknown() {
|
||||
echo "Running test_malformed_metadata_returns_unknown"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local metadata_file="$TMP/metadata.xml"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication.json"
|
||||
printf '%s\n' '<metadata><versioning>' > "$metadata_file"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication \
|
||||
--version "0.22.7-SNAPSHOT" \
|
||||
--metadata-file "$metadata_file" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication" "unknown"
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_malformed_metadata_returns_unknown"
|
||||
}
|
||||
|
||||
test_non_https_metadata_url_returns_unknown() {
|
||||
echo "Running test_non_https_metadata_url_returns_unknown"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication.json"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication \
|
||||
--version "0.22.7-SNAPSHOT" \
|
||||
--metadata-url "file:///tmp/snapshot-publication.xml" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication" "unknown"
|
||||
expect_file_contains "$output_file" "\"error\": \"--metadata-url must use https\""
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_non_https_metadata_url_returns_unknown"
|
||||
}
|
||||
|
||||
test_snapshot_publication_policy_defers_push_runs() {
|
||||
echo "Running test_snapshot_publication_policy_defers_push_runs"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication-policy.json"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication-policy \
|
||||
--event-name "push" \
|
||||
--workflow-name "" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication_enforced" "false"
|
||||
expect_output_value "$github_output" "snapshot_publication_pending_reason" "awaiting snapshot workflow completion before treating snapshot metadata drift as authoritative"
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_snapshot_publication_policy_defers_push_runs"
|
||||
}
|
||||
|
||||
test_snapshot_publication_policy_enforces_snapshot_workflow_runs() {
|
||||
echo "Running test_snapshot_publication_policy_enforces_snapshot_workflow_runs"
|
||||
|
||||
TMP="$(new_temp_dir)"
|
||||
local github_output="$TMP/github-output.txt"
|
||||
local output_file="$TMP/snapshot-publication-policy.json"
|
||||
|
||||
"$PYTHON" "$SCRIPT" snapshot-publication-policy \
|
||||
--event-name "workflow_run" \
|
||||
--workflow-name "Publish Snapshot to Maven Central" \
|
||||
--output "$output_file" \
|
||||
--github-output "$github_output" >/dev/null
|
||||
|
||||
expect_output_value "$github_output" "snapshot_publication_enforced" "true"
|
||||
expect_output_value "$github_output" "snapshot_publication_pending_reason" ""
|
||||
|
||||
rm -rf "$TMP"
|
||||
pass "test_snapshot_publication_policy_enforces_snapshot_workflow_runs"
|
||||
}
|
||||
|
||||
test_snapshot_version_present
|
||||
test_snapshot_version_missing
|
||||
test_non_snapshot_version_returns_na
|
||||
test_malformed_metadata_returns_unknown
|
||||
test_non_https_metadata_url_returns_unknown
|
||||
test_snapshot_publication_policy_defers_push_runs
|
||||
test_snapshot_publication_policy_enforces_snapshot_workflow_runs
|
||||
|
||||
echo
|
||||
echo "All snapshot publication tests passed."
|
||||
+153
@@ -0,0 +1,153 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# =============================================================================
|
||||
# test_validate_central_metadata.sh
|
||||
#
|
||||
# Validates behavior of scripts/validate-central-metadata.sh
|
||||
# =============================================================================
|
||||
|
||||
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
||||
SCRIPT="$ROOT/scripts/validate-central-metadata.sh"
|
||||
|
||||
cleanup() {
|
||||
if [[ -n "${TMP:-}" && -d "$TMP" ]]; then
|
||||
rm -rf "$TMP"
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
fail() { echo "[FAIL] $1" >&2; exit 1; }
|
||||
pass() { echo "[PASS] $1"; }
|
||||
|
||||
expect_contains() {
|
||||
local haystack="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" <<<"$haystack"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
expect_file_contains() {
|
||||
local file="$1"
|
||||
local needle="$2"
|
||||
local msg="$3"
|
||||
if ! grep -Fq -- "$needle" "$file"; then
|
||||
fail "$msg (missing: '$needle')"
|
||||
fi
|
||||
}
|
||||
|
||||
create_fixture() {
|
||||
local include_developers="$1"
|
||||
|
||||
mkdir -p ta4j-core ta4j-examples scripts
|
||||
cp "$SCRIPT" scripts/validate-central-metadata.sh
|
||||
chmod +x scripts/validate-central-metadata.sh
|
||||
|
||||
cat > pom.xml <<EOF
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<groupId>org.ta4j</groupId>
|
||||
<artifactId>ta4j-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
<packaging>pom</packaging>
|
||||
<name>Ta4j Parent</name>
|
||||
<description>Fixture description</description>
|
||||
<url>https://github.com/ta4j/ta4j</url>
|
||||
<licenses>
|
||||
<license>
|
||||
<name>MIT License</name>
|
||||
</license>
|
||||
</licenses>
|
||||
<scm>
|
||||
<connection>scm:git:git://github.com/ta4j/ta4j.git</connection>
|
||||
<url>https://github.com/ta4j/ta4j</url>
|
||||
</scm>
|
||||
EOF
|
||||
|
||||
if [[ "$include_developers" == "true" ]]; then
|
||||
cat >> pom.xml <<'EOF'
|
||||
<developers>
|
||||
<developer>
|
||||
<id>maintainer</id>
|
||||
<name>Maintainer</name>
|
||||
</developer>
|
||||
</developers>
|
||||
EOF
|
||||
fi
|
||||
|
||||
cat >> pom.xml <<'EOF'
|
||||
<modules>
|
||||
<module>ta4j-core</module>
|
||||
<module>ta4j-examples</module>
|
||||
</modules>
|
||||
</project>
|
||||
EOF
|
||||
|
||||
for module in ta4j-core ta4j-examples; do
|
||||
cat > "${module}/pom.xml" <<EOF
|
||||
<project xmlns="http://maven.apache.org/POM/4.0.0"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
|
||||
<modelVersion>4.0.0</modelVersion>
|
||||
<parent>
|
||||
<groupId>org.ta4j</groupId>
|
||||
<artifactId>ta4j-parent</artifactId>
|
||||
<version>1.0.0</version>
|
||||
</parent>
|
||||
<artifactId>${module}</artifactId>
|
||||
</project>
|
||||
EOF
|
||||
done
|
||||
}
|
||||
|
||||
run_test() {
|
||||
TMP="$(mktemp -d)"
|
||||
pushd "$TMP" >/dev/null || exit 1
|
||||
}
|
||||
|
||||
finish_test() {
|
||||
popd >/dev/null || exit 1
|
||||
rm -rf "$TMP"
|
||||
}
|
||||
|
||||
test_passes_with_complete_metadata() {
|
||||
echo "Running test_passes_with_complete_metadata"
|
||||
run_test
|
||||
create_fixture "true"
|
||||
|
||||
local out
|
||||
out="$(scripts/validate-central-metadata.sh)"
|
||||
expect_contains "$out" "validation passed" "validator should pass for complete metadata"
|
||||
|
||||
finish_test
|
||||
pass "test_passes_with_complete_metadata"
|
||||
}
|
||||
|
||||
test_fails_when_developers_missing() {
|
||||
echo "Running test_fails_when_developers_missing"
|
||||
run_test
|
||||
create_fixture "false"
|
||||
|
||||
local out_file="validator.log"
|
||||
if scripts/validate-central-metadata.sh >"$out_file" 2>&1; then
|
||||
fail "validator should fail when developers metadata is missing"
|
||||
fi
|
||||
|
||||
expect_file_contains "$out_file" "developers[0].id" "should mention missing developer id"
|
||||
expect_file_contains "$out_file" "developers[0].name" "should mention missing developer name"
|
||||
expect_file_contains "$out_file" "validation failed" "should print summary failure"
|
||||
|
||||
finish_test
|
||||
pass "test_fails_when_developers_missing"
|
||||
}
|
||||
|
||||
test_passes_with_complete_metadata
|
||||
test_fails_when_developers_missing
|
||||
|
||||
echo
|
||||
echo "All validate-central-metadata tests passed."
|
||||
Reference in New Issue
Block a user