yupdates 0.1.1

Yupdates Rust SDK
Documentation
//! These tests require the YUPDATES_TEST_FEED_SPECIFIC_TOKEN and YUPDATES_TEST_RO_TOKEN
//! environment variables.
use crate::{random_test_items, test_clients};
use yupdates::api::ReadOptions;
use yupdates::errors::{Kind, Result};

/// Add items to a pre-existing feed and read items back out with various query parameters
#[tokio::test]
async fn read_item_parameters() -> Result<()> {
    let (ro_client, feed_client) = test_clients()?;
    let (input_items, suffixes) = random_test_items(33);
    let feed_id = feed_client.new_items_all(&input_items, 128).await?;

    // The basic, most-recent items query:
    let most_recent_ten_items = ro_client.read_items(&feed_id).await?;
    assert_eq!(most_recent_ten_items.len(), 10);
    for (i, feed_item) in most_recent_ten_items.iter().enumerate() {
        let expected_suffix = suffixes.get(i).unwrap();
        assert_eq!(feed_item.title, format!("title-{}", expected_suffix));
        assert_eq!(feed_item.content, None);
        // Files are only present when content is requested:
        if let Some(files) = &feed_item.associated_files {
            assert!(files.is_empty());
        }
    }

    // With content:
    let options = ReadOptions {
        include_item_content: true,
        ..Default::default()
    };
    let ten_items_with_content = ro_client
        .read_items_with_options(&feed_id, &options)
        .await?;
    assert_eq!(ten_items_with_content.len(), 10);
    for (i, feed_item) in ten_items_with_content.iter().enumerate() {
        let expected_suffix = suffixes.get(i).unwrap();
        assert_eq!(feed_item.title, format!("title-{}", expected_suffix));
        assert_eq!(
            feed_item.content,
            Some(format!("content-{}", expected_suffix))
        );
        if i % 2 == 0 {
            if let Some(files) = &feed_item.associated_files {
                assert!(files.is_empty());
            }
        } else {
            assert!(feed_item.associated_files.is_some());
            let files = feed_item.associated_files.as_ref().unwrap();
            assert_eq!(files.len(), 1);
            assert_eq!(
                files[0].url,
                format!("https://www.example.com/file-{}", expected_suffix)
            )
        }
    }

    // Read all of the ones we just added. We can test more queries/bounds in the future with
    // unique feeds per test (new-feed API).
    let options = ReadOptions {
        max_items: 33,
        ..Default::default()
    };
    let all_items = ro_client
        .read_items_with_options(&feed_id, &options)
        .await?;
    assert_eq!(all_items.len(), 33);
    let mut tenth_item_time = None;
    for (i, feed_item) in all_items.iter().enumerate() {
        let expected_suffix = suffixes.get(i).unwrap();
        assert_eq!(feed_item.title, format!("title-{}", expected_suffix));
        assert_eq!(feed_item.content, None);
        assert_eq!(
            feed_item.canonical_url,
            format!("https://www.example.com/{}", expected_suffix)
        );
        if i == 9 {
            tenth_item_time = Some(feed_item.item_time.clone());
        }
    }

    // Query for items 11-23:
    let options = ReadOptions {
        max_items: 13,
        item_time_before: Some(tenth_item_time.clone().unwrap()),
        ..Default::default()
    };
    let middle_items = ro_client
        .read_items_with_options(&feed_id, &options)
        .await?;
    assert_eq!(middle_items.len(), 13);
    for (i, feed_item) in middle_items.iter().enumerate() {
        let expected_suffix = suffixes.get(i + 10).unwrap();
        assert_eq!(feed_item.title, format!("title-{}", expected_suffix));
    }

    // Query for items 5-9:
    let options = ReadOptions {
        max_items: 5,
        item_time_after: Some(tenth_item_time.unwrap()),
        ..Default::default()
    };
    let nine_to_five = ro_client
        .read_items_with_options(&feed_id, &options)
        .await?;
    assert_eq!(nine_to_five.len(), 5);
    for (i, feed_item) in nine_to_five.iter().enumerate() {
        let expected_suffix = suffixes.get(i + 4).unwrap();
        assert_eq!(feed_item.title, format!("title-{}", expected_suffix));
    }

    Ok(())
}

/// Exercise invalid options that are caught client-side
#[tokio::test]
async fn illegal_read_items() -> Result<()> {
    let feed_id = "02fb24a4478462a4491067224b66d9a8b2338ddca2737".to_string();
    let (ro_client, _) = test_clients()?;

    // Simultaneous use of `item_time_after` and `item_time_before`:
    let options = ReadOptions {
        max_items: 5,
        item_time_after: Some("1234567890".to_string()),
        item_time_before: Some("1234567890".to_string()),
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("cannot simultaneously query"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Querying for more than 50:
    let options = ReadOptions {
        max_items: 51,
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("1 to 50"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Querying for more than 10 when `include_item_content` is true:
    let options = ReadOptions {
        max_items: 11,
        include_item_content: true,
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("1 to 10 when"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Illegal item_time (`item_time_after` is not a number):
    let options = ReadOptions {
        max_items: 5,
        item_time_after: Some("1234567890x".to_string()),
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("invalid u64"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Illegal item_time (`item_time_before` is not a number):
    let options = ReadOptions {
        max_items: 5,
        item_time_before: Some("1234567890x".to_string()),
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("invalid u64"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Illegal item_time (base ms is too large):
    let options = ReadOptions {
        max_items: 5,
        item_time_before: Some("99999999999990".to_string()),
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("may not be larger than"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    // Illegal item_time (suffix is too large):
    let options = ReadOptions {
        max_items: 5,
        item_time_before: Some("123456789.1234567".to_string()),
        ..Default::default()
    };
    let result = ro_client.read_items_with_options(&feed_id, &options).await;
    assert!(result.is_err());
    match result.unwrap_err().kind {
        Kind::IllegalParameter(text) => {
            assert!(text.contains("may not be larger than"))
        }
        e => {
            panic!("unexpected error type: {:?}", e)
        }
    }

    Ok(())
}