Skip to content
Draft
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
246 changes: 246 additions & 0 deletions .github/workflows/lambda-tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,246 @@
name: Lambda Jest Tests on Changed Files

on:
pull_request:
branches:
- main
- develop

jobs:
detect-changes:
runs-on: ubuntu-latest
outputs:
lambda-dirs: ${{ steps.set-dirs.outputs.dirs }}
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
fetch-depth: 0

- name: Get changed files and extract Lambda directories
id: changed-files
uses: tj-actions/changed-files@v44
with:
files: |
**/*.js
**/*.ts
**/*.json
**/package.json
**/yarn.lock
files_separator: "\n"

- name: Extract unique Lambda directories
id: set-dirs
if: steps.changed-files.outputs.any_changed == 'true'
run: |
# Get directories containing package.json (Lambda function roots)
dirs=$(echo "${{ steps.changed-files.outputs.all_changed_files }}" | \
tr ' ' '\n' | \
xargs -I {} dirname {} | \
sort -u | \
while read -r dir; do
# Walk up directory tree to find package.json
current="$dir"
while [ "$current" != "." ] && [ "$current" != "/" ]; do
if [ -f "$current/package.json" ]; then
echo "$current"
break
fi
current=$(dirname "$current")
done
done | sort -u | jq -R -s -c 'split("\n")[:-1] | unique')

echo "Unique Lambda directories with changes: $dirs"
echo "dirs=$dirs" >> $GITHUB_OUTPUT

jest-tests:
needs: detect-changes
if: |
needs.detect-changes.outputs.lambda-dirs != '' &&
needs.detect-changes.outputs.lambda-dirs != '[]'
runs-on: ubuntu-latest
strategy:
matrix:
directory: ${{ fromJson(needs.detect-changes.outputs.lambda-dirs) }}
node-version: [18.x, 20.x]
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4
with:
token: ${{ secrets.GITHUB_TOKEN }}
ref: ${{ github.event.pull_request.head.ref }}

- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
cache-dependency-path: ${{ matrix.directory }}/package-lock.json

- name: Install dependencies
run: npm ci
working-directory: ${{ matrix.directory }}

- name: Run Jest tests
id: jest
run: |
npm test > test_output.txt 2>&1
TEST_EXIT_CODE=$?
TEST_OUTPUT=$(cat test_output.txt)
echo "stdout<<EOF" >> $GITHUB_OUTPUT
echo "$TEST_OUTPUT" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
exit $TEST_EXIT_CODE
working-directory: ${{ matrix.directory }}
continue-on-error: true
env:
NODE_ENV: test

- name: Upload coverage reports
uses: codecov/codecov-action@v4
if: matrix.node-version == '20.x' && steps.jest.outcome == 'success'
with:
files: ${{ matrix.directory }}/coverage/coverage-final.json
flags: unittests-${{ matrix.directory }}
name: lambda-coverage-${{ matrix.directory }}
fail_ci_if_error: false

- name: Upload test results
if: always()
run: |
ARTIFACT_NAME="test-results-$(echo '${{ matrix.directory }}' | tr '/' '-')-node-$(echo '${{ matrix.node-version }}' | tr '.' '-')"
echo "ARTIFACT_NAME=$ARTIFACT_NAME" >> $GITHUB_ENV
continue-on-error: true

- name: Upload artifacts
if: always()
uses: actions/upload-artifact@v4
with:
name: ${{ env.ARTIFACT_NAME }}
path: |
${{ matrix.directory }}/coverage/
${{ matrix.directory }}/jest-results.json
${{ matrix.directory }}/test_output.txt
retention-days: 30

- name: Comment PR with test results
if: github.event_name == 'pull_request' && matrix.node-version == '20.x'
env:
TEST_OUTPUT: ${{ steps.jest.outputs.stdout }}
uses: actions/github-script@v7
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const path = require('path');

// Delete old comments
const comments = await github.rest.issues.listComments({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo
});

const botComments = comments.data.filter(comment => {
return comment.user.type === 'Bot' &&
comment.body.includes('#### Jest Tests') &&
comment.body.includes('`${{ matrix.directory }}`') &&
comment.body.includes('Node.js ${{ matrix.node-version }}');
});

for (const comment of botComments) {
await github.rest.issues.deleteComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: comment.id
});
}

// Create new comment
const isSuccess = '${{ steps.jest.outcome }}' === 'success';
let coverageSummary = 'Coverage data not available';
let testOutput = process.env.TEST_OUTPUT || 'No test output captured';

// Try to read test output from file if not in env
if (!testOutput || testOutput === 'No test output captured') {
try {
const testFile = path.join('${{ matrix.directory }}', 'test_output.txt');
if (fs.existsSync(testFile)) {
testOutput = fs.readFileSync(testFile, 'utf8');
}
} catch (error) {
console.log('Error reading test output:', error.message);
}
}

// Get coverage summary if successful
if (isSuccess) {
try {
const coveragePath = path.join('${{ matrix.directory }}', 'coverage', 'coverage-summary.json');
if (fs.existsSync(coveragePath)) {
const coverage = JSON.parse(fs.readFileSync(coveragePath, 'utf8'));
const total = coverage.total;
coverageSummary = `
📊 **Coverage Summary:**
- Statements: ${total.statements.pct}%
- Branches: ${total.branches.pct}%
- Functions: ${total.functions.pct}%
- Lines: ${total.lines.pct}%`;
}
} catch (error) {
console.log('Error reading coverage:', error.message);
}
}

const status = isSuccess ? '✅' : '❌';
const title = isSuccess ? 'Jest Tests ✅' : 'Jest Tests Failed ❌';
const detailsTitle = isSuccess ? 'Show Test Output' : 'Show Error Details';

const output = `#### ${title} \`${{ matrix.directory }}\`
#### Node.js Version 🟢 \`${{ matrix.node-version }}\`
#### Test Status 🧪 \`${{ steps.jest.outcome }}\`

${isSuccess ? coverageSummary : ''}

<details><summary>${detailsTitle}</summary>

\`\`\`
${testOutput}
\`\`\`

</details>

*Pushed by: @${{ github.actor }}, Action: \`${{ github.event_name }}\`*`;

github.rest.issues.createComment({
issue_number: context.issue.number,
owner: context.repo.owner,
repo: context.repo.repo,
body: output
});

- name: Fail job if tests failed
if: steps.jest.outcome == 'failure'
run: exit 1

# Summary job to ensure all tests completed
jest-tests-summary:
needs: [detect-changes, jest-tests]
if: always()
runs-on: ubuntu-latest
steps:
- name: Summary
run: |
if [ "${{ needs.detect-changes.outputs.lambda-dirs }}" == "" ] || [ "${{ needs.detect-changes.outputs.lambda-dirs }}" == "[]" ]; then
echo "No Lambda changes detected"
else
echo "Jest tests completed for directories: ${{ needs.detect-changes.outputs.lambda-dirs }}"
if [ "${{ needs.jest-tests.result }}" == "failure" ]; then
echo "❌ Some tests failed"
exit 1
else
echo "✅ All tests passed"
fi
fi
6 changes: 6 additions & 0 deletions apps/backend/lambdas/projects/jest.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/*.test.ts'],
};

Loading
Loading