diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index adf21d1..90feea9 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -10,44 +10,82 @@ on: jobs: sonar: - runs-on: ubuntu-latest + runs-on: ubuntu-22.04 steps: - uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Install Rust toolchain + - name: Install Rust Toolchain & Components uses: dtolnay/rust-toolchain@stable with: - components: clippy + components: clippy, llvm-tools-preview - name: Install grcov - run: cargo install grcov + run: cargo install grcov --force - - name: Generate coverage - run: | - export CARGO_INCREMENTAL=0 - export RUSTFLAGS="-Cinstrument-coverage" - export LLVM_PROFILE_FILE="coverage-%p-%m.profraw" - - cargo test + - name: Run Tests with Coverage Instrumentation + env: + CARGO_INCREMENTAL: "0" + RUSTFLAGS: "-Cinstrument-coverage" + LLVM_PROFILE_FILE: "%m.profraw" + run: cargo test --all --verbose - grcov . --binary-path ./target/debug/ \ + - name: Generate Coverage Report (lcov format) + run: | + grcov . \ + --binary-path ./target/debug/deps/ \ -s . \ -t lcov \ --branch \ --ignore-not-existing \ + --ignore "/*" \ -o lcov.info + - name: Convert lcov to SonarCloud Generic Coverage format + run: | + python3 << 'EOF' + import re + + coverage = {} + current_file = None + + with open('lcov.info', 'r') as f: + for line in f: + if line.startswith('SF:'): + current_file = line[3:].strip() + if current_file not in coverage: + coverage[current_file] = [] + elif line.startswith('DA:') and current_file: + parts = line[3:].strip().split(',') + if len(parts) >= 2: + line_num = parts[0] + hits = parts[1] + coverage[current_file].append((line_num, hits)) + + with open('coverage.xml', 'w') as f: + f.write('\n') + f.write('\n') + for filepath, lines in coverage.items(): + if lines: + f.write(f' \n') + for line_num, hits in lines: + f.write(f' 0 else "false"}"/>\n') + f.write(' \n') + f.write('\n') + EOF + + - name: Delete profraw files + run: find . -name "*.profraw" -type f -delete + - name: SonarCloud Scan uses: SonarSource/sonarcloud-github-action@v2 env: SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }} GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: - args: > - -Dsonar.projectKey=morutine_backend + args: > + -Dsonar.projectKey=MORUTINE_backend -Dsonar.organization=morutine - -Dsonar.projectBaseDir=. - -Dsonar.coverageReportPaths=lcov.info + -Dsonar.coverageReportPaths=coverage.xml \ No newline at end of file diff --git a/morutine-api/src/common/error/error_into_response.rs b/morutine-api/src/common/error/error_into_response.rs index 70d3d94..410fd31 100644 --- a/morutine-api/src/common/error/error_into_response.rs +++ b/morutine-api/src/common/error/error_into_response.rs @@ -18,7 +18,6 @@ impl IntoResponse for AppError { let body = Json(json!({ "code": reason.code, "message": reason.message, - "data": {} })); (StatusCode::from_u16(reason.status).unwrap(), body).into_response() @@ -30,9 +29,7 @@ impl IntoResponse for DatabaseApiError { fn into_response(self) -> Response { let app_error: AppError = match self.0 { DatabaseErrorCode::UniqueViolation(_) => AppError::new(CommonErrorCode::Conflict), - DatabaseErrorCode::NotFound => AppError::new(CommonErrorCode::NotFound), - _ => AppError::new(CommonErrorCode::InternalServerError), }; diff --git a/morutine-api/src/common/mod.rs b/morutine-api/src/common/mod.rs index 52897d3..a91e735 100644 --- a/morutine-api/src/common/mod.rs +++ b/morutine-api/src/common/mod.rs @@ -1,2 +1 @@ pub mod error; -pub mod response; diff --git a/morutine-api/src/common/response/api_response.rs b/morutine-api/src/common/response/api_response.rs deleted file mode 100644 index 8c11d15..0000000 --- a/morutine-api/src/common/response/api_response.rs +++ /dev/null @@ -1,40 +0,0 @@ -use axum::{Json, http::StatusCode, response::IntoResponse}; -use common::constant::status::*; -use serde::Serialize; - -#[derive(Debug, Serialize)] -pub struct ApiResponse { - pub status: u16, - pub code: &'static str, - pub message: Option<&'static str>, - pub data: Option, -} - -impl IntoResponse for ApiResponse { - fn into_response(self) -> axum::response::Response { - let status = StatusCode::from_u16(self.status).unwrap_or(StatusCode::OK); - let body = Json(self); - - (status, body).into_response() - } -} - -impl ApiResponse { - pub fn success(data: T) -> Self { - ApiResponse { - status: OK, - code: "OK", - message: None, - data: Some(data), - } - } - - pub fn no_content() -> Self { - ApiResponse { - status: NO_CONTENT, - code: "NO_CONTENT", - message: None, - data: None, - } - } -} diff --git a/morutine-api/src/common/response/mod.rs b/morutine-api/src/common/response/mod.rs deleted file mode 100644 index 4f99ff6..0000000 --- a/morutine-api/src/common/response/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod api_response; diff --git a/morutine-api/src/domain/system/handlers.rs b/morutine-api/src/domain/system/handlers.rs index 5152c1d..6a8bb4a 100644 --- a/morutine-api/src/domain/system/handlers.rs +++ b/morutine-api/src/domain/system/handlers.rs @@ -1,6 +1,7 @@ -use crate::common::response::api_response::ApiResponse; +use axum::Json; use axum::response::IntoResponse; +use serde_json::json; pub async fn health_check() -> impl IntoResponse { - ApiResponse::success("OK") + Json(json!({"status": "OK"})) }