Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
37 commits
Select commit Hold shift + click to select a range
1ff436c
Test Actions
Mav55 Aug 1, 2025
257301e
Merge branch 'main' of github.com:olympix/lifi-contracts
Mav55 Aug 1, 2025
a93ef4d
Adjust Mutation Test Run
Mav55 Aug 1, 2025
6d9c835
Merge pull request #1 from olympix/feature/test-1
Mav55 Aug 1, 2025
f9bff59
Add manual trigger
Mav55 Aug 1, 2025
06480a4
Merge branch 'main' of github.com:olympix/lifi-contracts
Mav55 Aug 1, 2025
a703e6a
Manual run, check all files
Mav55 Aug 1, 2025
2640407
Manual run, check all files
Mav55 Aug 1, 2025
68ffca6
Fix mutation test action
Mav55 Aug 2, 2025
054a513
Limit how many contracts
Mav55 Aug 2, 2025
85dec0e
Merge branch 'lifinance:main' into main
Mav55 Aug 4, 2025
7e97e4b
Improve manual runner
Mav55 Aug 4, 2025
be26a8f
One file at a time
Mav55 Aug 4, 2025
6aa57f9
Batch the files
Mav55 Aug 4, 2025
e5576c0
Batch the files
Mav55 Aug 4, 2025
651619f
Run 100 files batches
Mav55 Aug 4, 2025
405d5e3
Test mutation testing: fix existing mutant and create new one
Mav55 Aug 4, 2025
41b32e5
Fix mutant #1128: Add test for refundExcessNative modifier
Mav55 Aug 4, 2025
4058180
Improve test for mutant #1128: Simplify refund detection logic
Mav55 Aug 4, 2025
9a00462
Fix PR summary workflow triggers
Mav55 Aug 4, 2025
cda381b
Fix PR summary workflow jq parsing errors
Mav55 Aug 4, 2025
cbfb17b
Fix authentication: Use GITHUB_TOKEN instead of PAT
Mav55 Aug 4, 2025
2d2638f
Add debugging for Code Scanning API queries
Mav55 Aug 4, 2025
a666917
Fix PR summary to find mutation testing results
Mav55 Aug 5, 2025
af9cba7
Fix 'Argument list too long' error
Mav55 Aug 5, 2025
bb47427
Fix JSON parsing error in comment update
Mav55 Aug 5, 2025
602c9fe
Add response logging for debugging PR comment updates
Mav55 Aug 5, 2025
a6d4e0c
Fix false 'fixed' alerts by always running on all files
Mav55 Aug 5, 2025
610fd0e
Fix printf option parsing error
Mav55 Aug 5, 2025
b222515
Simplify PR comment to show only key metrics
Mav55 Aug 5, 2025
2db45b8
Add professional Web3 security styling to mutation testing summary
Mav55 Aug 5, 2025
8822af5
Fix broken table formatting - use simple clean format
Mav55 Aug 5, 2025
93c43b1
Wait for mutation-results check run before generating PR summary
Mav55 Aug 5, 2025
a972ea3
Add note when showing existing mutation testing results
Mav55 Aug 5, 2025
57325fd
Reducing batch size to 20 files per batch
Mav55 Aug 5, 2025
46b353a
Merge pull request #2 from olympix/test-mutation-testing-pr
Mav55 Aug 5, 2025
d0138ab
feat: fix mutant #1128 and add new isBridgeAvailable function
Mav55 Aug 5, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
347 changes: 347 additions & 0 deletions .github/workflows/mutationTestingAlertsReview.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,347 @@
name: Mutation Testing PR Summary

# - generates a PR comment summary of mutation testing results from GitHub Code Scanning
# - lists surviving mutants (test coverage gaps) found in the changed files
# - shows how many mutants were dismissed with proper justification
# - reports net unresolved findings that need attention
# - provides a clear overview of test coverage quality for the PR
# - leaves a summary comment starting with "🧪 Mutation Testing Summary"

on:
pull_request:
types:
- opened
- synchronize
- reopened
- ready_for_review
paths:
- 'src/**/*.sol'
workflow_run:
workflows: ["Olympix Mutation Testing"]
types:
- completed
workflow_dispatch:

permissions:
contents: read # required to fetch repository contents
pull-requests: write # required to post, update PR comments & revert PR to draft
security-events: read # required to fetch code scanning alerts
issues: write # required to post comments via the GitHub Issues API (used for PR comments)

jobs:
mutation-testing-summary:
runs-on: ubuntu-latest

steps:

- uses: actions/checkout@v4

- name: Wait for mutation-results check run to complete
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Waiting for mutation-results check run to complete..."

# Get the commit SHA from the triggering workflow
COMMIT_SHA="${{ github.event.workflow_run.head_sha }}"
echo "Checking commit SHA: $COMMIT_SHA"

