Files
2026-05-23 15:11:48 +09:00

306 lines
9.8 KiB
Bash

#!/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."