Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
101 changes: 101 additions & 0 deletions .github/actions/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
# GitHub Actions for SMART-on-FHIR Proxy Testing

This directory contains reusable GitHub Actions for comprehensive testing of the SMART-on-FHIR Proxy application.

## Actions Overview

### 1. `setup-docker-inferno/`
Sets up Docker environment and prepares Inferno ONC Program Edition testing infrastructure.

**Inputs:**
- `test_stage`: Testing stage (alpha, beta, production)
- `fhir_server_url`: FHIR server URL for testing
- `keycloak_url`: Keycloak URL for OAuth testing
- `inferno_version`: Inferno Docker image version (default: latest)

**Outputs:**
- `inferno_container_id`: Container ID of started Inferno instance
- `test_config_path`: Path to generated test configuration

### 2. `run-inferno-tests/`
Executes Inferno ONC Program Edition tests and generates compliance reports.

**Inputs:**
- `test_stage`: Testing stage (alpha, beta, production)
- `inferno_container_id`: Container ID of running Inferno instance
- `test_config_path`: Path to Inferno test configuration
- `fhir_server_url`: FHIR server URL being tested
- `test_timeout`: Maximum test execution time (default: 1800s)

**Outputs:**
- `test_session_id`: ID of created test session
- `test_results_path`: Path to test results directory
- `compliance_status`: Overall compliance status (passed/failed)

### 3. `comprehensive-testing/`
Runs complete test suite: unit tests, integration tests, and ONC Inferno tests.

**Inputs:**
- `test_stage`: Testing stage (alpha, beta, production)
- `deployment_target`: Deployment location (local, fly.io, vps)
- `fhir_server_url`: FHIR server URL for testing
- `keycloak_url`: Keycloak URL for OAuth testing
- `app_version`: Application version being tested
- `skip_unit_tests`: Skip unit tests for deployed environments
- `skip_inferno_tests`: Skip Inferno ONC tests

**Outputs:**
- `unit_test_status`: Unit test results (passed/failed/skipped)
- `integration_test_status`: Integration test results (passed/failed)
- `inferno_test_status`: Inferno test results (passed/failed/skipped)
- `overall_status`: Overall test status (passed/failed)
- `test_reports_path`: Path to all test reports

## Usage Example

```yaml
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Run comprehensive tests
uses: ./.github/actions/comprehensive-testing
with:
test_stage: "beta"
deployment_target: "fly.io"
fhir_server_url: "https://my-app.fly.dev"
keycloak_url: "https://auth.fly.dev"
app_version: "1.2.3"
```

## Test Stages

- **Alpha**: Basic compliance testing with unit and integration tests
- **Beta**: US Core profile testing with extended FHIR compliance
- **Production**: Full ONC certification testing with all required sequences

## Deployment Targets

- **Local**: Tests run in GitHub Actions with local Docker services
- **Fly.io**: Tests run against deployed application on Fly.io
- **VPS**: Tests run against deployed application on your VPS

## Test Reports

All test results are stored in structured directories:
```
testing/
β”œβ”€β”€ alpha/reports/
β”œβ”€β”€ beta/reports/
β”œβ”€β”€ production/reports/
└── summary/
```

Each stage generates comprehensive reports including:
- Unit test coverage
- Integration test results
- FHIR compliance reports
- Inferno ONC test results
- Overall compliance status
262 changes: 262 additions & 0 deletions .github/actions/run-inferno-tests/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,262 @@
name: 'Run Inferno ONC Tests'
description: 'Executes Inferno ONC Program Edition tests and generates compliance reports'

inputs:
test_stage:
description: 'Testing stage: alpha, beta, or production'
required: true
inferno_container_id:
description: 'Container ID of the running Inferno instance'
required: true
test_config_path:
description: 'Path to the Inferno test configuration file'
required: true
fhir_server_url:
description: 'FHIR server URL being tested'
required: true
test_timeout:
description: 'Maximum time to wait for tests to complete (in seconds)'
required: false
default: '1800'

outputs:
test_session_id:
description: 'ID of the created test session'
value: ${{ steps.run-tests.outputs.session_id }}
test_results_path:
description: 'Path to the test results directory'
value: ${{ steps.run-tests.outputs.results_path }}
compliance_status:
description: 'Overall compliance test status (passed/failed)'
value: ${{ steps.analyze-results.outputs.status }}