# Wait up to 5 minutes for the mutation-results check run
MAX_WAIT=300 # 5 minutes
WAIT_TIME=0
CHECK_INTERVAL=30 # Check every 30 seconds

while [ $WAIT_TIME -lt $MAX_WAIT ]; do
echo "Checking for mutation-results check run... (${WAIT_TIME}s elapsed)"

# Get check runs for the commit
CHECK_RUNS=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/commits/${COMMIT_SHA}/check-runs")

# Look for mutation-results check run
MUTATION_CHECK=$(echo "$CHECK_RUNS" | jq -r '.check_runs[] | select(.name == "mutation-results") | .status + ":" + .conclusion')

# Debug: Show all check runs if we can't find mutation-results
if [[ -z "$MUTATION_CHECK" ]]; then
echo "Available check runs:"
echo "$CHECK_RUNS" | jq -r '.check_runs[] | .name + " (" + .status + ":" + (.conclusion // "null") + ")"'
fi

if [[ "$MUTATION_CHECK" == "completed:success" ]]; then
echo "✅ mutation-results check run completed successfully!"
break
elif [[ "$MUTATION_CHECK" == "completed:failure" ]]; then
echo "❌ mutation-results check run failed"
exit 1
elif [[ "$MUTATION_CHECK" == *"completed"* ]]; then
echo "⚠️ mutation-results check run completed with status: $MUTATION_CHECK"
exit 1
elif [[ -n "$MUTATION_CHECK" ]]; then
echo "⏳ mutation-results check run is still running: $MUTATION_CHECK"
else
echo "🔍 mutation-results check run not found yet"
fi

sleep $CHECK_INTERVAL
WAIT_TIME=$((WAIT_TIME + CHECK_INTERVAL))
done

if [ $WAIT_TIME -ge $MAX_WAIT ]; then
echo "⚠️ Timeout waiting for mutation-results check run to complete"
echo "Proceeding anyway to check for existing Code Scanning results..."

# Check if we have any existing mutation testing results at all
EXISTING_ALERTS=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?tool=Olympix%20Mutation%20Testing&per_page=1")

if echo "$EXISTING_ALERTS" | jq -e '. | length > 0' > /dev/null 2>&1; then
echo "Found existing mutation testing results, continuing with PR summary..."
echo "SHOWING_EXISTING_RESULTS=true" >> $GITHUB_ENV
else
echo "No existing mutation testing results found, skipping PR summary"
exit 0
fi
fi

- name: Get PR Number
id: get_pr
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Try to get PR number from different contexts
PR_NUMBER=""

# If triggered by pull_request event
if [ "${{ github.event_name }}" == "pull_request" ]; then
PR_NUMBER="${{ github.event.number }}"
echo "PR number from pull_request event: $PR_NUMBER"

# If triggered by workflow_run or workflow_dispatch, search for PR
elif [ -n "${{ github.sha }}" ]; then
echo "Searching for PR associated with commit ${{ github.sha }}"
SEARCH_RESULT=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/pulls?head=${{ github.repository_owner }}:${{ github.ref_name }}")

PR_NUMBER=$(echo "$SEARCH_RESULT" | jq -r '.[0].number // empty')
echo "PR number from API search: $PR_NUMBER"
fi

if [ -z "$PR_NUMBER" ] || [ "$PR_NUMBER" == "null" ]; then
echo "Error: No pull request found for this trigger." >&2
exit 1
fi

echo "Using PR number: $PR_NUMBER"
echo "PR_NUMBER=$PR_NUMBER" >> $GITHUB_ENV

- name: Fetch Mutation Testing Results for PR
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Fetching mutation testing results for PR #${PR_NUMBER}..."

# Fetch mutation testing results from GitHub Code Scanning
echo "Fetching alerts for PR #${PR_NUMBER}..."
MUTATIONS_PR=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?pr=${PR_NUMBER}")

echo "PR-specific API response:"
echo "$MUTATIONS_PR" | head -10

# Also fetch all recent alerts to compare
echo "Fetching all recent alerts..."
MUTATIONS_ALL=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?per_page=100&state=open")

echo "All alerts API response:"
echo "$MUTATIONS_ALL" | head -10

# Also try fetching by commit SHA
echo "Fetching alerts for commit ${{ github.sha }}..."
MUTATIONS_COMMIT=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/code-scanning/alerts?ref=${{ github.sha }}&per_page=100")

echo "Commit-specific API response:"
echo "$MUTATIONS_COMMIT" | head -10

# Use the source with the most results
PR_COUNT=$(echo "$MUTATIONS_PR" | jq 'length // 0')
ALL_COUNT=$(echo "$MUTATIONS_ALL" | jq 'length // 0')
COMMIT_COUNT=$(echo "$MUTATIONS_COMMIT" | jq 'length // 0')

