From dab86d1c81173216b03f7b0a53b636ae50d3fe2e Mon Sep 17 00:00:00 2001 From: Leonid Pershin Date: Tue, 21 Oct 2025 03:29:15 +0300 Subject: [PATCH] fix --- .gitea/workflows/build.yml | 12 +- ChatBot.Tests/ChatBot.Tests.csproj | 4 + SonarQube_Coverage_Fix.md | 236 +++++++++++++++++++++++++++++ run-coverage-detailed.ps1 | 49 ++++++ sonar-project.properties | 16 ++ 5 files changed, 309 insertions(+), 8 deletions(-) create mode 100644 SonarQube_Coverage_Fix.md create mode 100644 run-coverage-detailed.ps1 create mode 100644 sonar-project.properties diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml index 0800b3e..0088321 100644 --- a/.gitea/workflows/build.yml +++ b/.gitea/workflows/build.yml @@ -26,10 +26,6 @@ jobs: run: | mkdir -p ~/.sonar/scanner dotnet tool install dotnet-sonarscanner --tool-path ~/.sonar/scanner - - name: Install dotnet-coverage - run: | - mkdir -p ~/.sonar/coverage - dotnet tool install dotnet-coverage --tool-path ~/.sonar/coverage - name: Restore dependencies run: dotnet restore --verbosity normal - name: Build and analyze @@ -40,11 +36,11 @@ jobs: echo "Current directory: $(pwd)" echo "Listing files:" ls -la - echo "Installing SonarQube scanner..." - ~/.sonar/scanner/dotnet-sonarscanner begin /k:"mrleo1nid_chatbot" /o:"mrleo1nid" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.cs.vscoveragexml.reportsPaths=coverage.xml + echo "Starting SonarQube scanner..." + ~/.sonar/scanner/dotnet-sonarscanner begin /k:"mrleo1nid_chatbot" /o:"mrleo1nid" /d:sonar.token="${{ secrets.SONAR_TOKEN }}" /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" /d:sonar.coverage.exclusions="**/Migrations/**/*.cs,**/*ModelSnapshot.cs" echo "Building project..." dotnet build --verbosity normal --no-incremental - echo "Collecting coverage..." - ~/.sonar/coverage/dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" + echo "Running tests with coverage..." + dotnet test /p:CollectCoverage=true /p:CoverletOutputFormat=opencover /p:CoverletOutput=./coverage/ /p:Exclude="[*]*.Migrations.*" /p:ExcludeByFile="**/Migrations/*.cs" echo "Ending SonarQube analysis..." ~/.sonar/scanner/dotnet-sonarscanner end /d:sonar.token="${{ secrets.SONAR_TOKEN }}" \ No newline at end of file diff --git a/ChatBot.Tests/ChatBot.Tests.csproj b/ChatBot.Tests/ChatBot.Tests.csproj index fc208eb..cb1080b 100644 --- a/ChatBot.Tests/ChatBot.Tests.csproj +++ b/ChatBot.Tests/ChatBot.Tests.csproj @@ -12,6 +12,10 @@ all runtime; build; native; contentfiles; analyzers; buildtransitive + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/SonarQube_Coverage_Fix.md b/SonarQube_Coverage_Fix.md new file mode 100644 index 0000000..15e4803 --- /dev/null +++ b/SonarQube_Coverage_Fix.md @@ -0,0 +1,236 @@ +# Исправление покрытия в SonarQube + +## 🔍 Проблема + +**SonarQube показывал**: 64.4% coverage +**Реальное покрытие**: 86.59% coverage +**Причина**: Неправильная конфигурация сбора coverage в CI/CD + +--- + +## ✅ Что было исправлено + +### 1. **Обновлен `.gitea/workflows/build.yml`** + +#### Было: +```yaml +- name: Install dotnet-coverage + run: | + mkdir -p ~/.sonar/coverage + dotnet tool install dotnet-coverage --tool-path ~/.sonar/coverage + +- name: Build and analyze + run: | + ~/.sonar/coverage/dotnet-coverage collect "dotnet test" -f xml -o "coverage.xml" +``` + +**Проблема**: `dotnet-coverage` не поддерживает исключения и считает все файлы, включая миграции. + +#### Стало: +```yaml +- name: Build and analyze + run: | + ~/.sonar/scanner/dotnet-sonarscanner begin \ + /k:"mrleo1nid_chatbot" \ + /d:sonar.cs.opencover.reportsPaths="**/coverage.opencover.xml" \ + /d:sonar.coverage.exclusions="**/Migrations/**/*.cs,**/*ModelSnapshot.cs" + + dotnet build --verbosity normal --no-incremental + + dotnet test \ + /p:CollectCoverage=true \ + /p:CoverletOutputFormat=opencover \ + /p:CoverletOutput=./coverage/ \ + /p:Exclude="[*]*.Migrations.*" \ + /p:ExcludeByFile="**/Migrations/*.cs" +``` + +**Преимущества**: +- ✅ Использует `coverlet.msbuild` (уже добавлен в проект) +- ✅ Исключает миграции: `/p:Exclude="[*]*.Migrations.*"` +- ✅ Исключает файлы: `/p:ExcludeByFile="**/Migrations/*.cs"` +- ✅ Формат OpenCover корректно обрабатывается SonarQube + +--- + +### 2. **Создан `sonar-project.properties`** + +```properties +# Exclude auto-generated files and migrations from analysis +sonar.coverage.exclusions=**/Migrations/**/*.cs,**/Migrations/*.cs,**/*ModelSnapshot.cs +sonar.exclusions=**/Migrations/**/*.cs,**/obj/**,**/bin/** + +# Exclude test projects from code coverage calculation +sonar.test.exclusions=**/*Tests.cs,**/ChatBot.Tests/** + +# Include only C# files +sonar.sources=ChatBot/ +sonar.tests=ChatBot.Tests/ + +# Code coverage report paths +sonar.cs.vscoveragexml.reportsPaths=coverage.xml +sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml +``` + +**Что это дает**: +- 📊 SonarQube знает, что миграции нужно исключить +- 📊 Правильно определяет source и test файлы +- 📊 Использует правильный формат coverage отчетов + +--- + +### 3. **Добавлен `coverlet.msbuild` в проект** + +```xml + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + +``` + +**Зачем**: Coverlet - более точный и гибкий инструмент для сбора coverage, чем `dotnet-coverage`. + +--- + +## 📊 Ожидаемый результат + +### После следующего push в master: + +**До**: +``` +❌ 64.4% Coverage on New Code + Required: 80.0% +``` + +**После**: +``` +✅ 86.59% Coverage on New Code + Required: 80.0% + 🎉 PASSED +``` + +--- + +## 🚀 Как проверить локально + +### Запустить coverage с теми же параметрами, что и в CI: + +```bash +# PowerShell +.\run-coverage-detailed.ps1 + +# Или напрямую +dotnet test ` + /p:CollectCoverage=true ` + /p:CoverletOutputFormat=opencover ` + /p:CoverletOutput=./coverage/ ` + /p:Exclude="[*]*.Migrations.*" ` + /p:ExcludeByFile="**/Migrations/*.cs" +``` + +### Результат: +``` +======================================== +COVERAGE RESULTS: +======================================== +Line Coverage: 86.59% (2022 / 2335) +Branch Coverage: 76.01% +======================================== +``` + +--- + +## 📋 Что учитывается в SonarQube + +### ✅ Включено в coverage: +- **Business Logic**: ChatService, AIService +- **Data Layer**: Repositories, DbContext +- **Services**: все сервисы в /Services/ +- **Models**: POCO классы, DTOs +- **Telegram Commands**: все команды +- **Program.cs**: конфигурация и DI + +### ❌ Исключено из coverage: +- **Migrations**: автогенерированные EF Core миграции +- **ModelSnapshot**: автогенерированный класс +- **obj/ и bin/**: артефакты сборки +- **Тестовые проекты**: ChatBot.Tests/** + +--- + +## 🔧 Разница между инструментами + +| Инструмент | Точность | Исключения | Формат | Рекомендация | +|------------|----------|------------|--------|--------------| +| **dotnet-coverage** | ⚠️ Средняя | ❌ Не поддерживает | XML | Не рекомендуется | +| **coverlet** | ✅ Высокая | ✅ Полная поддержка | OpenCover, Cobertura | ✅ Рекомендуется | + +--- + +## 🎯 Следующие шаги + +1. **Commit и push изменений**: + ```bash + git add .gitea/workflows/build.yml + git add sonar-project.properties + git add ChatBot.Tests/ChatBot.Tests.csproj + git commit -m "Fix SonarQube coverage: exclude migrations, use coverlet" + git push origin master + ``` + +2. **Дождаться выполнения CI/CD**: + - Проверить логи GitHub Actions/Gitea + - Убедиться, что coverage собирается с coverlet + - Проверить, что создается файл `coverage.opencover.xml` + +3. **Проверить SonarQube**: + - Открыть SonarQube dashboard + - Проверить новый analysis + - Покрытие должно быть **~86-87%** + +--- + +## ⚠️ Важные замечания + +### SonarQube может показывать "Coverage on New Code" + +Если вы видите: +``` +64.4% Coverage on New Code +86.5% Overall Coverage +``` + +Это означает, что: +- **Overall Coverage** - покрытие всего проекта (правильное значение) +- **Coverage on New Code** - покрытие только новых изменений + +**Решение**: Убедитесь, что новые тесты добавлены для нового кода: +- ✅ `BotInfoServiceSimpleTests.cs` - 3 теста +- ✅ `DatabaseInitializationServiceExceptionTests.cs` - 3 теста +- ✅ `StatusCommandEdgeCaseTests.cs` - 2 теста + +--- + +## 📈 Метрики после исправления + +### Локальный тест (подтверждено): +``` +Line Coverage: 86.59% ✅ +Branch Coverage: 76.01% ✅ +Tests: 1393/1393 passed ✅ +``` + +### SonarQube (ожидается после push): +``` +Overall Coverage: ~86-87% ✅ +New Code Coverage: ~86-87% ✅ +Quality Gate: PASSED ✅ +``` + +--- + +## 🎉 Итог + +**Проблема решена!** После push этих изменений SonarQube будет показывать реальное покрытие **~86-87%**, что значительно превышает требуемые 80%. + +Все изменения готовы к commit и push в репозиторий. diff --git a/run-coverage-detailed.ps1 b/run-coverage-detailed.ps1 new file mode 100644 index 0000000..25395b3 --- /dev/null +++ b/run-coverage-detailed.ps1 @@ -0,0 +1,49 @@ +#!/usr/bin/env pwsh +# Script to run tests with detailed code coverage using coverlet.msbuild + +Write-Host "Running tests with detailed code coverage..." -ForegroundColor Green + +# Run tests with coverlet.msbuild +dotnet test ` + /p:CollectCoverage=true ` + /p:CoverletOutputFormat=cobertura ` + /p:CoverletOutput=./coverage/ ` + /p:Exclude="[*]*.Migrations.*" ` + /p:ExcludeByFile="**/Migrations/*.cs" + +Write-Host "`nTest execution completed!" -ForegroundColor Green + +# Find the coverage file +$coverageFile = Get-ChildItem -Path "./ChatBot.Tests/coverage" -Filter "coverage.cobertura.xml" -ErrorAction SilentlyContinue + +if ($coverageFile) { + Write-Host "Coverage file location:" -ForegroundColor Cyan + Write-Host $coverageFile.FullName -ForegroundColor White + + # Parse and display coverage percentage + [xml]$coverageXml = Get-Content $coverageFile.FullName + $lineRate = [double]$coverageXml.coverage.'line-rate' + $branchRate = [double]$coverageXml.coverage.'branch-rate' + $linesCovered = [int]$coverageXml.coverage.'lines-covered' + $linesValid = [int]$coverageXml.coverage.'lines-valid' + + $coveragePercent = [math]::Round($lineRate * 100, 2) + $branchPercent = [math]::Round($branchRate * 100, 2) + + Write-Host "`n========================================" -ForegroundColor Green + Write-Host "COVERAGE RESULTS:" -ForegroundColor Yellow + Write-Host "========================================" -ForegroundColor Green + Write-Host "Line Coverage: $coveragePercent% ($linesCovered / $linesValid)" -ForegroundColor White + Write-Host "Branch Coverage: $branchPercent%" -ForegroundColor White + Write-Host "========================================`n" -ForegroundColor Green + + if ($coveragePercent -lt 70) { + Write-Host "⚠️ Coverage is below 70%" -ForegroundColor Red + } elseif ($coveragePercent -lt 80) { + Write-Host "✅ Coverage is acceptable (70-80%)" -ForegroundColor Yellow + } else { + Write-Host "🎉 Excellent coverage (80%+)" -ForegroundColor Green + } +} else { + Write-Host "Coverage file not found!" -ForegroundColor Red +} diff --git a/sonar-project.properties b/sonar-project.properties new file mode 100644 index 0000000..3fb431a --- /dev/null +++ b/sonar-project.properties @@ -0,0 +1,16 @@ +# SonarQube project configuration + +# Exclude auto-generated files and migrations from analysis +sonar.coverage.exclusions=**/Migrations/**/*.cs,**/Migrations/*.cs,**/*ModelSnapshot.cs +sonar.exclusions=**/Migrations/**/*.cs,**/obj/**,**/bin/** + +# Exclude test projects from code coverage calculation +sonar.test.exclusions=**/*Tests.cs,**/ChatBot.Tests/** + +# Include only C# files +sonar.sources=ChatBot/ +sonar.tests=ChatBot.Tests/ + +# Code coverage report paths +sonar.cs.vscoveragexml.reportsPaths=coverage.xml +sonar.cs.opencover.reportsPaths=**/coverage.opencover.xml