runs:
using: 'composite'
steps:
- name: Create Inferno test directories
shell: bash
run: |
echo "πŸ“ Creating Inferno-specific test directories..."
mkdir -p testing/${{ inputs.test_stage }}/reports/inferno
mkdir -p testing/${{ inputs.test_stage }}/data

- name: Verify Inferno is running
shell: bash
run: |
echo "πŸ” Verifying Inferno container is running..."
if ! docker ps | grep -q "${{ inputs.inferno_container_id }}"; then
echo "❌ Inferno container is not running"
exit 1
fi
echo "βœ… Inferno container is running"

- name: Validate FHIR server for Inferno testing
shell: bash
run: |
echo "πŸ₯ Validating FHIR server for Inferno compliance testing..."

# Test FHIR capability statement (required for Inferno)
echo "Testing FHIR capability statement..."
if curl -f -H "Accept: application/fhir+json" "${{ inputs.fhir_server_url }}/metadata" > testing/${{ inputs.test_stage }}/reports/inferno/capability_statement.json; then
echo "βœ… FHIR capability statement retrieved"
else
echo "❌ Failed to retrieve FHIR capability statement"
exit 1
fi

# Test SMART configuration (required for ONC certification)
echo "Testing SMART configuration..."
if curl -f -H "Accept: application/json" "${{ inputs.fhir_server_url }}/.well-known/smart_configuration" > testing/${{ inputs.test_stage }}/reports/inferno/smart_configuration.json; then
echo "βœ… SMART configuration retrieved"
else
echo "⚠️ SMART configuration not available (may be expected for some implementations)"
fi

- name: Run Inferno ONC tests
id: run-tests
shell: bash
run: |
echo "πŸ”₯ Starting Inferno ONC Program Edition tests for ${{ inputs.test_stage }}..."

# Create test session via Inferno API
echo "Creating new test session..."
SESSION_RESPONSE=$(curl -X POST http://localhost:4567/api/test_sessions \
-H "Content-Type: application/json" \
-d @${{ inputs.test_config_path }} \
--fail --silent --show-error) || {
echo "❌ Failed to create Inferno test session"
echo "Response: $SESSION_RESPONSE"
exit 1
}

# Extract session ID from response
SESSION_ID=$(echo "$SESSION_RESPONSE" | jq -r '.id // empty')
if [ -z "$SESSION_ID" ]; then
echo "❌ Failed to extract session ID from response"
echo "Response: $SESSION_RESPONSE"
exit 1
fi

echo "session_id=$SESSION_ID" >> $GITHUB_OUTPUT
echo "results_path=testing/${{ inputs.test_stage }}/reports/inferno" >> $GITHUB_OUTPUT
echo "βœ… Test session created with ID: $SESSION_ID"

# Start the test execution
echo "Starting test execution..."
curl -X POST "http://localhost:4567/api/test_sessions/$SESSION_ID/start" \
-H "Content-Type: application/json" \
--fail || {
echo "❌ Failed to start test execution"
exit 1
}

# Monitor test progress
echo "⏳ Monitoring test progress..."
start_time=$(date +%s)
timeout_time=$((start_time + ${{ inputs.test_timeout }}))

while [ $(date +%s) -lt $timeout_time ]; do
# Check test session status
STATUS_RESPONSE=$(curl -s "http://localhost:4567/api/test_sessions/$SESSION_ID" || echo "{}")
STATUS=$(echo "$STATUS_RESPONSE" | jq -r '.status // "unknown"')

case "$STATUS" in
"completed")
echo "βœ… Tests completed successfully"
break
;;
"failed"|"error")
echo "❌ Tests failed or encountered an error"
echo "Status response: $STATUS_RESPONSE"
break
;;
"running"|"started")
echo "πŸ”„ Tests still running... ($(date))"
sleep 30
;;
*)
echo "⚠️ Unknown status: $STATUS"
sleep 30
;;
esac
done

# Final status check
FINAL_STATUS=$(curl -s "http://localhost:4567/api/test_sessions/$SESSION_ID" | jq -r '.status // "timeout"')
if [ "$FINAL_STATUS" != "completed" ]; then
echo "⚠️ Tests did not complete successfully. Final status: $FINAL_STATUS"
fi

echo "πŸ”„ Downloading test results..."
# Download test results
curl -s "http://localhost:4567/api/test_sessions/$SESSION_ID/results" > testing/${{ inputs.test_stage }}/reports/inferno/test_results.json
curl -s "http://localhost:4567/api/test_sessions/$SESSION_ID/report" > testing/${{ inputs.test_stage }}/reports/inferno/test_report.html

