greader_api 0.4.0

rust implementation of the GoogleReader-API
Documentation
use crate::models::{AuthData, AuthInput, Enclosure, Feed, GoogleAuthInput, StreamType};
use crate::GReaderApi;
use reqwest::Client;
use std::env;
use url::Url;

async fn test_setup_env() -> GReaderApi {
    dotenv::dotenv().expect("Failed to read .env file");
    let url = env::var("GREADER_URL").expect("Failed to read GREADER_URL");
    let user = env::var("GREADER_USER").expect("Failed to read GREADER_USER");
    let pw = env::var("GREADER_PW").expect("Failed to read GREADER_PW");

    let auth = GoogleAuthInput {
        username: user,
        password: pw,
    };

    let url = Url::parse(&url).unwrap();
    let api = GReaderApi::new(&url, AuthData::Uninitialized);
    api.login(&AuthInput::Google(auth), &Client::new())
        .await
        .unwrap();
    api
}

#[tokio::test]
async fn user_info() {
    let api = test_setup_env().await;
    let response = api.user_info(&Client::new()).await.unwrap();
    println!("user: {}", response.user_id);
}

#[tokio::test]
async fn unread_count() {
    let api = test_setup_env().await;
    let response = api.unread_count(&Client::new()).await;
    assert!(response.is_ok());
}

#[tokio::test]
async fn subscription_list() {
    let api = test_setup_env().await;
    let response = api.subscription_list(&Client::new()).await.unwrap();
    println!("{:#?}", response);
}

#[tokio::test]
async fn subscription_create_edit_delete() {
    let api = test_setup_env().await;

    let sample_feed = Url::parse("https://feedforall.com/sample.xml").unwrap();
    let sample_feed_stream_id = format!("feed/{}", sample_feed);
    let feed_name = "TESTING";

    let _response = api
        .subscription_delete(&sample_feed_stream_id, &Client::new())
        .await;

    let response = api
        .subscription_create(&sample_feed, Some(feed_name), None, &Client::new())
        .await;
    assert!(response.is_ok());

    let response = api.subscription_list(&Client::new()).await;
    assert!(response.is_ok());
    let mut current_feed: Feed = Feed {
        id: "placeholder".to_string(),
        title: "placeholder".to_string(),
        categories: Vec::new(),
        url: "placeholder".to_string(),
        html_url: "placeholder".to_string(),
        icon_url: "placeholder".to_string(),
        sortid: None,
    };

    for feed in response.unwrap().subscriptions {
        if feed.title == feed_name {
            current_feed = feed;
        }
    }

    let response = api
        .subscription_edit(
            &format!("feed/{}", sample_feed),
            Some("AN EXAMPLE"),
            Some(&current_feed.categories[0].id),
            Some(&current_feed.categories[0].id),
            &Client::new(),
        )
        .await;
    assert!(response.is_ok());

    let response = api
        .subscription_delete(&sample_feed_stream_id, &Client::new())
        .await;
    assert!(response.is_ok());
}

#[tokio::test]
async fn subscription_quickadd() {
    let api = test_setup_env().await;
    let sample_feed = Url::parse("https://feedforall.com/sample-feed.xml").unwrap();
    let sample_feed_stream_id = format!("feed/{}", sample_feed);

    let _response = api
        .subscription_delete(&sample_feed_stream_id, &Client::new())
        .await;

    let response = api
        .subscription_quickadd(&sample_feed, &Client::new())
        .await;
    assert!(response.is_ok());

    let response = api
        .subscription_delete(&sample_feed_stream_id, &Client::new())
        .await;
    assert!(response.is_ok());
}

#[tokio::test]
async fn stream_contents() {
    let api = test_setup_env().await;

    let response = api
        .stream_contents(
            Some("feed/1"),
            false,
            None,
            None,
            None,
            None,
            None,
            None,
            &Client::new(),
        )
        .await
        .unwrap();

    println!("{:#?}", response);
}

#[tokio::test]
async fn item_ids() {
    let api = test_setup_env().await;

    let response = api
        .items_ids(
            Some("feed/1"),
            Some(50),
            false,
            None,
            None,
            None,
            None,
            None,
            &Client::new(),
        )
        .await
        .unwrap();

    println!("{:#?}", response);
}

