gouqi 0.20.0

Rust interface for Jira
Documentation
//! Tests for Jira Cloud API version detection and endpoint selection
//!
//! This test verifies that the implementation correctly:
//! 1. Detects Jira Cloud deployment type
//! 2. Selects V3 search API for Cloud instances
//! 3. Constructs correct V3 search URLs

use gouqi::core::{ClientCore, JiraDeploymentType, SearchApiVersion};
use gouqi::{Credentials, Jira};

#[test]
fn test_cloud_deployment_detection() {
    let core = ClientCore::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create ClientCore");

    // Test deployment type detection
    let deployment_type = core.detect_deployment_type();
    assert_eq!(
        deployment_type,
        JiraDeploymentType::Cloud,
        "Should detect gouji.atlassian.net as Cloud deployment"
    );
}

#[test]
fn test_cloud_auto_selects_v3_api() {
    let core = ClientCore::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create ClientCore");

    // Test that Auto resolves to V3 for Cloud
    let selected_version = core.get_search_api_version();
    assert_eq!(
        selected_version,
        SearchApiVersion::V3,
        "Should auto-select V3 API for Cloud deployment"
    );
}

#[test]
fn test_versioned_url_construction_v3() {
    let core = ClientCore::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create ClientCore");

    // Test V3 URL construction
    let url = core
        .build_versioned_url("api", Some("3"), "/search/jql?jql=project=TEST")
        .expect("Failed to build V3 URL");

    let expected = "https://gouji.atlassian.net/rest/api/3/search/jql?jql=project=TEST";
    assert_eq!(
        url.as_str(),
        expected,
        "Should construct correct V3 search URL"
    );
}

#[test]
fn test_versioned_url_construction_v2() {
    let core = ClientCore::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create ClientCore");

    // Test V2 URL construction for comparison
    let url = core
        .build_versioned_url("api", Some("latest"), "/search?jql=project=TEST")
        .expect("Failed to build V2 URL");

    let expected = "https://gouji.atlassian.net/rest/api/latest/search?jql=project=TEST";
    assert_eq!(
        url.as_str(),
        expected,
        "Should construct correct V2 search URL"
    );
}

#[test]
fn test_search_endpoint_selection() {
    let jira = Jira::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create Jira client");

    let search = jira.search();

    // Use reflection to test the internal endpoint selection
    // This verifies the Search struct correctly selects V3 endpoints
    let (api_name, endpoint, version) = search.get_search_endpoint();

    assert_eq!(api_name, "api");
    assert_eq!(endpoint, "/search/jql");
    assert_eq!(version, Some("3"));
}

#[test]
fn test_manual_v3_selection() {
    let jira = Jira::with_search_api_version(
        "https://gouji.atlassian.net",
        Credentials::Anonymous,
        SearchApiVersion::V3,
    )
    .expect("Failed to create Jira client with V3");

    // Verify the configured version is respected
    assert_eq!(jira.get_search_api_version(), SearchApiVersion::V3);
}

#[test]
fn test_manual_v2_override_for_cloud() {
    let jira = Jira::with_search_api_version(
        "https://gouji.atlassian.net",
        Credentials::Anonymous,
        SearchApiVersion::V2,
    )
    .expect("Failed to create Jira client with V2 override");

    // Verify we can override auto-detection
    assert_eq!(jira.get_search_api_version(), SearchApiVersion::V2);

    let search = jira.search();
    let (api_name, endpoint, version) = search.get_search_endpoint();

    assert_eq!(api_name, "api");
    assert_eq!(endpoint, "/search");
    assert_eq!(version, Some("latest"));
}

// Integration test with async client
#[cfg(feature = "async")]
#[tokio::test]
async fn test_async_cloud_api_selection() {
    use gouqi::r#async::Jira as AsyncJira;

    let jira = AsyncJira::new("https://gouji.atlassian.net", Credentials::Anonymous)
        .expect("Failed to create async Jira client");

    // Verify async client also detects Cloud correctly
    assert_eq!(jira.get_search_api_version(), SearchApiVersion::V3);
}

// Real integration test with Jira Cloud using API token
#[test]
fn test_real_jira_cloud_v3_search() {
    let token = match std::env::var("INTEGRATION_JIRA_TOKEN") {
        Ok(token) => token,
        Err(_) => {
            eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN not set, skipping real API test");
            return;
        }
    };

    if token.trim().is_empty() {
        eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN is empty, skipping real API test");
        return;
    }

    println!("๐Ÿงช Testing real Jira Cloud V3 search API...");

    // Use Bearer token authentication (recommended for Cloud)
    let credentials = Credentials::Bearer(token);

    // Test auto-detection - should use V3 for .atlassian.net
    let jira = Jira::new("https://gouji.atlassian.net", credentials.clone())
        .expect("Failed to create Jira client");

    // Verify auto-detection worked
    assert_eq!(
        jira.get_search_api_version(),
        SearchApiVersion::V3,
        "Should auto-select V3 for Cloud"
    );

    // Verify endpoint selection
    let search = jira.search();
    let (api_name, endpoint, version) = search.get_search_endpoint();
    assert_eq!(api_name, "api");
    assert_eq!(endpoint, "/search/jql"); // V3 uses /search/jql
    assert_eq!(version, Some("3"));

    println!("โœ… V3 API auto-detection working correctly");

    // Test actual search with a simple JQL query
    let search_options = gouqi::SearchOptions::builder().max_results(1).build();

    println!("๐Ÿ” Testing V3 search with simple query...");

    // Try a very basic query first
    match search.list("", &search_options) {
        Ok(results) => {
            println!(
                "โœ… V3 search successful! Found {} total issues",
                results.total
            );
            println!("   ๐Ÿ“‹ Search used endpoint: /rest/api/3/search/jql");
            if !results.issues.is_empty() {
                println!("   ๐ŸŽฏ Sample issue key: {}", results.issues[0].key);
            } else {
                println!("   ๐Ÿ“ No issues found (empty instance or no permissions)");
            }
        }
        Err(e) => {
            println!("โš ๏ธ  V3 search with empty query failed: {:?}", e);

            // Try with a more explicit query for empty instances
            println!("๐Ÿ” Trying alternative query for empty instances...");
            match search.list("order by key", &search_options) {
                Ok(results) => {
                    println!(
                        "โœ… V3 search with 'order by key' successful! Found {} total issues",
                        results.total
                    );
                }
                Err(e2) => {
                    println!("โš ๏ธ  Alternative query also failed: {:?}", e2);
                    // This suggests either permissions or parsing issue, not a fundamental API problem
                    println!(
                        "   ๐Ÿ” This indicates the V3 endpoint is accessible but might return different format for empty instances"
                    );
                }
            }
        }
    }
}

