twitch_highway 0.2.1

Twitch API
Documentation
use serde::Serialize;

use crate::types::RedemptionId;

use super::types::RedemptionStatus;

define_request!(
    #[derive(Serialize)]
    CustomRewardsRequiredBody<'a> {
        req: {
            title: &'a str,
            cost: u64
        };
    }
);

define_request!(
    #[derive(Serialize)]
    CustomRewardsBody<'a> {
        opts: {
            #[serde(skip_serializing_if = "Option::is_none")]
            title: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            prompt: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            background_color: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            cost: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_user_input_required: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_max_per_stream_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            max_per_stream: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_max_per_user_per_stream_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            max_per_user_per_stream: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_global_cooldown_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            global_cooldown_seconds: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            should_redemptions_skip_request_queue: bool
        };
    }
);

define_request!(
    #[derive(Serialize)]
    RedemptionStatusQuery {
        req: {
            status: RedemptionStatus
        };
        into_json
    }
);

define_request!(
    #[derive(Serialize)]
    CustomRewardRedemptionQuery<'a> {
        opts: {
            sort: &'a str,
            status: RedemptionStatus,
            id: RedemptionId,
        };
        into_query
    }
);

define_request!(
    #[derive(Serialize, Default)]
    UpdateCustomRewardRequest<'a> {
        opts: {
            #[serde(skip_serializing_if = "Option::is_none")]
            title: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            prompt: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            background_color: &'a str,
            #[serde(skip_serializing_if = "Option::is_none")]
            cost: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_user_input_required: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_max_per_stream_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            max_per_stream: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_max_per_user_per_stream_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            max_per_user_per_stream: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_global_cooldown_enabled: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            global_cooldown_seconds: u64,
            #[serde(skip_serializing_if = "Option::is_none")]
            is_paused: bool,
            #[serde(skip_serializing_if = "Option::is_none")]
            should_redemptions_skip_request_queue: bool
        };
        into_json
    }
);
#[cfg(test)]
mod tests {
    use serde_json::json;

    use crate::{
        channel_points::{
            request::{
                CustomRewardRedemptionQuery, CustomRewardsBody, CustomRewardsRequiredBody,
                RedemptionStatusQuery, UpdateCustomRewardRequest,
            },
            types::RedemptionStatus,
        },
        types::RedemptionId,
    };

    #[test]
    fn custom_rewards_required_body_serialization() {
        let body = CustomRewardsRequiredBody::new("Test Reward", 1000);

        let json = serde_json::to_string(&body).unwrap();
        let expected = json!({
            "title": "Test Reward",
            "cost": 1000
        });

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&json).unwrap(),
            expected
        );
    }

    #[test]
    fn custom_rewards_body_serialization() {
        let body = CustomRewardsBody::new()
            .title("Updated Reward")
            .prompt("This is a test reward")
            .background_color("#9147FF")
            .cost(500)
            .is_enabled(true)
            .is_user_input_required(false)
            .is_max_per_stream_enabled(true)
            .max_per_stream(10)
            .is_max_per_user_per_stream_enabled(true)
            .max_per_user_per_stream(1)
            .is_global_cooldown_enabled(true)
            .global_cooldown_seconds(300)
            .should_redemptions_skip_request_queue(false);

        let serialized = serde_json::to_string(&body).unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&serialized).unwrap();

        assert_eq!(parsed["title"], "Updated Reward");
        assert_eq!(parsed["prompt"], "This is a test reward");
        assert_eq!(parsed["background_color"], "#9147FF");
        assert_eq!(parsed["cost"], 500);
        assert_eq!(parsed["is_enabled"], true);
        assert_eq!(parsed["is_user_input_required"], false);
        assert_eq!(parsed["max_per_stream"], 10);
        assert_eq!(parsed["global_cooldown_seconds"], 300);
    }

    #[test]
    fn update_custom_reward_request_serialization() {
        let request = UpdateCustomRewardRequest::new()
            .title("Updated Title")
            .prompt("Updated prompt")
            .cost(750)
            .is_enabled(false)
            .is_paused(true);

        let json = request.into_json().unwrap();
        let parsed: serde_json::Value = serde_json::from_str(&json).unwrap();

        assert_eq!(parsed["title"], "Updated Title");
        assert_eq!(parsed["prompt"], "Updated prompt");
        assert_eq!(parsed["cost"], 750);
        assert_eq!(parsed["is_enabled"], false);
        assert_eq!(parsed["is_paused"], true);
    }

    #[test]
    fn redemption_status_query_serialization() {
        let query = RedemptionStatusQuery::new(RedemptionStatus::FULFILLED);

        let json = query.into_json().unwrap();
        let expected = json!({
            "status": "FULFILLED"
        });

        assert_eq!(
            serde_json::from_str::<serde_json::Value>(&json).unwrap(),
            expected
        );
    }

    #[test]
    fn custom_reward_redemption_query() {
        let query = CustomRewardRedemptionQuery::new()
            .sort("newest")
            .status(RedemptionStatus::UNFULFILLED)
            .id(RedemptionId::from("redemption123"));

        let mut url = url::Url::parse("https://api.twitch.tv/helix").unwrap();
        let mut query_pairs = url.query_pairs_mut();
        query.into_query(&mut query_pairs);
        drop(query_pairs);

        let query_str = url.query().unwrap();
        assert!(query_str.contains("sort=newest"));
        assert!(query_str.contains("status=UNFULFILLED"));
        assert!(query_str.contains("id=redemption123"));
    }
}