- name: Analyze test results
id: analyze-results
shell: bash
run: |
echo "πŸ“Š Analyzing Inferno test results..."

RESULTS_FILE="testing/${{ inputs.test_stage }}/reports/inferno/test_results.json"

if [ ! -f "$RESULTS_FILE" ]; then
echo "❌ Test results file not found"
echo "status=failed" >> $GITHUB_OUTPUT
exit 1
fi

# Parse test results
TOTAL_TESTS=$(jq '.test_results | length // 0' "$RESULTS_FILE")
PASSED_TESTS=$(jq '[.test_results[] | select(.result == "pass")] | length // 0' "$RESULTS_FILE")
FAILED_TESTS=$(jq '[.test_results[] | select(.result == "fail")] | length // 0' "$RESULTS_FILE")
SKIPPED_TESTS=$(jq '[.test_results[] | select(.result == "skip")] | length // 0' "$RESULTS_FILE")

echo "πŸ“ˆ Test Results Summary:"
echo " Total: $TOTAL_TESTS"
echo " Passed: $PASSED_TESTS"
echo " Failed: $FAILED_TESTS"
echo " Skipped: $SKIPPED_TESTS"

# Determine overall status
if [ "$FAILED_TESTS" -eq 0 ] && [ "$PASSED_TESTS" -gt 0 ]; then
OVERALL_STATUS="passed"
echo "βœ… All tests passed!"
elif [ "$FAILED_TESTS" -gt 0 ]; then
OVERALL_STATUS="failed"
echo "❌ Some tests failed"
else
OVERALL_STATUS="unknown"
echo "⚠️ No clear test results"
fi

echo "status=$OVERALL_STATUS" >> $GITHUB_OUTPUT

- name: Generate compliance report
shell: bash
run: |
echo "πŸ“„ Generating compliance report..."

REPORT_FILE="testing/${{ inputs.test_stage }}/reports/inferno/compliance_report.md"

cat > "$REPORT_FILE" << EOF
# ONC Inferno Compliance Report

**Test Stage**: ${{ inputs.test_stage }}
**FHIR Server**: ${{ inputs.fhir_server_url }}
**Test Session ID**: ${{ steps.run-tests.outputs.session_id }}
**Generated**: $(date -u)
**Overall Status**: ${{ steps.analyze-results.outputs.status }}

## Test Configuration
- **Inferno Module**: ONC Program Edition
- **Test Focus**: $(jq -r '.inferno.test_focus // "Unknown"' ${{ inputs.test_config_path }})
- **Test Sequences**: $(jq -r '.inferno.sequences | length // 0' ${{ inputs.test_config_path }}) sequences configured

## Results Summary
- **Test Results**: Available in \`test_results.json\`
- **HTML Report**: Available in \`test_report.html\`
- **FHIR Capability**: Available in \`capability_statement.json\`
- **SMART Config**: Available in \`smart_configuration.json\`

## Next Steps
EOF

if [ "${{ steps.analyze-results.outputs.status }}" = "passed" ]; then
cat >> "$REPORT_FILE" << EOF
βœ… **All tests passed** - Ready for ONC certification submission

1. Review detailed test report in \`test_report.html\`
2. Verify all required sequences completed successfully
3. Prepare certification documentation
4. Submit to ONC for official certification review
EOF
else
cat >> "$REPORT_FILE" << EOF
❌ **Some tests failed** - Review and address issues before certification

1. Review failed tests in \`test_results.json\`
2. Check detailed error messages in \`test_report.html\`
3. Address compliance gaps in FHIR implementation
4. Re-run tests after fixes are implemented
EOF
fi

echo "βœ… Compliance report generated at $REPORT_FILE"

- name: Export container logs
if: always()
shell: bash
run: |
echo "πŸ“‹ Exporting Inferno container logs..."
docker logs "${{ inputs.inferno_container_id }}" > testing/${{ inputs.test_stage }}/reports/inferno/inferno_container.log 2>&1 || true
echo "βœ… Container logs exported"

- name: Cleanup Inferno container
if: always()
shell: bash
run: |
echo "🧹 Cleaning up Inferno container..."
docker stop "${{ inputs.inferno_container_id }}" || true
docker rm "${{ inputs.inferno_container_id }}" || true
echo "βœ… Inferno container cleaned up"
Loading