// Test explicit V3 selection vs V2 fallback
#[test]
fn test_explicit_api_version_selection() {
    let token = match std::env::var("INTEGRATION_JIRA_TOKEN") {
        Ok(token) => token,
        Err(_) => {
            eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN not set, skipping API version test");
            return;
        }
    };

    if token.trim().is_empty() {
        eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN is empty, skipping API version test");
        return;
    }

    let credentials = Credentials::Bearer(token);

    println!("๐Ÿงช Testing explicit V3 selection...");

    // Explicitly request V3
    let jira_v3 = Jira::with_search_api_version(
        "https://gouji.atlassian.net",
        credentials.clone(),
        SearchApiVersion::V3,
    )
    .expect("Failed to create V3 Jira client");

    assert_eq!(jira_v3.get_search_api_version(), SearchApiVersion::V3);

    let search_v3 = jira_v3.search();
    let (_api_name, endpoint, version) = search_v3.get_search_endpoint();
    assert_eq!(
        endpoint, "/search/jql",
        "V3 should use /search/jql endpoint"
    );
    assert_eq!(version, Some("3"), "V3 should use version '3'");

    println!("โœ… Explicit V3 selection working correctly");

    println!("๐Ÿงช Testing V2 fallback override...");

    // Test V2 override (should work but might get deprecated responses)
    let jira_v2 = Jira::with_search_api_version(
        "https://gouji.atlassian.net",
        credentials,
        SearchApiVersion::V2,
    )
    .expect("Failed to create V2 Jira client");

    assert_eq!(jira_v2.get_search_api_version(), SearchApiVersion::V2);

    let search_v2 = jira_v2.search();
    let (_api_name, endpoint, version) = search_v2.get_search_endpoint();
    assert_eq!(endpoint, "/search", "V2 should use /search endpoint");
    assert_eq!(version, Some("latest"), "V2 should use 'latest' version");

    println!("โœ… V2 override selection working correctly");
    println!("๐Ÿ“ Note: V2 might return deprecation warnings from Jira Cloud");
}

// Async version of the real integration test
#[cfg(feature = "async")]
#[tokio::test]
async fn test_real_async_jira_cloud_v3_search() {
    use gouqi::r#async::Jira as AsyncJira;

    let token = match std::env::var("INTEGRATION_JIRA_TOKEN") {
        Ok(token) => token,
        Err(_) => {
            eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN not set, skipping async API test");
            return;
        }
    };

    if token.trim().is_empty() {
        eprintln!("โš ๏ธ  INTEGRATION_JIRA_TOKEN is empty, skipping async API test");
        return;
    }

    println!("๐Ÿงช Testing real async Jira Cloud V3 search API...");

    let credentials = Credentials::Bearer(token);
    let jira = AsyncJira::new("https://gouji.atlassian.net", credentials)
        .expect("Failed to create async Jira client");

    // Verify auto-detection
    assert_eq!(jira.get_search_api_version(), SearchApiVersion::V3);

    let search = jira.search();
    let search_options = gouqi::SearchOptions::builder().max_results(1).build();

    match search.list("ORDER BY created DESC", &search_options).await {
        Ok(results) => {
            println!(
                "โœ… Async V3 search successful! Found {} total issues",
                results.total
            );
        }
        Err(e) => {
            println!(
                "โš ๏ธ  Async V3 search returned error (might be permissions): {:?}",
                e
            );
        }
    }
}

#[test]
fn test_non_cloud_deployment_detection() {
    // Test that non-.atlassian.net domains are not detected as Cloud
    let core = ClientCore::new("https://jira.company.com", Credentials::Anonymous)
        .expect("Failed to create ClientCore for on-premise");

    let deployment_type = core.detect_deployment_type();
    assert_eq!(
        deployment_type,
        JiraDeploymentType::Unknown,
        "Should detect on-premise as Unknown deployment"
    );

    // Should default to V2 for Unknown/Server deployments
    let selected_version = core.get_search_api_version();
    assert_eq!(
        selected_version,
        SearchApiVersion::V2,
        "Should auto-select V2 API for on-premise deployment"
    );
}