From 97f3833bf23838bb642cea6f84e69ec7dfac60ea Mon Sep 17 00:00:00 2001 From: thienpt Date: Mon, 14 Sep 2026 10:33:35 +0700 Subject: [PATCH] feat: enforce >90% SonarQube coverage rules and exclusions for Laravel - Enhance laravel-unit-test skill with layer-by-layer branch coverage rules - Expand Laravel boilerplate exclusions in sonar-project.properties template - Update SonarQube guide with Laravel comprehensive exclusions --- .claude/skills/laravel-unit-test/skill.md | 46 ++++++++++++++----- ...10-sonarqube-100-percent-coverage-guide.md | 4 +- .../configs/sonar-project.properties.template | 2 +- 3 files changed, 37 insertions(+), 15 deletions(-) diff --git a/.claude/skills/laravel-unit-test/skill.md b/.claude/skills/laravel-unit-test/skill.md index 653ca4d..c1631f0 100644 --- a/.claude/skills/laravel-unit-test/skill.md +++ b/.claude/skills/laravel-unit-test/skill.md @@ -83,18 +83,40 @@ Người dùng có thể kích hoạt linh hoạt theo các chế độ (từ qu - **Dùng Laravel Fakes**: `Http::fake()`, `Queue::fake()`, `Event::fake()`, `Storage::fake()`. - **Dùng Object thật (Không Mock)**: DTOs, Value Objects, Collections, Request payload dạng mảng. -### Bước 4: Lập Ma Trận Test Scenarios (Bắt Buộc Đầy Đủ & Không Bỏ Sót) -- **Bắt buộc 100% Public Methods**: Mọi phương thức public đều phải có test. - - *Nếu là Job / Command*: **BẮT BUỘC 100% phải test phương thức `handle()`** và `failed()`. Tuyệt đối CẤM chỉ test hàm helper phụ (`buildRedisKey`) bằng reflection! - - *Nếu là Repository*: **BẮT BUỘC phải test kết quả truy vấn / cập nhật dữ liệu thật** trên SQLite in-memory, tuyệt đối CẤM chỉ assert `Builder::class`! -- **Bao phủ 100% nhánh rẽ SonarQube**: - - Mọi toán tử `??` (Null Coalescing) và `?:` (Ternary) phải có test case cho cả 2 vế. - - Mọi khối `try - catch - finally` bắt buộc phải có test case ép mock ném Exception để chạy qua khối `catch`. - - Mọi `if`, `else if`, `else`, `switch`, `match` (kể cả nhánh `default`) đều phải được kích hoạt. -- **Tối thiểu cho mỗi public method**: - - **1 Happy Path**: Đầu vào hợp lệ -> Thực thi thành công -> Trả về đúng kết quả. - - **N Edge Cases**: Input biên (0, số âm, rỗng `""`, `[]`, `null`, unicode). - - **N Exception Cases**: Tham số sai ném `InvalidArgumentException`, service ngoài lỗi ném `PaymentFailedException`,... +### Bước 4: Lập Ma Trận Test Scenarios (Đảm Bảo Coverage > 90% Trên SonarQube) +Mục tiêu cốt lõi: SonarQube tính cả Line Coverage và Branch Coverage. Để đạt **Coverage > 90%**, Agent phải tuân thủ nghiêm ngặt ma trận sau: + +1. **100% Public Methods**: Mọi phương thức public của class đều phải có bài test. +2. **Quét 100% Nhánh Rẽ (Branch / Condition Coverage)**: + - **Toán tử `??` (Null Coalescing)**: Bắt buộc 2 test case (có giá trị & null/fallback). + - **Toán tử `?:` (Ternary)**: Bắt buộc 2 test case (true & false). + - **Biểu thức logic `&&` và `||`**: Phải test các kịch bản kết hợp để thỏa mãn điều kiện rẽ nhánh SonarQube. + - **Khối `try - catch - finally`**: BẮT BUỘC có test case ép mock ném Exception để chạy qua khối `catch`. + - **Mọi `if`, `else if`, `else`, `switch`, `match`** (kể cả nhánh `default`). +3. **Chiến Lược Riêng Cho Từng Tầng Laravel Để Đạt > 90%**: + - **FormRequest (`app/Http/Requests/`)**: + - Test `authorize()` trả về cả `true` (cho phép) và `false` (từ chối). + - Test `rules()`: Bắt buộc test dữ liệu hợp lệ VÀ test vi phạm cho **từng rule** (`required`, `email`, `max`, `numeric`, `unique`) để phủ 100% các nhánh validator. + - **Custom Rules (`app/Rules/`)**: Test cả 2 trường hợp `passes() === true` và `passes() === false` (kèm thông báo `message()`). + - **Controllers (`app/Http/Controllers/`)**: + - Endpoint Success: 200 OK hoặc 201 Created. + - Validation Error: 422 Unprocessable Entity khi payload thiếu/sai. + - Unauthorized: 401 Unauthenticated hoặc 403 Forbidden. + - Not Found: 404 khi Model ID không tồn tại. + - **Services & Actions (`app/Services/`, `app/Actions/`)**: + - Happy Path: Đầu vào chuẩn -> kết quả đúng. + - Exception Path: Bắt buộc test mock ném ngoại lệ (`PaymentFailedException`,...). + - Nếu có `DB::transaction()`: Test cả commit thành công và rollback khi gặp lỗi. + - **Jobs (`app/Jobs/`)**: + - BẮT BUỘC test phương thức **`handle()`** (với đầy đủ mock S3, Queue, DB, Mail). + - BẮT BUỘC test phương thức **`failed(Throwable $e)`**. Tuyệt đối cấm chỉ test helper phụ. + - **Policies (`app/Policies/`)**: + - Mọi action (`view`, `create`, `update`, `delete`,...) bắt buộc test cả 2 kịch bản: `true` (User có quyền) và `false` (User không có quyền). + - **Artisan Commands (`app/Console/Commands/`)**: + - Test exit code 0 (`assertExitCode(0)`) khi arguments hợp lệ. + - Test exit code 1 khi thiếu arguments hoặc command gặp Exception. + - **Repositories (`app/Repositories/`)**: + - Test kết quả truy vấn / cập nhật dữ liệu thật trên SQLite in-memory (cấm chỉ assert `Builder::class`). ### Bước 5: Sinh Mã Nguồn Test 1. Xác định đường dẫn file test theo quy tắc Mirror: diff --git a/docs/10-sonarqube-100-percent-coverage-guide.md b/docs/10-sonarqube-100-percent-coverage-guide.md index 7a50cbb..8891c35 100644 --- a/docs/10-sonarqube-100-percent-coverage-guide.md +++ b/docs/10-sonarqube-100-percent-coverage-guide.md @@ -47,8 +47,8 @@ sonar.php.tests.reportPath=phpunit-report.xml # Inclusions: sonar.coverage.inclusions=app/Services/**,app/Actions/**,app/Http/Controllers/**,app/Http/Requests/**,app/Rules/**,app/Jobs/**,app/Policies/**,app/Models/**,app/Repositories/**,app/Console/Commands/**,app/Listeners/**,app/Notifications/**,app/Mail/** -# Exclusions: -sonar.coverage.exclusions=app/Providers/**,app/Console/Kernel.php,app/Http/Middleware/TrustProxies.php,bootstrap/**,config/**,database/**,tests/** +# Exclusions: Loại trừ toàn bộ boilerplate, configs, migrations để đảm bảo Coverage > 90% +sonar.coverage.exclusions=app/Providers/**,app/Console/Kernel.php,app/Exceptions/Handler.php,app/Http/Kernel.php,app/Http/Middleware/TrustHosts.php,app/Http/Middleware/TrustProxies.php,app/Http/Middleware/VerifyCsrfToken.php,app/Http/Middleware/EncryptCookies.php,app/Http/Middleware/TrimStrings.php,app/Http/Middleware/PreventRequestsDuringMaintenance.php,bootstrap/**,config/**,database/**,tests/** sonar.cpd.exclusions=tests/** ``` diff --git a/templates/configs/sonar-project.properties.template b/templates/configs/sonar-project.properties.template index 832dca2..fec37a6 100644 --- a/templates/configs/sonar-project.properties.template +++ b/templates/configs/sonar-project.properties.template @@ -18,5 +18,5 @@ sonar.php.tests.reportPath=phpunit-report.xml sonar.coverage.inclusions=app/Services/**,app/Actions/**,app/Http/Controllers/**,app/Http/Requests/**,app/Rules/**,app/Jobs/**,app/Policies/**,app/Models/**,app/Repositories/**,app/Console/Commands/**,app/Listeners/**,app/Notifications/**,app/Mail/** # Exclusions: Loại trừ boilerplate, migrations, seeders, config để đảm bảo Coverage > 90% -sonar.coverage.exclusions=app/Providers/**,app/Console/Kernel.php,app/Http/Middleware/TrustProxies.php,bootstrap/**,config/**,database/**,tests/** +sonar.coverage.exclusions=app/Providers/**,app/Console/Kernel.php,app/Exceptions/Handler.php,app/Http/Kernel.php,app/Http/Middleware/TrustHosts.php,app/Http/Middleware/TrustProxies.php,app/Http/Middleware/VerifyCsrfToken.php,app/Http/Middleware/EncryptCookies.php,app/Http/Middleware/TrimStrings.php,app/Http/Middleware/PreventRequestsDuringMaintenance.php,bootstrap/**,config/**,database/**,tests/** sonar.cpd.exclusions=tests/**