wakapi 0.3.1

Wakatime API client
Documentation
//! Integration tests for all wakapi endpoints.
//!
//! Requires environment variables:
//!   WAKAPI_URL  - base URL of the Wakatime-compatible server (e.g. https://wakatime.com)
//!   WAKAPI_KEY  - API key (plain text; the client Base64-encodes it internally)
//!
//! Run async tests:    cargo test --test integration
//! Run blocking tests: cargo test --features blocking --test integration

use wakapi::{
    AllTimeSinceToday, AllTimeSinceTodayParams, CommitParams, CommitsParams, Durations,
    DurationsParams, Heartbeats, HeartbeatsParams, Projects, ProjectsParams, Stats, StatsParams,
    Summaries, SummariesParams, SummaryRange, WakapiClient,
};

fn make_client() -> Option<WakapiClient> {
    let url = std::env::var("WAKAPI_URL").ok()?;
    let key = std::env::var("WAKAPI_KEY").ok()?;
    Some(WakapiClient::new(&url, &key))
}

fn today() -> chrono::NaiveDate {
    chrono::Local::now().date_naive()
}

fn today_str() -> String {
    today().format("%Y-%m-%d").to_string()
}

// ---------------------------------------------------------------------------
// Blocking tests
// ---------------------------------------------------------------------------
#[cfg(feature = "blocking")]
mod blocking {
    use super::*;

    #[test]
    fn test_all_time_since_today() {
        let Some(client) = make_client() else { return };
        let result = AllTimeSinceToday::fetch(&client, AllTimeSinceTodayParams::new())
            .expect("all_time_since_today failed");
        match result {
            AllTimeSinceToday::Success(body) | AllTimeSinceToday::WillRefresh(body) => {
                assert!(body.data.total_seconds >= 0.0);
            }
        }
    }

    #[test]
    fn test_durations() {
        let Some(client) = make_client() else { return };
        let params = DurationsParams::new(Some(today_str()));
        let result = Durations::fetch(&client, params).expect("durations failed");
        let _ = result.data;
    }

    #[test]
    fn test_summaries() {
        let Some(client) = make_client() else { return };
        let params = SummariesParams::from_range(SummaryRange::Today);
        let result = Summaries::fetch(&client, params).expect("summaries failed");
        let _ = result.data;
    }

    #[test]
    fn test_heartbeats() {
        let Some(client) = make_client() else { return };
        let params = HeartbeatsParams::new(today());
        let result = Heartbeats::fetch(&client, params).expect("heartbeats failed");
        let _ = result.data;
    }

    #[test]
    fn test_projects() {
        let Some(client) = make_client() else { return };
        let result = Projects::fetch(&client, ProjectsParams::new()).expect("projects failed");
        assert!(result.total_pages >= 1);
    }

    #[test]
    fn test_stats() {
        let Some(client) = make_client() else { return };
        let params = StatsParams::from_range("last_7_days");
        let result = Stats::fetch(&client, params).expect("stats failed");
        assert!(!result.data.username.is_empty());
    }

    #[test]
    fn test_commits_and_commit() {
        let Some(client) = make_client() else { return };

        let projects =
            Projects::fetch(&client, ProjectsParams::new()).expect("projects fetch failed");
        let Some(project) = projects.data.first() else {
            eprintln!("No projects found; skipping commits test");
            return;
        };

        let commits_result =
            wakapi::Commits::fetch(&client, CommitsParams::new(&project.name));
        let commits = match commits_result {
            Ok(c) => c,
            Err(e) => {
                eprintln!("Commits fetch skipped ({e}); project may have no synced commits");
                return;
            }
        };

        let Some(first_commit) = commits.commits.first() else {
            eprintln!("No commits found for project '{}'; skipping single-commit test", project.name);
            return;
        };

        let commit_result = wakapi::Commit::fetch(
            &client,
            CommitParams::new(&project.name, &first_commit.hash),
        );
        let commit = match commit_result {
            Ok(c) => c,
            Err(e) => {
                eprintln!("Single-commit fetch skipped ({e})");
                return;
            }
        };
        assert_eq!(commit.commit.hash, first_commit.hash);
    }
}

// ---------------------------------------------------------------------------
// Async tests
// ---------------------------------------------------------------------------
#[cfg(not(feature = "blocking"))]
mod async_tests {
    use super::*;

    #[tokio::test]
    async fn test_all_time_since_today() {
        let Some(client) = make_client() else { return };
        let result = AllTimeSinceToday::fetch(&client, AllTimeSinceTodayParams::new())
            .await
            .expect("all_time_since_today failed");
        match result {
            AllTimeSinceToday::Success(body) | AllTimeSinceToday::WillRefresh(body) => {
                assert!(body.data.total_seconds >= 0.0);
            }
        }
    }

    #[tokio::test]
    async fn test_durations() {
        let Some(client) = make_client() else { return };
        let params = DurationsParams::new(Some(today_str()));
        let result = Durations::fetch(&client, params).await.expect("durations failed");
        let _ = result.data;
    }

    #[tokio::test]
    async fn test_summaries() {
        let Some(client) = make_client() else { return };
        let params = SummariesParams::from_range(SummaryRange::Today);
        let result = Summaries::fetch(&client, params).await.expect("summaries failed");
        let _ = result.data;
    }

    #[tokio::test]
    async fn test_heartbeats() {
        let Some(client) = make_client() else { return };
        let params = HeartbeatsParams::new(today());
        let result = Heartbeats::fetch(&client, params).await.expect("heartbeats failed");
        let _ = result.data;
    }

    #[tokio::test]
    async fn test_projects() {
        let Some(client) = make_client() else { return };
        let result = Projects::fetch(&client, ProjectsParams::new())
            .await
            .expect("projects failed");
        assert!(result.total_pages >= 1);
    }

    #[tokio::test]
    async fn test_stats() {
        let Some(client) = make_client() else { return };
        let params = StatsParams::from_range("last_7_days");
        let result = Stats::fetch(&client, params).await.expect("stats failed");
        assert!(!result.data.username.is_empty());
    }

    #[tokio::test]
    async fn test_commits_and_commit() {
        let Some(client) = make_client() else { return };

        let projects = Projects::fetch(&client, ProjectsParams::new())
            .await
            .expect("projects fetch failed");
        let Some(project) = projects.data.first() else {
            eprintln!("No projects found; skipping commits test");
            return;
        };

        let commits_result =
            wakapi::Commits::fetch(&client, CommitsParams::new(&project.name)).await;
        let commits = match commits_result {
            Ok(c) => c,
            Err(e) => {
                eprintln!("Commits fetch skipped ({e}); project may have no synced commits");
                return;
            }
        };

        let Some(first_commit) = commits.commits.first() else {
            eprintln!("No commits found for project '{}'; skipping single-commit test", project.name);
            return;
        };

        let commit_result = wakapi::Commit::fetch(
            &client,
            CommitParams::new(&project.name, &first_commit.hash),
        )
        .await;
        let commit = match commit_result {
            Ok(c) => c,
            Err(e) => {
                eprintln!("Single-commit fetch skipped ({e})");
                return;
            }
        };
        assert_eq!(commit.commit.hash, first_commit.hash);
    }
}