mod common;
use std::str::FromStr as _;
use alloy::signers::Signer as _;
use alloy::signers::local::LocalSigner;
use httpmock::MockServer;
use polymarket_client_sdk::POLYGON;
use polymarket_client_sdk::auth::Credentials;
use polymarket_client_sdk::clob::{Client, Config};
use polymarket_client_sdk::error::{Synchronization, Validation};
use reqwest::StatusCode;
use serde_json::json;
use crate::common::{API_KEY, PASSPHRASE, POLY_ADDRESS, PRIVATE_KEY, SECRET, create_authenticated};
#[tokio::test]
async fn authenticate_with_explicit_credentials_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?
.authentication_builder(&signer)
.credentials(Credentials::default())
.authenticate()
.await?;
assert_eq!(signer.address(), client.address());
Ok(())
}
#[tokio::test]
async fn authenticate_with_nonce_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET)
.path("/auth/derive-api-key");
then.status(StatusCode::OK).json_body(json!({
"apiKey": API_KEY,
"passphrase": PASSPHRASE,
"secret": SECRET
}));
});
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?
.authentication_builder(&signer)
.nonce(123)
.authenticate()
.await?;
assert_eq!(signer.address(), client.address());
mock.assert();
Ok(())
}
#[tokio::test]
async fn authenticate_with_explicit_credentials_and_nonce_should_fail() -> anyhow::Result<()> {
let server = MockServer::start();
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let err = Client::new(&server.base_url(), Config::default())?
.authentication_builder(&signer)
.nonce(123)
.credentials(Credentials::default())
.authenticate()
.await
.unwrap_err();
let validation_err = err.downcast_ref::<Validation>().unwrap();
assert_eq!(
validation_err.reason,
"Credentials and nonce are both set. If nonce is set, then you must not supply credentials"
);
Ok(())
}
#[tokio::test]
async fn authenticated_to_unauthenticated_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let client = create_authenticated(&server).await?;
assert_eq!(client.host().as_str(), format!("{}/", server.base_url()));
client.deauthenticate()?;
Ok(())
}
#[tokio::test]
async fn authenticate_with_multiple_strong_references_should_fail() -> anyhow::Result<()> {
let server = MockServer::start();
server.mock(|when, then| {
when.method(httpmock::Method::GET)
.path("/auth/derive-api-key");
then.status(StatusCode::OK).json_body(json!({
"apiKey": API_KEY,
"passphrase": PASSPHRASE,
"secret": SECRET
}));
});
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?;
let _client_clone = client.clone();
let err = client
.authentication_builder(&signer)
.authenticate()
.await
.unwrap_err();
err.downcast_ref::<Synchronization>().unwrap();
Ok(())
}
#[tokio::test]
async fn deauthenticated_with_multiple_strong_references_should_fail() -> anyhow::Result<()> {
let server = MockServer::start();
let client = create_authenticated(&server).await?;
let _client_clone = client.clone();
let err = client.deauthenticate().unwrap_err();
let sync_error = err.downcast_ref::<Synchronization>().unwrap();
assert_eq!(
sync_error.to_string(),
"synchronization error: multiple threads are attempting to log in or log out"
);
Ok(())
}
#[tokio::test]
async fn create_api_key_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?;
let mock = server.mock(|when, then| {
when.method(httpmock::Method::POST)
.path("/auth/api-key")
.header(POLY_ADDRESS, signer.address().to_string().to_lowercase());
then.status(StatusCode::OK).json_body(json!({
"apiKey": API_KEY.to_string(),
"passphrase": PASSPHRASE,
"secret": SECRET
}));
});
let credentials = client.create_api_key(&signer, None).await?;
assert_eq!(
credentials,
Credentials::new(API_KEY, SECRET.to_owned(), PASSPHRASE.to_owned())
);
mock.assert();
Ok(())
}
#[tokio::test]
async fn derive_api_key_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?;
let mock = server.mock(|when, then| {
when.method(httpmock::Method::GET)
.path("/auth/derive-api-key")
.header(POLY_ADDRESS, signer.address().to_string().to_lowercase());
then.status(StatusCode::OK).json_body(json!({
"apiKey": API_KEY.to_string(),
"passphrase": PASSPHRASE,
"secret": SECRET
}));
});
let credentials = client.derive_api_key(&signer, None).await?;
assert_eq!(
credentials,
Credentials::new(API_KEY, SECRET.to_owned(), PASSPHRASE.to_owned())
);
mock.assert();
Ok(())
}
#[tokio::test]
async fn create_or_derive_api_key_should_succeed() -> anyhow::Result<()> {
let server = MockServer::start();
let signer = LocalSigner::from_str(PRIVATE_KEY)?.with_chain_id(Some(POLYGON));
let client = Client::new(&server.base_url(), Config::default())?;
let mock = server.mock(|when, then| {
when.method(httpmock::Method::POST).path("/auth/api-key");
then.status(StatusCode::NOT_FOUND);
});
let mock2 = server.mock(|when, then| {
when.method(httpmock::Method::GET)
.path("/auth/derive-api-key")
.header(POLY_ADDRESS, signer.address().to_string().to_lowercase());
then.status(StatusCode::OK).json_body(json!({
"apiKey": API_KEY.to_string(),
"passphrase": PASSPHRASE,
"secret": SECRET
}));
});
let credentials = client.create_or_derive_api_key(&signer, None).await?;
assert_eq!(
credentials,
Credentials::new(API_KEY, SECRET.to_owned(), PASSPHRASE.to_owned())
);
mock.assert();
mock2.assert();
Ok(())
}