Skip to content

feat: fuck you

feat: fuck you #19

---
name: "[Tests] Unit Tests"
description: "Run unit tests"
# This workflow runs three independent test suites:
# 1. run-tests: General project tests (excludes RAG and ArgoCD MCP tests)
# 2. run-argocd-tests: ArgoCD MCP tests (requires ARGOCD_API_URL env var)
# 3. run-rag-tests: RAG module tests (requires PyTorch and special setup)
# Each suite runs independently to isolate dependencies and environment requirements.
on:
push:
branches:
- main
pull_request:
branches:
- main
permissions:
contents: read
pull-requests: write
issues: write
actions: read
jobs:
run-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Install dependencies and run tests
run: |
echo "Installing dependencies with uv sync..."
/home/runner/.local/bin/uv sync
echo "Adding test dependencies..."
/home/runner/.local/bin/uv add pytest-asyncio pytest-cov --group unittest
echo "Running tests with coverage..."
/home/runner/.local/bin/uv run pytest \
--ignore=integration \
--ignore=evals \
--ignore=ai_platform_engineering/knowledge_bases/rag/tests \
--ignore=ai_platform_engineering/agents/argocd/mcp/tests \
--ignore=ai_platform_engineering/agents/argocd/tests \
--ignore=ai_platform_engineering/agents/komodor/tests \
--ignore=volumes \
--ignore=docker-compose \
--cov=ai_platform_engineering \
--cov-report=xml \
--cov-report=html
- name: Upload coverage reports
if: github.event_name == 'pull_request'
uses: actions/upload-artifact@v6
with:
name: coverage-reports-main
path: |
coverage.xml
htmlcov/
continue-on-error: true
run-argocd-tests:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Set up Python
uses: actions/setup-python@v6
with:
python-version: "3.13"
- name: Install UV
run: |
curl -LsSf https://astral.sh/uv/install.sh | sh
- name: Run ArgoCD MCP tests
run: |
/home/runner/.local/bin/uv venv
source .venv/bin/activate && uv sync --no-dev
cd ai_platform_engineering/agents/argocd/mcp && make test
# run-rag-tests:
# runs-on: ubuntu-latest
# steps:
# - name: Checkout code
# uses: actions/checkout@v6
# - name: Set up Python
# uses: actions/setup-python@v6
# with:
# python-version: "3.13"
# - name: Install UV
# run: |
# curl -LsSf https://astral.sh/uv/install.sh | sh
# - name: Run RAG tests
# run: |
# /home/runner/.local/bin/uv venv
# source .venv/bin/activate && cd ai_platform_engineering/knowledge_bases/rag/server && uv sync --dev
# source .venv/bin/activate && uv add --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match torch --force-reinstall
# make test-rag-all
# - name: Upload RAG coverage reports
# if: github.event_name == 'pull_request'
# uses: actions/upload-artifact@v6
# with:
# name: coverage-reports-rag
# path: |
# ai_platform_engineering/knowledge_bases/rag/server/coverage.xml
# ai_platform_engineering/knowledge_bases/rag/server/htmlcov/
coverage-report:
runs-on: ubuntu-latest
needs: [run-tests, run-argocd-tests]
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v6
- name: Download main coverage reports
uses: actions/download-artifact@v7
with:
name: coverage-reports-main
path: ./main-coverage/
# RAG tests disabled - uncomment when re-enabled
# - name: Download RAG coverage reports
# uses: actions/download-artifact@v7
# with:
# name: coverage-reports-rag
# path: ./rag-coverage/
- name: Generate coverage summary
run: |
echo "## 📊 Test Coverage Report" >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
# Check for main coverage
if [ -f "./main-coverage/coverage.xml" ]; then
echo "### Main Tests Coverage" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "Main test suite coverage data available" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
# Check for RAG coverage
if [ -f "./rag-coverage/coverage.xml" ]; then
echo "### RAG Tests Coverage" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "RAG test suite coverage data available" >> $GITHUB_STEP_SUMMARY
echo '```' >> $GITHUB_STEP_SUMMARY
echo "" >> $GITHUB_STEP_SUMMARY
fi
if [ ! -f "./main-coverage/coverage.xml" ] && [ ! -f "./rag-coverage/coverage.xml" ]; then
echo "❌ No coverage data available" >> $GITHUB_STEP_SUMMARY
fi
- name: Install coverage parsing dependencies
run: |
npm install xml2js
- name: Comment PR with coverage
if: github.event_name == 'pull_request' && github.event.pull_request.number
uses: actions/github-script@v8
with:
github-token: ${{ secrets.GITHUB_TOKEN }}
script: |
const fs = require('fs');
const xml2js = require('xml2js');
// Function to parse coverage XML and extract coverage data
async function parseCoverageXML(filePath, testSuite) {
try {
if (!fs.existsSync(filePath)) {
console.log(`Coverage file not found: ${filePath}`);
return null;
}
const xmlContent = fs.readFileSync(filePath, 'utf8');
console.log(`Parsing coverage file: ${filePath}`);
console.log(`File size: ${xmlContent.length} bytes`);
const parser = new xml2js.Parser();
const result = await parser.parseStringPromise(xmlContent);
const coverage = result.coverage;
if (!coverage || !coverage.$) {
console.log(`Invalid coverage XML structure in ${filePath}`);
console.log(`Coverage object:`, JSON.stringify(coverage, null, 2));
return null;
}
console.log(`Coverage attributes:`, coverage.$);
const lineRate = parseFloat(coverage.$['line-rate']);
const branchRate = parseFloat(coverage.$['branch-rate']);
const linesCovered = parseInt(coverage.$['lines-covered']);
const linesValid = parseInt(coverage.$['lines-valid']);
const branchesCovered = parseInt(coverage.$['branches-covered']);
const branchesValid = parseInt(coverage.$['branches-valid']);
// Check for NaN values
if (isNaN(lineRate) || isNaN(branchRate) || isNaN(linesCovered) || isNaN(linesValid) || isNaN(branchesCovered) || isNaN(branchesValid)) {
console.log(`NaN values detected in ${filePath}:`, {
lineRate, branchRate, linesCovered, linesValid, branchesCovered, branchesValid
});
return null;
}
return {
testSuite,
lineRate: (lineRate * 100).toFixed(1),
branchRate: (branchRate * 100).toFixed(1),
linesCovered,
linesValid,
branchesCovered,
branchesValid
};
} catch (error) {
console.log(`Error parsing ${filePath}:`, error.message);
return null;
}
}
// Generate coverage summary
let coverageSummary = '## 📊 Test Coverage Report\n\n';
// Debug: List available files
console.log('=== Debug: Available files ===');
try {
const mainDir = fs.readdirSync('./main-coverage/');
console.log('Main coverage directory contents:', mainDir);
} catch (e) {
console.log('Main coverage directory not found or empty');
}
try {
const ragDir = fs.readdirSync('./rag-coverage/');
console.log('RAG coverage directory contents:', ragDir);
} catch (e) {
console.log('RAG coverage directory not found or empty');
}
// Parse main coverage
const mainCoverage = await parseCoverageXML('./main-coverage/coverage.xml', 'Main Tests');
if (mainCoverage) {
coverageSummary += `### ${mainCoverage.testSuite} Coverage\n`;
coverageSummary += `| Metric | Coverage | Details |\n`;
coverageSummary += `|--------|----------|----------|\n`;
coverageSummary += `| **Lines** | ${mainCoverage.lineRate}% | ${mainCoverage.linesCovered}/${mainCoverage.linesValid} lines |\n`;
coverageSummary += `| **Branches** | ${mainCoverage.branchRate}% | ${mainCoverage.branchesCovered}/${mainCoverage.branchesValid} branches |\n\n`;
}
// Parse RAG coverage
const ragCoverage = await parseCoverageXML('./rag-coverage/coverage.xml', 'RAG Tests');
if (ragCoverage) {
coverageSummary += `### ${ragCoverage.testSuite} Coverage\n`;
coverageSummary += `| Metric | Coverage | Details |\n`;
coverageSummary += `|--------|----------|----------|\n`;
coverageSummary += `| **Lines** | ${ragCoverage.lineRate}% | ${ragCoverage.linesCovered}/${ragCoverage.linesValid} lines |\n`;
coverageSummary += `| **Branches** | ${ragCoverage.branchRate}% | ${ragCoverage.branchesCovered}/${ragCoverage.branchesValid} branches |\n\n`;
}
if (!mainCoverage && !ragCoverage) {
coverageSummary += '❌ No coverage data available\n\n';
coverageSummary += '**Debug Information:**\n';
coverageSummary += '- Main tests: Excluded RAG module from coverage (by design)\n';
coverageSummary += '- RAG tests: Check if coverage XML was generated and uploaded correctly\n';
coverageSummary += '- Verify that pytest-cov is properly configured in RAG module\n';
coverageSummary += '- Look at the workflow logs for detailed error messages\n\n';
} else if (!mainCoverage && ragCoverage) {
coverageSummary += 'ℹ️ **Note**: Main tests coverage not available (RAG module excluded by design)\n\n';
}
// Add coverage artifacts info with download links
coverageSummary += '### 📁 Coverage Artifacts\n';
// Get artifacts for this workflow run
const artifacts = await github.rest.actions.listWorkflowRunArtifacts({
owner: context.repo.owner,
repo: context.repo.repo,
run_id: context.runId
});
// Find coverage artifacts
const mainArtifact = artifacts.data.artifacts.find(artifact => artifact.name === 'coverage-reports-main');
const ragArtifact = artifacts.data.artifacts.find(artifact => artifact.name === 'coverage-reports-rag');
if (mainArtifact) {
// Use the archive_download_url if available, otherwise construct the URL
const mainUrl = mainArtifact.archive_download_url ||
`https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/artifacts/${mainArtifact.id}`;
coverageSummary += `- **Main tests**: [coverage-reports-main](${mainUrl}) artifact\n`;
} else {
coverageSummary += '- **Main tests**: `coverage-reports-main` artifact (not available)\n';
}
if (ragArtifact) {
// Use the archive_download_url if available, otherwise construct the URL
const ragUrl = ragArtifact.archive_download_url ||
`https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId}/artifacts/${ragArtifact.id}`;
coverageSummary += `- **RAG tests**: [coverage-reports-rag](${ragUrl}) artifact\n`;
} else {
coverageSummary += '- **RAG tests**: `coverage-reports-rag` artifact (not available)\n';
}
coverageSummary += '- **Download artifacts** to view detailed HTML coverage reports\n';
// Debug context information
console.log('Event name:', context.eventName);
console.log('Issue number:', context.issue.number);
console.log('PR number:', context.payload.pull_request?.number);
console.log('Event type:', typeof context.payload);
// Only proceed if this is actually a pull request
if (context.eventName !== 'pull_request' || !context.payload.pull_request) {
console.log('Not a pull request event, skipping comment');
return;
}
// Use PR number from payload
const issueNumber = context.payload.pull_request.number;
console.log('Using PR number:', issueNumber);
if (!issueNumber) {
console.log('No PR number available, skipping comment');
return;
}
// Post comment to PR
github.rest.issues.createComment({
issue_number: issueNumber,
owner: context.repo.owner,
repo: context.repo.repo,
body: coverageSummary
});