tovuk 0.1.114

Use Tovuk scraper APIs from a native CLI.
use serde_json::json;

use crate::cli::args::CliOptions;

use super::{billing_checkout_body, billing_session_url};

#[test]
fn billing_checkout_body_requires_explicit_plan() {
    let cli = CliOptions {
        args: vec!["checkout".to_owned()],
        ..CliOptions::default()
    };

    let message = billing_checkout_body(&cli)
        .err()
        .map(|error| error.to_string());

    assert_eq!(message.as_deref(), Some("Billing plan is required."));
}

#[test]
fn billing_checkout_body_rejects_unknown_plan() {
    let cli = CliOptions {
        args: vec!["checkout".to_owned(), "business".to_owned()],
        ..CliOptions::default()
    };

    let message = billing_checkout_body(&cli)
        .err()
        .map(|error| error.to_string());

    assert_eq!(
        message.as_deref(),
        Some("Billing plan must be plus, pro, or max.")
    );
}

#[test]
fn billing_checkout_body_uses_plan_and_reason() -> Result<(), Box<dyn std::error::Error>> {
    let cli = CliOptions {
        args: vec![
            "checkout".to_owned(),
            "max".to_owned(),
            "scale".to_owned(),
            "usage".to_owned(),
        ],
        ..CliOptions::default()
    };

    let body = billing_checkout_body(&cli)?;

    let expected = json!({
        "target_plan": "max",
        "reason": "scale usage"
    });
    if body != expected {
        return Err(format!("unexpected checkout body: {body}").into());
    }
    Ok(())
}

#[test]
fn billing_checkout_body_uses_top_up_amount_and_reason() -> Result<(), Box<dyn std::error::Error>> {
    let cli = CliOptions {
        args: vec![
            "checkout".to_owned(),
            "more".to_owned(),
            "balance".to_owned(),
        ],
        top_up_usd_cents: "2000".to_owned(),
        ..CliOptions::default()
    };

    let body = billing_checkout_body(&cli)?;

    let expected = json!({
        "top_up_usd_cents": 2000,
        "reason": "more balance"
    });
    if body != expected {
        return Err(format!("unexpected checkout body: {body}").into());
    }
    Ok(())
}

#[test]
fn billing_checkout_body_rejects_plan_with_top_up_amount() {
    let cli = CliOptions {
        args: vec!["checkout".to_owned(), "plus".to_owned()],
        top_up_usd_cents: "2000".to_owned(),
        ..CliOptions::default()
    };

    let message = billing_checkout_body(&cli)
        .err()
        .map(|error| error.to_string());

    assert_eq!(
        message.as_deref(),
        Some("Billing checkout cannot include both a plan and top-up amount.")
    );
}

#[test]
fn billing_checkout_body_rejects_invalid_top_up_amount() {
    let cli = CliOptions {
        args: vec!["checkout".to_owned()],
        top_up_usd_cents: "twenty".to_owned(),
        ..CliOptions::default()
    };

    let message = billing_checkout_body(&cli)
        .err()
        .map(|error| error.to_string());

    assert_eq!(
        message.as_deref(),
        Some("Billing top-up amount must be an integer number of USD cents.")
    );
}

#[test]
fn billing_session_url_reads_checkout_url() -> Result<(), Box<dyn std::error::Error>> {
    let response = json!({
        "checkout": {
            "url": "https://billing.stripe.test/session"
        }
    });

    let url = billing_session_url(&response, false)?;

    if url != "https://billing.stripe.test/session" {
        return Err(format!("unexpected url: {url}").into());
    }
    Ok(())
}

#[test]
fn billing_session_url_rejects_missing_url() -> Result<(), Box<dyn std::error::Error>> {
    let response = json!({});
    let error = match billing_session_url(&response, false) {
        Ok(url) => return Err(format!("unexpected url: {url}").into()),
        Err(error) => error,
    };

    let payload = error.payload();
    if payload.code != "billing_url_missing" {
        return Err(format!("unexpected code: {}", payload.code).into());
    }
    Ok(())
}

#[test]
fn billing_session_url_rejects_blank_url() -> Result<(), Box<dyn std::error::Error>> {
    let response = json!({
        "checkout": {
            "url": " "
        }
    });
    let error = match billing_session_url(&response, false) {
        Ok(url) => return Err(format!("unexpected url: {url}").into()),
        Err(error) => error,
    };

    let payload = error.payload();
    if payload.message != "Tovuk billing did not return a URL." {
        return Err(format!("unexpected message: {}", payload.message).into());
    }
    Ok(())
}