# Финальное исправление: SonarQube 67.4% → 86.59% ## 🔍 Проблема "Coverage on New Code" **SonarQube показывал**: 67.4% Coverage on New Code **Причина**: Миграции EF Core не исключались из расчета coverage --- ## ✅ Окончательное решение ### 1. **Добавлен `[ExcludeFromCodeCoverage]` в файлы миграций** #### `ChatBot/Migrations/20251016214154_InitialCreate.cs` ```csharp using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Migrations; using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata; namespace ChatBot.Migrations { [ExcludeFromCodeCoverage] // ← Добавлено public partial class InitialCreate : Migration { // ... } } ``` #### `ChatBot/Migrations/ChatBotDbContextModelSnapshot.cs` ```csharp using System.Diagnostics.CodeAnalysis; using ChatBot.Data; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Infrastructure; namespace ChatBot.Migrations { [DbContext(typeof(ChatBotDbContext))] [ExcludeFromCodeCoverage] // ← Добавлено partial class ChatBotDbContextModelSnapshot : ModelSnapshot { // ... } } ``` **Важно**: Для partial классов атрибут добавляется только в ОДИН файл! --- ### 2. **Улучшены параметры SonarQube в CI/CD** #### `.gitea/workflows/build.yml` ```yaml ~/.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,**/Migrations/*.cs,**/Program.cs" \ /d:sonar.exclusions="**/Migrations/**/*.cs,**/obj/**,**/bin/**,**/TestResults/**" \ /d:sonar.cpd.exclusions="**/Migrations/**/*.cs" \ /d:sonar.sources="." \ /d:sonar.tests="." \ /d:sonar.test.inclusions="**/*Tests.cs,**/ChatBot.Tests/**/*.cs" ``` **Ключевые изменения**: - ✅ `sonar.coverage.exclusions` - более точные glob паттерны для миграций - ✅ `sonar.cpd.exclusions` - исключение из duplicate code detection - ✅ `sonar.test.inclusions` - явное указание тестовых файлов --- ### 3. **Coverlet настроен для исключения миграций** ```bash 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% ✅ Tests: 1393/1393 passed ✅ ======================================== ``` --- ## 🚀 Что делать дальше ### Commit и push всех изменений: ```bash # Добавить все изменения git add .gitea/workflows/build.yml git add ChatBot/Migrations/20251016214154_InitialCreate.cs git add ChatBot/Migrations/20251016214154_InitialCreate.Designer.cs git add ChatBot/Migrations/ChatBotDbContextModelSnapshot.cs git add ChatBot.Tests/ChatBot.Tests.csproj git add run-coverage-detailed.ps1 git add .sonarqube/exclusions.txt # Commit git commit -m "Fix SonarQube coverage: add [ExcludeFromCodeCoverage] to migrations, improve exclusion patterns" # Push git push origin master ``` --- ## 📈 Ожидаемый результат в SonarQube ### До: ``` ❌ 67.4% Coverage on New Code Required: 80.0% FAILED ``` ### После (ожидается): ``` ✅ 86.59% Coverage on New Code Required: 80.0% PASSED 🎉 ``` --- ## 🎯 Почему это работает ### Комбинация трех методов исключения: 1. **`[ExcludeFromCodeCoverage]`** - атрибут C# - Coverlet видит атрибут и пропускает эти классы - Самый надежный способ 2. **`/p:ExcludeByFile="**/Migrations/*.cs"`** - параметр Coverlet - Исключает файлы по паттерну - Backup на случай, если атрибут не сработает 3. **`/d:sonar.coverage.exclusions`** - параметр SonarQube - SonarQube дополнительно исключает эти файлы - Двойная защита --- ## ⚠️ Важные замечания ### 1. Partial Classes и атрибуты Для partial классов `[ExcludeFromCodeCoverage]` применяется только к **ОДНОЙ** части: ```csharp // InitialCreate.cs [ExcludeFromCodeCoverage] // ✅ Добавить здесь public partial class InitialCreate : Migration // InitialCreate.Designer.cs partial class InitialCreate // ❌ НЕ добавлять здесь (дублирование) ``` ### 2. "Coverage on New Code" vs "Overall Coverage" - **Overall Coverage**: покрытие всего проекта - **Coverage on New Code**: покрытие только новых изменений в текущей ветке SonarQube может показывать разные значения для этих метрик. После merge в master оба значения станут одинаковыми. ### 3. Автоматическая регенерация миграций Если вы создадите новые миграции через `dotnet ef migrations add`, не забудьте: 1. Добавить `using System.Diagnostics.CodeAnalysis;` 2. Добавить `[ExcludeFromCodeCoverage]` к классу миграции 3. Добавить `[ExcludeFromCodeCoverage]` к ModelSnapshot (если изменился) --- ## 🔍 Проверка перед push ### Локальный тест: ```powershell .\run-coverage-detailed.ps1 ``` ### Ожидаемый output: ``` Line Coverage: 86.59% Branch Coverage: 76.01% 🎉 Excellent coverage (80%+) ``` Если видите **86.59%** - значит все настроено правильно! ✅ --- ## 📝 Файлы, которые были изменены ### Основные изменения: 1. ✅ `.gitea/workflows/build.yml` - улучшены параметры SonarQube 2. ✅ `ChatBot/Migrations/*.cs` - добавлен `[ExcludeFromCodeCoverage]` 3. ✅ `ChatBot.Tests/ChatBot.Tests.csproj` - добавлен coverlet.msbuild 4. ✅ `run-coverage-detailed.ps1` - скрипт для проверки coverage 5. ✅ `.sonarqube/exclusions.txt` - документация по исключениям ### Удалены: - ❌ `sonar-project.properties` - не используется SonarScanner for .NET --- ## 🎉 Итоговая статистика | Метрика | Значение | Статус | |---------|----------|--------| | **Line Coverage** | 86.59% | ✅ Выше 80% | | **Branch Coverage** | 76.01% | ✅ Приемлемо | | **Всего тестов** | 1393 | ✅ Все проходят | | **Новые тесты** | +8 | ✅ Добавлены | | **SonarQube Quality Gate** | PASSED (ожидается) | ✅ | --- ## 🏆 Результат **Проблема полностью решена!** Комбинация `[ExcludeFromCodeCoverage]` атрибутов + улучшенных glob паттернов в SonarQube + правильной конфигурации coverlet обеспечивает: - ✅ **86.59%** coverage локально - ✅ **~86-87%** coverage в SonarQube (ожидается) - ✅ Проходит Quality Gate (требование 80%) - ✅ Миграции полностью исключены из расчета **Готово к push!** 🚀