mod auth;
mod client;
mod endpoints;
mod error;
mod types;
pub use auth::{Auth, SessionData};
pub use client::{CredentialsBuilder, Instagram, InstagramBuilder};
pub use endpoints::QUERY_HASH;
pub use error::{Error, Result};
pub use types::*;
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn build_anonymous_client() {
let client = Instagram::builder().build();
assert!(client.is_ok());
}
#[test]
fn build_with_credentials() {
let client = Instagram::builder()
.id("test_user")
.password("test_pass")
.build();
assert!(client.is_ok());
}
#[test]
fn build_with_session() {
let session = SessionData::new("sess123", "csrf456");
let client = Instagram::builder().session(session).build();
assert!(client.is_ok());
}
#[test]
fn session_with_user_id() {
let session = SessionData::new("sess123", "csrf456").with_user_id("user789");
assert_eq!(session.user_id, Some("user789".into()));
}
#[test]
fn custom_user_agent() {
let client = Instagram::builder().user_agent("CustomBot/1.0").build();
assert!(client.is_ok());
}
#[test]
fn pagination_default() {
let page: Pagination<Media> = Pagination::default();
assert!(page.items.is_empty());
assert!(!page.has_next);
assert!(page.cursor.is_none());
}
#[test]
fn endpoint_urls() {
let url = endpoints::profile_url("testuser");
assert!(url.contains("testuser"));
let url = endpoints::user_info_url("12345");
assert!(url.contains("12345"));
let url = endpoints::media_info_url("ABC");
assert!(url.contains("ABC"));
}
#[test]
fn action_urls() {
let url = endpoints::like_url("123");
assert!(url.contains("123") && url.contains("like"));
let url = endpoints::follow_url("456");
assert!(url.contains("456") && url.contains("follow"));
let url = endpoints::comment_url("789");
assert!(url.contains("789") && url.contains("comment"));
}
#[test]
fn auth_variants() {
let auth = Auth::None;
assert!(matches!(auth, Auth::None));
let auth = Auth::Credentials {
id: "u".into(),
password: "p".into(),
};
assert!(matches!(auth, Auth::Credentials { .. }));
let session = SessionData::new("s", "c");
let auth = Auth::Session(session);
assert!(matches!(auth, Auth::Session(_)));
}
#[test]
fn media_type_serialization() {
let reel = MediaType::Reel;
let json = serde_json::to_string(&reel).unwrap();
assert_eq!(json, "\"reel\"");
let carousel = MediaType::Carousel;
let json = serde_json::to_string(&carousel).unwrap();
assert_eq!(json, "\"carousel\"");
}
}
#[cfg(test)]
mod api_tests {
use super::*;
fn client() -> Instagram {
Instagram::builder().build().unwrap()
}
async fn auth_client() -> Option<Instagram> {
let id = "id";
let password = "password";
let _ig = Instagram::builder().id(id).password(password).build();
match &_ig {
Ok(_) => println!("Ok"),
Err(err) => println!("{:?}", err),
}
let ig = _ig.ok()?;
let res = ig.login().await;
println!("{:?}", res);
res.ok()?;
println!("gurt: yo");
Some(ig)
}
#[tokio::test]
#[ignore]
async fn fetch_user_profile() {
let ig = client();
let user = ig.user("instagram").await.unwrap();
assert_eq!(user.username, "instagram");
assert!(user.follower_count > 0);
}
#[tokio::test]
#[ignore]
async fn fetch_media_post() {
let ig = client();
let media = ig.media("CvIBvWQPxKB").await.unwrap();
assert!(!media.id.is_empty());
assert!(!media.shortcode.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_reel() {
let Some(ig) = auth_client().await else {
println!("Fail");
return;
};
let reel = ig.reel("DNaaHuTRxKF").await.unwrap();
assert!(!reel.id.is_empty());
assert!(!reel.video_url.is_empty());
assert!(reel.duration > 0.0);
println!("{:?}", reel);
}
#[tokio::test]
#[ignore]
async fn search_users() {
let ig = client();
let results = ig.search("instagram").await.unwrap();
assert!(!results.users.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_user_posts() {
let Some(ig) = auth_client().await else {
return;
};
let user = ig.user("instagram").await.unwrap();
let posts = ig.user_posts(&user.id, 12, None).await.unwrap();
assert!(!posts.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_comments() {
let Some(ig) = auth_client().await else {
return;
};
let shortcode = "DSdV9bYkg0G";
let comments = ig.comments(shortcode, 20, None).await.unwrap();
println!("{:?}", comments);
assert!(!comments.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_likers() {
let Some(ig) = auth_client().await else {
return;
};
let likers = ig.likers("CvIBvWQPxKB", 20, None).await.unwrap();
assert!(!likers.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_followers() {
let Some(ig) = auth_client().await else {
return;
};
let user = ig.user("instagram").await.unwrap();
let followers = ig.followers(&user.id, 20, None).await.unwrap();
println!("{:?}", followers);
assert!(!followers.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_following() {
let Some(ig) = auth_client().await else {
return;
};
let user = ig.user("instagram").await.unwrap();
let following = ig.following(&user.id, 20, None).await.unwrap();
assert!(!following.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_highlights() {
let Some(ig) = auth_client().await else {
return;
};
let user = ig.user("instagram").await.unwrap();
let highlights = ig.highlights(&user.id).await.unwrap();
assert!(!highlights.is_empty());
}
#[tokio::test]
#[ignore]
async fn fetch_hashtag_media() {
let Some(ig) = auth_client().await else {
return;
};
let media = ig.hashtag_media("rust", 12, None).await.unwrap();
println!("{:?}", media);
assert!(!media.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn pagination_flow() {
let Some(ig) = auth_client().await else {
return;
};
let user = ig.user("instagram").await.unwrap();
let first = ig.user_posts(&user.id, 5, None).await.unwrap();
assert!(first.has_next);
let second = ig
.user_posts(&user.id, 5, first.cursor.as_deref())
.await
.unwrap();
assert!(!second.items.is_empty());
}
#[tokio::test]
#[ignore]
async fn login_flow() {
let Some(ig) = auth_client().await else {
println!("skipping login test / INSTA_ID and INSTA_PASSWORD not set");
return;
};
let user = ig.user("instagram").await.unwrap();
println!("{:?}", user);
assert!(!user.id.is_empty());
}
}