Files
wehub-resource-sync 409e92d6ae
Build and Push Docker Images / Build Docker Image (push) Has been cancelled
Build and Push Docker Images / Build Railway Docker Image (push) Has been cancelled
Build and Publish n8n Docker Image / test-image (push) Has been cancelled
Dependency Compatibility Check / Fresh Install Dependency Check (push) Has been cancelled
Build and Publish n8n Docker Image / build-and-push (push) Has been cancelled
Build and Publish n8n Docker Image / create-release (push) Has been cancelled
Automated Release / Detect Version Change (push) Has been cancelled
Automated Release / Generate Release Notes (push) Has been cancelled
Automated Release / Create GitHub Release (push) Has been cancelled
Automated Release / Package MCPB Bundle (push) Has been cancelled
Automated Release / Build and Verify (push) Has been cancelled
Automated Release / Publish to NPM (push) Has been cancelled
Automated Release / Build and Push Docker Images (push) Has been cancelled
Automated Release / Update Documentation (push) Has been cancelled
Automated Release / Notify Release Completion (push) Has been cancelled
Secret Scan / secretlint (push) Has been cancelled
Test Suite / test (push) Has been cancelled
Test Suite / cjs-runtime (push) Has been cancelled
Test Suite / publish-results (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:41:06 +08:00

325 lines
12 KiB
YAML

name: Test Suite
on:
push:
branches: [main, feat/comprehensive-testing-suite]
paths-ignore:
- '**.md'
- '**.txt'
- 'docs/**'
- 'examples/**'
- '.github/FUNDING.yml'
- '.github/ISSUE_TEMPLATE/**'
- '.github/pull_request_template.md'
- '.gitignore'
- 'LICENSE*'
- 'ATTRIBUTION.md'
- 'SECURITY.md'
- 'CODE_OF_CONDUCT.md'
pull_request:
branches: [main]
paths-ignore:
- '**.md'
- '**.txt'
- 'docs/**'
- 'examples/**'
- '.github/FUNDING.yml'
- '.github/ISSUE_TEMPLATE/**'
- '.github/pull_request_template.md'
- '.gitignore'
- 'LICENSE*'
- 'ATTRIBUTION.md'
- 'SECURITY.md'
- 'CODE_OF_CONDUCT.md'
permissions:
contents: read
issues: write
pull-requests: write
checks: write
jobs:
test:
runs-on: ubuntu-latest
timeout-minutes: 15 # Increased from 10 to accommodate larger database with community nodes
env:
# A boolean flag (not the secrets themselves) recording whether a live n8n
# instance is configured, so the live-integration step can gate on it without
# the N8N_API_* values being exposed to every step in the job. The live tests
# need BOTH the URL and the key (getN8nCredentials() throws if either is
# missing), and both are empty on Dependabot/fork PRs — so require both here,
# otherwise a partially-configured instance would run the step and then fail.
HAS_N8N_INSTANCE: ${{ secrets.N8N_API_URL != '' && secrets.N8N_API_KEY != '' }}
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
# --legacy-peer-deps: n8n-nodes-base 2.20.x pulls in mappersmith with a `diff`
# peer that conflicts with ts-node's `diff@^4`; npm 7+ strict resolution leaves
# the lock internally inconsistent. Match local dev (.npmrc legacy-peer-deps=true).
run: npm ci --legacy-peer-deps
# Verify test environment setup
- name: Verify test environment
run: |
echo "Current directory: $(pwd)"
echo "Checking for .env.test file:"
ls -la .env.test || echo ".env.test not found!"
echo "First few lines of .env.test:"
head -5 .env.test || echo "Cannot read .env.test"
# Run unit tests first (without MSW)
- name: Run unit tests with coverage
run: npm run test:unit -- --coverage --coverage.thresholds.lines=0 --coverage.thresholds.functions=0 --coverage.thresholds.branches=0 --coverage.thresholds.statements=0 --reporter=default --reporter=junit
env:
CI: true
# Pre-build the Docker test image used by tests/integration/docker/*.test.ts
# Two test files used to build it independently inside their beforeAll hooks; the
# builder stage does an `npm install` from the public registry which intermittently
# fails on CI runners and tanked the entire test job. Building it here once gets us:
# - one build attempt instead of two
# - a clear, separately-visible CI step when the npm registry is flaky
# - the test suite degrades to skipped tests if this step fails (won't block PRs)
- name: Build Docker test image
run: npm run docker:test:build
timeout-minutes: 8
continue-on-error: true
# Offline integration suites (database, security, mcp-protocol, workflow-diff,
# templates, docker, …). These need no live instance, so they always run and
# stay a blocking gate on every PR — Dependabot and forks included. Only the
# live n8n-api / ai-validation suites are excluded here (they run in the next
# step). MSW setup applies as configured.
- name: Run integration tests (offline suites)
run: npm run test:integration -- --exclude 'tests/integration/n8n-api/**' --exclude 'tests/integration/ai-validation/**' --reporter=default --reporter=junit
env:
CI: true
# Live n8n-API + AI-validation suites hit a real instance via the N8N_API_*
# secrets, which are unavailable on Dependabot and fork PRs (there every file
# here fails outright, turning the required `test` check permanently red). Skip
# them when no instance is configured; on in-repo PRs the secret is present so
# they run and remain a blocking gate. Secrets are scoped to this step only.
- name: Run integration tests (live n8n instance)
if: ${{ env.HAS_N8N_INSTANCE == 'true' }}
run: npm run test:integration -- tests/integration/n8n-api tests/integration/ai-validation --reporter=default --reporter=junit
env:
CI: true
N8N_API_URL: ${{ secrets.N8N_API_URL }}
N8N_API_KEY: ${{ secrets.N8N_API_KEY }}
N8N_TEST_WEBHOOK_GET_URL: ${{ secrets.N8N_TEST_WEBHOOK_GET_URL }}
N8N_TEST_WEBHOOK_POST_URL: ${{ secrets.N8N_TEST_WEBHOOK_POST_URL }}
N8N_TEST_WEBHOOK_PUT_URL: ${{ secrets.N8N_TEST_WEBHOOK_PUT_URL }}
N8N_TEST_WEBHOOK_DELETE_URL: ${{ secrets.N8N_TEST_WEBHOOK_DELETE_URL }}
# Generate test summary
- name: Generate test summary
if: always()
run: node scripts/generate-test-summary.js
# Generate detailed reports
- name: Generate detailed reports
if: always()
run: node scripts/generate-detailed-reports.js
# Upload test results artifacts
- name: Upload test results
if: always()
uses: actions/upload-artifact@v7
with:
name: test-results-${{ github.run_number }}-${{ github.run_attempt }}
path: |
test-results/
test-summary.md
test-reports/
retention-days: 30
if-no-files-found: warn
# Upload coverage artifacts
- name: Upload coverage reports
if: always()
uses: actions/upload-artifact@v7
with:
name: coverage-${{ github.run_number }}-${{ github.run_attempt }}
path: |
coverage/
retention-days: 30
if-no-files-found: warn
# Upload coverage to Codecov
- name: Upload coverage to Codecov
if: always()
uses: codecov/codecov-action@v7
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./coverage/lcov.info
flags: unittests
name: codecov-umbrella
fail_ci_if_error: false
verbose: true
# Run linting
- name: Run linting
run: npm run lint
# Run type checking
- name: Run type checking
run: npm run typecheck
# Create test report comment for PRs
- name: Create test report comment
if: github.event_name == 'pull_request' && always()
uses: actions/github-script@v7
continue-on-error: true
with:
script: |
const fs = require('fs');
let summary = '## Test Results\n\nTest summary generation failed.';
try {
if (fs.existsSync('test-summary.md')) {
summary = fs.readFileSync('test-summary.md', 'utf8');
}
} catch (error) {
console.error('Error reading test summary:', error);
}
try {
// Find existing comment
const { data: comments } = await github.rest.issues.listComments({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
});
const botComment = comments.find(comment =>
comment.user.type === 'Bot' &&
comment.body.includes('## Test Results')
);
if (botComment) {
// Update existing comment
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: summary
});
} else {
// Create new comment
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: summary
});
}
} catch (error) {
console.error('Failed to create/update PR comment:', error.message);
console.log('This is likely due to insufficient permissions for external PRs.');
console.log('Test results have been saved to the job summary instead.');
}
# Generate job summary
- name: Generate job summary
if: always()
run: |
echo "# Test Run Summary" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
if [ -f test-summary.md ]; then
cat test-summary.md >> $GITHUB_STEP_SUMMARY
else
echo "Test summary generation failed." >> $GITHUB_STEP_SUMMARY
fi
echo "" >> $GITHUB_STEP_SUMMARY
echo "## 📥 Download Artifacts" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
echo "- [Test Results](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
echo "- [Coverage Report](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})" >> $GITHUB_STEP_SUMMARY
# Store test metadata
- name: Store test metadata
if: always()
run: |
cat > test-metadata.json << EOF
{
"run_id": "${{ github.run_id }}",
"run_number": "${{ github.run_number }}",
"run_attempt": "${{ github.run_attempt }}",
"sha": "${{ github.sha }}",
"ref": "${{ github.ref }}",
"event_name": "${{ github.event_name }}",
"repository": "${{ github.repository }}",
"actor": "${{ github.actor }}",
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
"node_version": "$(node --version)",
"npm_version": "$(npm --version)"
}
EOF
- name: Upload test metadata
if: always()
uses: actions/upload-artifact@v7
with:
name: test-metadata-${{ github.run_number }}-${{ github.run_attempt }}
path: test-metadata.json
retention-days: 30
# Verify the compiled CommonJS artifact loads under Node's CJS loader (regression guard for #864).
# Node 20.19+/22.12+ silently tolerate require() of ESM-only deps; scripts/smoke-cjs-runtime.js
# forces the strict loader (--no-experimental-require-module) when the running Node supports it,
# so a CJS/ESM mismatch in a shipped dependency (e.g. uuid@14) fails here regardless of the
# runner's Node version. The unit suite runs under Vitest's ESM pipeline and cannot catch this;
# tsc only type-checks.
cjs-runtime:
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- uses: actions/checkout@v7
- uses: actions/setup-node@v6
with:
node-version: 20
cache: 'npm'
- name: Install dependencies
run: npm ci --legacy-peer-deps
- name: Build
run: npm run build
- name: Load compiled CommonJS artifact under strict CJS loader
run: npm run test:cjs-runtime
# Publish test results as checks
publish-results:
needs: test
runs-on: ubuntu-latest
if: always()
permissions:
checks: write
steps:
- uses: actions/checkout@v7
- name: Download test results
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Publish test results
uses: dorny/test-reporter@v3
if: always()
continue-on-error: true
with:
name: Test Results
path: 'artifacts/test-results-*/test-results/junit.xml'
reporter: java-junit
fail-on-error: false
fail-on-empty: false