echo "PR alerts: $PR_COUNT, All alerts: $ALL_COUNT, Commit alerts: $COMMIT_COUNT"

# Show what's in each response for debugging
if [[ $PR_COUNT -gt 0 ]]; then
echo "PR response tools:"
echo "$MUTATIONS_PR" | jq -r '.[] | .tool.name' | head -5
fi
if [[ $ALL_COUNT -gt 0 ]]; then
echo "All alerts tools:"
echo "$MUTATIONS_ALL" | jq -r '.[] | .tool.name' | head -5
fi
if [[ $COMMIT_COUNT -gt 0 ]]; then
echo "Commit alerts tools:"
echo "$MUTATIONS_COMMIT" | jq -r '.[] | .tool.name' | head -5
fi

if [[ $PR_COUNT -gt 0 ]]; then
MUTATIONS="$MUTATIONS_PR"
echo "Using PR-specific alerts ($PR_COUNT results)"
elif [[ $COMMIT_COUNT -gt 0 ]]; then
MUTATIONS="$MUTATIONS_COMMIT"
echo "Using commit-specific alerts ($COMMIT_COUNT results)"
else
MUTATIONS="$MUTATIONS_ALL"
echo "Using all recent alerts ($ALL_COUNT results)"
fi

echo "Filtering to Olympix Mutation Testing results only"

# Debug: Show what tools are available
echo "Available tools in alerts:"
echo "$MUTATIONS" | jq -r '.[] | .tool.name' | sort | uniq -c || echo "No tools found"

# Filter specifically for "Olympix Mutation Testing"
echo "Filtering for tool name: 'Olympix Mutation Testing'"

# Show first few alerts for debugging
echo "Sample alerts (first 3):"
echo "$MUTATIONS" | jq -c '.[:3] | .[] | {tool_name: .tool.name, rule_id: .rule.id, state: .state, file: .most_recent_instance.location.path}' || echo "No alerts to sample"

MUTATIONS=$(echo "$MUTATIONS" | jq -c '[ .[] | select(.tool.name == "Olympix Mutation Testing") ]' || echo "[]")

MUTATION_COUNT=$(echo "$MUTATIONS" | jq 'length')
echo "Found $MUTATION_COUNT Olympix Mutation Testing alerts"

# Extract surviving mutants (open - test coverage gaps)
SURVIVING_MUTANTS=$(echo "$MUTATIONS" | jq -c '[.[] | select(.state == "open") ]' || echo "[]")
# Extract dismissed mutants (acknowledged coverage gaps)
DISMISSED_MUTANTS=$(echo "$MUTATIONS" | jq -c '[.[] | select(.state == "dismissed")]' || echo "[]")

SURVIVING_COUNT=$(echo "$SURVIVING_MUTANTS" | jq -r 'length')
DISMISSED_COUNT=$(echo "$DISMISSED_MUTANTS" | jq -r 'length')
TOTAL_MUTANTS=$((SURVIVING_COUNT + DISMISSED_COUNT))

# Output for debugging
echo "SURVIVING_MUTANTS: $SURVIVING_MUTANTS"
echo "DISMISSED_MUTANTS: $DISMISSED_MUTANTS"
echo "SURVIVING_COUNT: $SURVIVING_COUNT"
echo "DISMISSED_COUNT: $DISMISSED_COUNT"
echo "TOTAL_MUTANTS: $TOTAL_MUTANTS"

# Save values in the environment - limit data to prevent argument list too long
# Only save first 50 of each type to avoid shell limits
SURVIVING_MUTANTS_LIMITED=$(echo "$SURVIVING_MUTANTS" | jq -c '.[:50]')
DISMISSED_MUTANTS_LIMITED=$(echo "$DISMISSED_MUTANTS" | jq -c '.[:50]')

echo "SURVIVING_MUTANTS=$SURVIVING_MUTANTS_LIMITED" >> $GITHUB_ENV
echo "DISMISSED_MUTANTS=$DISMISSED_MUTANTS_LIMITED" >> $GITHUB_ENV
echo "SURVIVING_COUNT=$SURVIVING_COUNT" >> $GITHUB_ENV
echo "DISMISSED_COUNT=$DISMISSED_COUNT" >> $GITHUB_ENV
echo "TOTAL_MUTANTS=$TOTAL_MUTANTS" >> $GITHUB_ENV

if [[ $SURVIVING_COUNT -gt 50 ]]; then
echo "Note: Limiting display to first 50 surviving mutants (total: $SURVIVING_COUNT)"
fi
if [[ $DISMISSED_COUNT -gt 50 ]]; then
echo "Note: Limiting display to first 50 dismissed mutants (total: $DISMISSED_COUNT)"
fi