#[cfg(feature = "feedhq")]
#[tokio::test]
async fn items_count() {
    let api = test_setup_env().await;

    let response = api.items_count("feed/1", false, &Client::new()).await;
    assert!(response.is_ok());
}

#[tokio::test]
async fn items_contents() {
    let api = test_setup_env().await;

    let response = api
        .items_contents(
            vec![
                "1666489196310810".to_string(),
                "1666489196310811".to_string(),
            ],
            &Client::new(),
        )
        .await
        .unwrap();

    println!("{:#?}", response);
}

#[tokio::test]
async fn tag_list() {
    let api = test_setup_env().await;
    let response = api.tag_list(&Client::new()).await;
    assert!(response.is_ok());
}

#[tokio::test]
async fn tag_edit_rename_delete() {
    let tag_name_1 = "user/-/label/com.google/test";
    let tag_name_2 = "user/-/label/com.google/works";
    let item_id = "1591997825171867";
    let api = test_setup_env().await;

    // remove tag from articles to have a consistent start
    let _response = api
        .tag_delete(StreamType::Stream, tag_name_1, &Client::new())
        .await;
    let _response = api
        .tag_delete(StreamType::Stream, tag_name_2, &Client::new())
        .await;

    let response = api.tag_list(&Client::new()).await;
    assert!(response.is_ok());
    let tag_amount_old = response.unwrap().tags.len();

    // adding new label
    let response = api
        .tag_edit(&[item_id], Some(tag_name_1), None, &Client::new())
        .await;
    assert!(response.is_ok());

    let response = api.tag_list(&Client::new()).await;
    assert!(response.is_ok());
    let tag_amount_new = response.unwrap().tags.len();
    assert_eq!(tag_amount_old + 1, tag_amount_new);

    // renaming should not change anything
    let response = api
        .tag_rename(StreamType::Stream, tag_name_1, tag_name_2, &Client::new())
        .await;
    assert!(response.is_ok());

    let response = api.tag_list(&Client::new()).await;
    assert!(response.is_ok());
    let tag_amount_new = response.unwrap().tags.len();
    assert_eq!(tag_amount_old + 1, tag_amount_new);

    // delete tag
    let response = api
        .tag_delete(StreamType::Stream, tag_name_2, &Client::new())
        .await;
    assert!(response.is_ok());

    let response = api.tag_list(&Client::new()).await;
    assert!(response.is_ok());
    let tag_amount_new = response.unwrap().tags.len();
    assert_eq!(tag_amount_old, tag_amount_new);
}

#[tokio::test]
async fn mark_all_as_read() {
    let api = test_setup_env().await;
    api.mark_all_as_read("feed/1", None, &Client::new())
        .await
        .unwrap();
}

#[tokio::test]
async fn mark_article_read() {
    let api = test_setup_env().await;
    api.tag_edit(
        &["tag:google.com,2005:reader/item/0005eb9cd791a3ca"],
        Some("user/-/state/com.google/read"),
        None,
        &Client::new(),
    )
    .await
    .unwrap();
}

#[tokio::test]
async fn tag_rename() {
    let api = test_setup_env().await;
    api.tag_rename(
        StreamType::Stream,
        "user/-/label/Linux",
        "user/-/label/Linux",
        &Client::new(),
    )
    .await
    .unwrap();
}

#[tokio::test]
async fn deserialize_enclosure() {
    let enclosure_without_length_str = r#"
    {
        "href": "https://example.com",
        "type": "audio/x-mp3"
    }
    "#;

    let enclosure_with_null_length_str = r#"
    {
        "href": "https://example.com",
        "type": "audio/x-mp3",
        "length": null
    }
    "#;

    let enclosure_length_str = r#"
    {
        "href": "https://example.com",
        "type": "audio/x-mp3",
        "length": "9999"
    }
    "#;

    let enclosure_int_str = r#"
    {
        "href": "https://example.com",
        "type": "audio/x-mp3",
        "length": 9999
    }
    "#;

    serde_json::from_str::<Enclosure>(enclosure_without_length_str).unwrap();
    serde_json::from_str::<Enclosure>(enclosure_with_null_length_str).unwrap();
    serde_json::from_str::<Enclosure>(enclosure_length_str).unwrap();
    serde_json::from_str::<Enclosure>(enclosure_int_str).unwrap();
}