Skip to content

feat: Add integration test coverage docs and improve HTTP client tests #84

feat: Add integration test coverage docs and improve HTTP client tests

feat: Add integration test coverage docs and improve HTTP client tests #84

Workflow file for this run

name: CI
on:
push:
branches: [main]
pull_request:
branches: [main]
permissions:
contents: read
pull-requests: write
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Build
run: go build -v ./...
integration-test-coverage:
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: "1.24"
- name: Download dependencies
run: go mod download
- name: Run integration tests with coverage
id: tests
run: |
# Build coverage-instrumented binary
echo "Building coverage-instrumented binary..."
mkdir -p ./bin
go build -cover -covermode=atomic -coverpkg=./... -o ./bin/tomato-coverage .
# Run tests and capture exit code
echo "Running integration tests with coverage..."
mkdir -p ./coverage
set +e
GOCOVERDIR=./coverage ./bin/tomato-coverage run -c ./tests/tomato.yml
TEST_EXIT_CODE=$?
set -e
# Generate coverage reports regardless of test result
echo "Converting coverage data..."
go tool covdata textfmt -i=./coverage -o=coverage-integration.out
echo "Generating coverage report..."
go tool cover -html=coverage-integration.out -o coverage-integration.html
# Save test result for later
echo "test_exit_code=$TEST_EXIT_CODE" >> $GITHUB_OUTPUT
if [ $TEST_EXIT_CODE -ne 0 ]; then
echo "::warning::Integration tests failed with exit code $TEST_EXIT_CODE"
fi
- name: Generate coverage summary
id: coverage
run: |
# Get total coverage percentage
TOTAL=$(go tool cover -func=coverage-integration.out | grep total | awk '{print $3}')
echo "total=$TOTAL" >> $GITHUB_OUTPUT
# Generate coverage table by package (excluding internal/handler)
TABLE=$(go tool cover -func=coverage-integration.out | grep -v "total:" | grep -v "internal/handler" | \
awk -F'[:\t ]+' 'BEGIN{} {path=$1; pct=$NF; gsub(/%/, "", pct); gsub(/github.com\/tomatool\/tomato\//, "", path); n=split(path, parts, "/"); if (n > 1) {pkg = parts[1]; for (i=2; i<n; i++) pkg = pkg "/" parts[i]} else {pkg = "root"} funcs[pkg]++; total[pkg]+=pct} END {for(p in funcs) {avg = total[p]/funcs[p]; printf "%s|%d|%.1f\n", p, funcs[p], avg}}' | sort -t'|' -k3 -rn)
echo "table<<EOF" >> $GITHUB_OUTPUT
echo "$TABLE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
# Generate coverage table for internal/handler by file
HANDLER_TABLE=$(go tool cover -func=coverage-integration.out | grep "internal/handler" | \
awk -F'[:\t ]+' 'BEGIN{} {path=$1; pct=$NF; gsub(/%/, "", pct); gsub(/github.com\/tomatool\/tomato\/internal\/handler\//, "", path); n=split(path, parts, "/"); file=parts[1]; funcs[file]++; total[file]+=pct} END {for(f in funcs) {avg = total[f]/funcs[f]; printf "%s|%d|%.1f\n", f, funcs[f], avg}}' | sort -t'|' -k3 -rn)
echo "handler_table<<EOF" >> $GITHUB_OUTPUT
echo "$HANDLER_TABLE" >> $GITHUB_OUTPUT
echo "EOF" >> $GITHUB_OUTPUT
- name: Post coverage comment
uses: actions/github-script@v7
with:
script: |
const total = '${{ steps.coverage.outputs.total }}';
const tableData = `${{ steps.coverage.outputs.table }}`;
const handlerTableData = `${{ steps.coverage.outputs.handler_table }}`;
// Determine emoji based on coverage
let emoji = '馃敶';
const coverageNum = parseFloat(total);
if (coverageNum >= 80) emoji = '馃煝';
else if (coverageNum >= 60) emoji = '馃煛';
else if (coverageNum >= 40) emoji = '馃煚';
// Helper to build table rows
const buildRows = (data) => data.trim().split('\n').filter(r => r).map(row => {
const [name, funcs, avg] = row.split('|');
const pct = parseFloat(avg);
let icon = '馃敶';
if (pct >= 80) icon = '馃煝';
else if (pct >= 60) icon = '馃煛';
else if (pct >= 40) icon = '馃煚';
return `| ${icon} | \`${name}\` | ${funcs} | ${avg}% |`;
}).join('\n');
const packageRows = buildRows(tableData);
const handlerRows = buildRows(handlerTableData);
const body = `## ${emoji} Integration Test Coverage: **${total}**
### By Package
| | Package | Functions | Coverage |
|---|---------|-----------|----------|
${packageRows}
### Handler Files (\`internal/handler\`)
| | File | Functions | Coverage |
|---|------|-----------|----------|
${handlerRows}
---
*Coverage generated from \`make integration-test-coverage\`*
`;
// 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('Integration Test Coverage')
);
if (botComment) {
await github.rest.issues.updateComment({
owner: context.repo.owner,
repo: context.repo.repo,
comment_id: botComment.id,
body: body
});
} else {
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: context.issue.number,
body: body
});
}
- name: Fail if tests failed
if: steps.tests.outputs.test_exit_code != '0'
run: |
echo "Integration tests failed with exit code ${{ steps.tests.outputs.test_exit_code }}"
exit 1