- name: Find Existing PR Comment
id: find_comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
echo "Searching for existing PR comment..."

# Get comments with error handling - limit to recent comments only
COMMENTS_RESPONSE=$(curl -s -H "Authorization: token ${GITHUB_TOKEN}" \
"https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments?per_page=20&sort=updated&direction=desc")

echo "Recent comments count: $(echo "$COMMENTS_RESPONSE" | jq 'length // 0')"

# Check if response is valid JSON array
if echo "$COMMENTS_RESPONSE" | jq -e '. | type == "array"' > /dev/null 2>&1; then
COMMENT_ID=$(echo "$COMMENTS_RESPONSE" | jq -r \
'.[] | select(.body | startswith("## 🧪 Mutation Testing Summary")) | .id // empty' | head -1)
else
echo "Warning: Invalid JSON response from comments API"
COMMENT_ID=""
fi

if [[ -n "$COMMENT_ID" && "$COMMENT_ID" != "null" ]]; then
echo "EXISTING_COMMENT_ID=$COMMENT_ID" >> $GITHUB_ENV
echo "Found existing comment ID: $COMMENT_ID"
else
echo "No existing comment found"
fi

- name: Post or Update PR Comment
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
# Create comment body with proper line breaks using printf
if [[ "$TOTAL_MUTANTS" -gt 0 ]]; then
KILLED_COUNT=$((TOTAL_MUTANTS - SURVIVING_COUNT))
MUTATION_SCORE=$(( (KILLED_COUNT * 100) / TOTAL_MUTANTS ))

# Determine status emoji based on score
if [[ $MUTATION_SCORE -ge 80 ]]; then
STATUS_EMOJI="✅"
elif [[ $MUTATION_SCORE -ge 60 ]]; then
STATUS_EMOJI="🟡"
elif [[ $MUTATION_SCORE -ge 40 ]]; then
STATUS_EMOJI="🟠"
else
STATUS_EMOJI="🔴"
fi

COMMENT_BODY=$(printf -- "### 🧪 Olympix Mutation Testing\n\n")

# Add note if showing existing results due to timeout
if [[ "${SHOWING_EXISTING_RESULTS:-false}" == "true" ]]; then
COMMENT_BODY+=$(printf -- "⚠️ *Showing existing results - current run in progress*\n\n")
fi

COMMENT_BODY+=$(printf -- "%s **Score: %s%%** (%s/%s killed)\n\n" "$STATUS_EMOJI" "$MUTATION_SCORE" "$KILLED_COUNT" "$TOTAL_MUTANTS")
if [[ $SURVIVING_COUNT -gt 0 ]]; then
COMMENT_BODY+=$(printf -- "⚠️ **%s coverage gaps** need attention\n\n" "$SURVIVING_COUNT")
fi
else
COMMENT_BODY=$(printf -- "### 🧪 Olympix Mutation Testing\n\n")

# Add note if showing existing results due to timeout
if [[ "${SHOWING_EXISTING_RESULTS:-false}" == "true" ]]; then
COMMENT_BODY+=$(printf -- "⚠️ *Current run in progress - no previous results available*\n\n")
else
COMMENT_BODY+=$(printf -- "ℹ️ **No results found**\n\n")
fi
fi

# Simple action items
if [[ "$SURVIVING_COUNT" -gt 0 ]]; then
COMMENT_BODY+=$(printf -- "👀 [View details](../../security/code-scanning?tool=Olympix%%20Mutation%%20Testing) • Add tests to kill mutants\n")
else
COMMENT_BODY+=$(printf -- "🎉 **Perfect coverage** - all mutants killed!\n")
fi

# Properly escape JSON for API call
COMMENT_JSON=$(jq -n --arg body "$COMMENT_BODY" '{body: $body}')

# Update existing comment if found; otherwise, post a new one.
if [[ -n "$EXISTING_COMMENT_ID" ]]; then
echo "Updating existing comment ID: $EXISTING_COMMENT_ID"
RESPONSE=$(echo "$COMMENT_JSON" | curl -s -X PATCH -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
-d @- \
"https://api.github.com/repos/${{ github.repository }}/issues/comments/${EXISTING_COMMENT_ID}")
echo "Update response: $RESPONSE"
else
echo "Posting new comment to PR..."
RESPONSE=$(echo "$COMMENT_JSON" | curl -s -X POST -H "Authorization: token ${GITHUB_TOKEN}" -H "Content-Type: application/json" \
-d @- \
"https://api.github.com/repos/${{ github.repository }}/issues/${PR_NUMBER}/comments")
echo "Post response: $RESPONSE"
fi


Loading