111 lines
3.8 KiB
Bash
Executable File
111 lines
3.8 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
|
BACKEND_DIR="$ROOT_DIR/backend"
|
|
COVERAGE_FILE="$BACKEND_DIR/coverage.txt"
|
|
MIN_COVERAGE="${CHARON_MIN_COVERAGE:-${CPM_MIN_COVERAGE:-85}}"
|
|
|
|
# Perf asserts are sensitive to -race overhead; loosen defaults for hook runs
|
|
export PERF_MAX_MS_GETSTATUS_P95="${PERF_MAX_MS_GETSTATUS_P95:-25ms}"
|
|
export PERF_MAX_MS_GETSTATUS_P95_PARALLEL="${PERF_MAX_MS_GETSTATUS_P95_PARALLEL:-50ms}"
|
|
export PERF_MAX_MS_LISTDECISIONS_P95="${PERF_MAX_MS_LISTDECISIONS_P95:-75ms}"
|
|
|
|
# trap 'rm -f "$COVERAGE_FILE"' EXIT
|
|
|
|
cd "$BACKEND_DIR"
|
|
|
|
# Packages to exclude from coverage (main packages and infrastructure code)
|
|
# These are entrypoints and initialization code that don't benefit from unit tests
|
|
EXCLUDE_PACKAGES=(
|
|
"github.com/Wikid82/charon/backend/cmd/api"
|
|
"github.com/Wikid82/charon/backend/cmd/seed"
|
|
"github.com/Wikid82/charon/backend/internal/logger"
|
|
"github.com/Wikid82/charon/backend/internal/metrics"
|
|
"github.com/Wikid82/charon/backend/internal/trace"
|
|
"github.com/Wikid82/charon/backend/integration"
|
|
)
|
|
|
|
# Try to run tests to produce coverage file; some toolchains may return a non-zero
|
|
# exit if certain coverage tooling is unavailable (e.g. covdata) while still
|
|
# producing a usable coverage file. Capture the status so we can report real
|
|
# test failures after the coverage check.
|
|
# Note: Using -v for verbose output and -race for race detection
|
|
GO_TEST_STATUS=0
|
|
if ! go test -race -v -mod=readonly -coverprofile="$COVERAGE_FILE" ./...; then
|
|
GO_TEST_STATUS=$?
|
|
fi
|
|
|
|
if [ "$GO_TEST_STATUS" -ne 0 ]; then
|
|
echo "Warning: go test returned non-zero (status ${GO_TEST_STATUS}); checking coverage file presence"
|
|
fi
|
|
|
|
# Filter out excluded packages from coverage file
|
|
if [ -f "$COVERAGE_FILE" ]; then
|
|
echo "Filtering excluded packages from coverage report..."
|
|
FILTERED_COVERAGE="${COVERAGE_FILE}.filtered"
|
|
|
|
# Build sed command with all patterns at once (more efficient than loop)
|
|
SED_PATTERN=""
|
|
for pkg in "${EXCLUDE_PACKAGES[@]}"; do
|
|
if [ -z "$SED_PATTERN" ]; then
|
|
SED_PATTERN="\|^${pkg}|d"
|
|
else
|
|
SED_PATTERN="${SED_PATTERN};\|^${pkg}|d"
|
|
fi
|
|
done
|
|
|
|
# Use non-blocking sed with explicit input/output (avoids -i hang issues)
|
|
timeout 30 sed "$SED_PATTERN" "$COVERAGE_FILE" > "$FILTERED_COVERAGE" || {
|
|
echo "Error: Coverage filtering failed or timed out"
|
|
echo "Using unfiltered coverage file"
|
|
cp "$COVERAGE_FILE" "$FILTERED_COVERAGE"
|
|
}
|
|
|
|
mv "$FILTERED_COVERAGE" "$COVERAGE_FILE"
|
|
echo "Coverage filtering complete"
|
|
fi
|
|
|
|
if [ ! -f "$COVERAGE_FILE" ]; then
|
|
echo "Error: coverage file not generated by go test"
|
|
exit 1
|
|
fi
|
|
|
|
# Generate coverage report once with timeout protection
|
|
COVERAGE_OUTPUT=$(timeout 60 go tool cover -func="$COVERAGE_FILE" 2>&1) || {
|
|
echo "Error: go tool cover failed or timed out after 60 seconds"
|
|
echo "This may indicate corrupted coverage data or memory issues"
|
|
exit 1
|
|
}
|
|
|
|
# Display summary line
|
|
echo "$COVERAGE_OUTPUT" | tail -n 1
|
|
|
|
# Extract total coverage percentage
|
|
TOTAL_LINE=$(echo "$COVERAGE_OUTPUT" | grep total)
|
|
TOTAL_PERCENT=$(echo "$TOTAL_LINE" | awk '{print substr($3, 1, length($3)-1)}')
|
|
|
|
echo "Computed coverage: ${TOTAL_PERCENT}% (minimum required ${MIN_COVERAGE}%)"
|
|
|
|
export TOTAL_PERCENT
|
|
export MIN_COVERAGE
|
|
|
|
python3 - <<'PY'
|
|
import os, sys
|
|
from decimal import Decimal
|
|
|
|
total = Decimal(os.environ['TOTAL_PERCENT'])
|
|
minimum = Decimal(os.environ['MIN_COVERAGE'])
|
|
if total < minimum:
|
|
print(f"Coverage {total}% is below required {minimum}% (set CHARON_MIN_COVERAGE or CPM_MIN_COVERAGE to override)", file=sys.stderr)
|
|
sys.exit(1)
|
|
PY
|
|
|
|
echo "Coverage requirement met"
|
|
|
|
# Bubble up real test failures (after printing coverage info) so pre-commit
|
|
# reflects the actual test status.
|
|
if [ "$GO_TEST_STATUS" -ne 0 ]; then
|
|
exit "$GO_TEST_STATUS"
|
|
fi
|