assay-lua 0.17.2

General-purpose enhanced Lua runtime. Batteries-included scripting, automation, and web services.
Documentation
mod common;

use common::run_lua;
use wiremock::matchers::{header, header_exists, method, path, query_param};
use wiremock::{Mock, MockServer, ResponseTemplate};

#[tokio::test]
async fn test_require_sonarqube() {
    let script = r#"
        local sonarqube = require("assay.sonarqube")
        assert.not_nil(sonarqube)
        assert.not_nil(sonarqube.client)
    "#;
    run_lua(script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_project_status() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/qualitygates/project_status"))
        .and(query_param("projectKey", "demo-project"))
        .and(header("Authorization", "Bearer test-token"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "projectStatus": {
                "status": "OK",
                "conditions": [
                    {"status": "OK", "metricKey": "new_coverage", "actualValue": "85.0"}
                ]
            }
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local s = c.qualitygate:project_status("demo-project")
        assert.eq(s.projectStatus.status, "OK")
        assert.eq(#s.projectStatus.conditions, 1)
        assert.eq(s.projectStatus.conditions[1].metricKey, "new_coverage")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_project_status_not_found() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/qualitygates/project_status"))
        .respond_with(ResponseTemplate::new(404))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local s = c.qualitygate:project_status("missing")
        assert.eq(s, nil)
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_issues_search() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/issues/search"))
        .and(query_param("componentKeys", "demo-project"))
        .and(query_param("severities", "BLOCKER"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "total": 2,
            "issues": [
                {"key": "AY-1", "severity": "BLOCKER", "type": "BUG"},
                {"key": "AY-2", "severity": "BLOCKER", "type": "VULNERABILITY"}
            ]
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local r = c.issues:search({{ component_keys = "demo-project", severities = "BLOCKER" }})
        assert.eq(r.total, 2)
        assert.eq(#r.issues, 2)
        assert.eq(r.issues[1].key, "AY-1")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_hotspots_search() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/hotspots/search"))
        .and(query_param("projectKey", "demo-project"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "paging": {"pageIndex": 1, "pageSize": 100, "total": 1},
            "hotspots": [
                {"key": "HS-1", "status": "TO_REVIEW", "vulnerabilityProbability": "HIGH"}
            ]
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local r = c.hotspots:search({{ project_key = "demo-project" }})
        assert.eq(#r.hotspots, 1)
        assert.eq(r.hotspots[1].status, "TO_REVIEW")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_measures_component() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/measures/component"))
        .and(query_param("component", "demo-project"))
        .and(query_param("metricKeys", "coverage,bugs"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "component": {
                "key": "demo-project",
                "measures": [
                    {"metric": "coverage", "value": "82.5"},
                    {"metric": "bugs", "value": "3"}
                ]
            }
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local r = c.measures:component("demo-project", {{ "coverage", "bugs" }})
        assert.eq(r.component.key, "demo-project")
        assert.eq(#r.component.measures, 2)
        assert.eq(r.component.measures[1].metric, "coverage")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_projects_search() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/projects/search"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "paging": {"pageIndex": 1, "pageSize": 100, "total": 2},
            "components": [
                {"key": "demo-project", "name": "Demo Project"},
                {"key": "project-1", "name": "Project One"}
            ]
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local r = c.projects:search({{ query = "demo" }})
        assert.eq(#r.components, 2)
        assert.eq(r.components[1].key, "demo-project")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_error_status() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/issues/search"))
        .respond_with(ResponseTemplate::new(500).set_body_string("internal error"))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ token = "test-token" }})
        local ok, err = pcall(function() c.issues:search({{}}) end)
        assert.eq(ok, false)
        assert.contains(tostring(err), "HTTP 500")
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}

#[tokio::test]
async fn test_sonarqube_basic_auth() {
    let server = MockServer::start().await;
    Mock::given(method("GET"))
        .and(path("/api/projects/search"))
        .and(header_exists("Authorization"))
        .respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
            "components": []
        })))
        .mount(&server)
        .await;

    let script = format!(
        r#"
        local sonarqube = require("assay.sonarqube")
        local c = sonarqube.client("{}", {{ user = "squid", password = "s3cr3t" }})
        local r = c.projects:search()
        assert.eq(#r.components, 0)
        "#,
        server.uri()
    );
    run_lua(&script).await.unwrap();
}