From 5767833c561212ca62750044bc1b4a00c267ffd9 Mon Sep 17 00:00:00 2001 From: floor-licker Date: Thu, 18 Dec 2025 13:52:33 -0500 Subject: [PATCH] tests: Improve e2e auth test coverage --- Cargo.lock | 190 +++++++-------- Cargo.toml | 4 +- examples/final_benchmark.rs | 2 +- examples/performance_benchmark.rs | 2 +- src/dns_cache.rs | 4 +- tests/integration_tests.rs | 383 ++++++++++++++++++++++++++++++ tests/order_posting_test.rs | 88 +++++++ tests/simple_auth_test.rs | 159 +++++++++++++ 8 files changed, 720 insertions(+), 112 deletions(-) create mode 100644 tests/integration_tests.rs create mode 100644 tests/order_posting_test.rs create mode 100644 tests/simple_auth_test.rs diff --git a/Cargo.lock b/Cargo.lock index 304c5ee..c2f8a00 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -650,9 +650,9 @@ dependencies = [ [[package]] name = "async-compression" -version = "0.4.34" +version = "0.4.36" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e86f6d3dc9dc4352edeea6b8e499e13e3f5dc3b964d7ca5fd411415a3498473" +checksum = "98ec5f6c2f8bc326c994cb9e241cc257ddaba9afa8555a43cffbb5dd86efaa37" dependencies = [ "compression-codecs", "compression-core", @@ -814,9 +814,9 @@ dependencies = [ [[package]] name = "bumpalo" -version = "3.19.0" +version = "3.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" +checksum = "5dd9dc738b7a8311c7ade152424974d8115f2cdad61e8dab8dac9f2362298510" [[package]] name = "byte-slice-cast" @@ -884,9 +884,9 @@ checksum = "37b2a672a2cb129a2e41c10b1224bb368f9f37a2b16b612598138befd7b37eb5" [[package]] name = "cc" -version = "1.2.48" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c481bdbf0ed3b892f6f806287d72acd515b352a4ec27a208489b8c1bc839633a" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" dependencies = [ "find-msvc-tools", "shlex", @@ -981,9 +981,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.33" +version = "0.4.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "302266479cb963552d11bd042013a58ef1adc56768016c8b82b4199488f2d4ad" +checksum = "b0f7ac3e5b97fdce45e8922fb05cae2c37f7bbd63d30dd94821dacfd8f3f2bf2" dependencies = [ "compression-core", "flate2", @@ -1270,10 +1270,10 @@ dependencies = [ ] [[package]] -name = "dotenv" -version = "0.15.0" +name = "dotenvy" +version = "0.15.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77c90badedccf4105eca100756a0b1289e191f6fcbdadd3cee1d2f614f97da8f" +checksum = "1aaf95b3e5c8f23aa320147307562d361db0ae0d51242340f558153b4eb2439b" [[package]] name = "dunce" @@ -1460,9 +1460,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.1.7" +version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2152dbcb980c05735e2a651d96011320a949eb31a0c8b38b72645ce97dec676" +checksum = "bfe33edd8e85a12a67454e37f8c75e730830d83e313556ab9ebf9ee7fbeb3bfb" dependencies = [ "crc32fast", "miniz_oxide", @@ -1760,6 +1760,51 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70" +[[package]] +name = "hickory-proto" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92652067c9ce6f66ce53cc38d1169daa36e6e7eb7dd3b63b5103bd9d97117248" +dependencies = [ + "async-trait", + "cfg-if", + "data-encoding", + "enum-as-inner", + "futures-channel", + "futures-io", + "futures-util", + "idna", + "ipnet", + "once_cell", + "rand 0.8.5", + "thiserror 1.0.69", + "tinyvec", + "tokio", + "tracing", + "url", +] + +[[package]] +name = "hickory-resolver" +version = "0.24.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbb117a1ca520e111743ab2f6688eddee69db4e0ea242545a604dce8a66fd22e" +dependencies = [ + "cfg-if", + "futures-util", + "hickory-proto", + "ipconfig", + "lru-cache", + "once_cell", + "parking_lot", + "rand 0.8.5", + "resolv-conf", + "smallvec", + "thiserror 1.0.69", + "tokio", + "tracing", +] + [[package]] name = "hmac" version = "0.12.1" @@ -1973,9 +2018,9 @@ checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e93fcd3157766c0c8da2f8cff6ce651a31f0810eaa1c51ec363ef790bbb5fb99" +checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", @@ -1987,9 +2032,9 @@ dependencies = [ [[package]] name = "icu_properties_data" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02845b3647bb045f1100ecd6480ff52f34c35f82d9880e029d329c21d1054899" +checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" @@ -2006,16 +2051,6 @@ dependencies = [ "zerovec", ] -[[package]] -name = "idna" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d20d6b07bfbc108882d88ed8e37d39636dcc260e15e30c45e6ba089610b917c" -dependencies = [ - "unicode-bidi", - "unicode-normalization", -] - [[package]] name = "idna" version = "1.1.0" @@ -2644,10 +2679,11 @@ dependencies = [ "bytes", "chrono", "criterion", - "dotenv", + "dotenvy", "env_logger", "futures", "futures-util", + "hickory-resolver", "hmac", "mockito", "proptest", @@ -2665,7 +2701,6 @@ dependencies = [ "tokio-tungstenite", "tracing", "tracing-subscriber", - "trust-dns-resolver", "url", "uuid", ] @@ -2963,11 +2998,10 @@ dependencies = [ [[package]] name = "reqwest" -version = "0.12.24" +version = "0.12.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d0946410b9f7b082a427e4ef5c8ff541a88b357bc6c637c40db3a68ac70a36f" +checksum = "3b4c14b2d9afca6a60277086b0cc6a6ae0b568f6f7916c943a8cdc79f8be240f" dependencies = [ - "async-compression", "base64", "bytes", "encoding_rs", @@ -3192,9 +3226,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.13.1" +version = "1.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "708c0f9d5f54ba0272468c1d306a52c495b31fa155e91bc25371e6df7996908c" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" dependencies = [ "zeroize", ] @@ -3458,9 +3492,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.7" +version = "0.3.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" [[package]] name = "simd-json" @@ -3859,18 +3893,18 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "0.7.3" +version = "0.7.4+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f2cdb639ebbc97961c51720f858597f7f24c4fc295327923af55b74c3c724533" +checksum = "fe3cea6b2aa3b910092f6abd4053ea464fab5f9c170ba5e9a6aead16ec4af2b6" dependencies = [ "serde_core", ] [[package]] name = "toml_edit" -version = "0.23.8" +version = "0.23.10+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9b7ac41d92f2d2803f233e297127bac397df7b337e0460a1cc39d6c006dee4" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" dependencies = [ "indexmap", "toml_datetime", @@ -3880,9 +3914,9 @@ dependencies = [ [[package]] name = "toml_parser" -version = "1.0.4" +version = "1.0.5+spec-1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c0cbe268d35bdb4bb5a56a2de88d0ad0eb70af5384a99d648cd4b3d04039800e" +checksum = "4c03bee5ce3696f31250db0bbaff18bc43301ce0e8db2ed1f07cbb2acf89984c" dependencies = [ "winnow", ] @@ -3904,17 +3938,22 @@ dependencies = [ [[package]] name = "tower-http" -version = "0.6.7" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf146f99d442e8e68e585f5d798ccd3cad9a7835b917e09728880a862706456" +checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ + "async-compression", "bitflags", "bytes", + "futures-core", "futures-util", "http", "http-body", + "http-body-util", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", @@ -3993,52 +4032,6 @@ dependencies = [ "tracing-log", ] -[[package]] -name = "trust-dns-proto" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3119112651c157f4488931a01e586aa459736e9d6046d3bd9105ffb69352d374" -dependencies = [ - "async-trait", - "cfg-if", - "data-encoding", - "enum-as-inner", - "futures-channel", - "futures-io", - "futures-util", - "idna 0.4.0", - "ipnet", - "once_cell", - "rand 0.8.5", - "smallvec", - "thiserror 1.0.69", - "tinyvec", - "tokio", - "tracing", - "url", -] - -[[package]] -name = "trust-dns-resolver" -version = "0.23.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "10a3e6c3aff1718b3c73e395d1f35202ba2ffa847c6a62eea0db8fb4cfe30be6" -dependencies = [ - "cfg-if", - "futures-util", - "ipconfig", - "lru-cache", - "once_cell", - "parking_lot", - "rand 0.8.5", - "resolv-conf", - "smallvec", - "thiserror 1.0.69", - "tokio", - "tracing", - "trust-dns-proto", -] - [[package]] name = "try-lock" version = "0.2.5" @@ -4095,27 +4088,12 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eaea85b334db583fe3274d12b4cd1880032beab409c0d774be044d4480ab9a94" -[[package]] -name = "unicode-bidi" -version = "0.3.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" - [[package]] name = "unicode-ident" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" -[[package]] -name = "unicode-normalization" -version = "0.1.25" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5fd4f6878c9cb28d874b009da9e8d183b5abc80117c40bbd187a1fde336be6e8" -dependencies = [ - "tinyvec", -] - [[package]] name = "unicode-segmentation" version = "1.12.0" @@ -4141,7 +4119,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08bc136a29a3d1758e07a9cca267be308aeebf5cfd5a10f3f67ab2097683ef5b" dependencies = [ "form_urlencoded", - "idna 1.1.0", + "idna", "percent-encoding", "serde", ] diff --git a/Cargo.toml b/Cargo.toml index da37a08..8df860d 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,13 +22,13 @@ futures-util = "0.3" # HTTP client reqwest = { version = "0.12", features = ["json", "stream", "gzip"] } -trust-dns-resolver = "0.23" +hickory-resolver = "0.24" # Serialization serde = { version = "1.0", features = ["derive"] } serde_json = "1.0" simd-json = "0.13" -dotenv = "0.15" +dotenvy = "0.15" # Ethereum and crypto alloy-primitives = "0.8.26" diff --git a/examples/final_benchmark.rs b/examples/final_benchmark.rs index 5941c16..5b501b1 100644 --- a/examples/final_benchmark.rs +++ b/examples/final_benchmark.rs @@ -3,7 +3,7 @@ use std::time::Instant; #[tokio::main] async fn main() -> Result<(), Box> { - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); println!("Final Benchmark - Apples-to-Apples Comparison"); println!("==============================================\n"); diff --git a/examples/performance_benchmark.rs b/examples/performance_benchmark.rs index 664c7ec..d7785ea 100644 --- a/examples/performance_benchmark.rs +++ b/examples/performance_benchmark.rs @@ -98,7 +98,7 @@ fn format_duration(d: Duration) -> String { #[tokio::main] async fn main() -> Result<(), Box> { // Load environment variables from .env file - dotenv::dotenv().ok(); + dotenvy::dotenv().ok(); println!("🚀 Real-World Polymarket Performance Benchmark"); println!("=============================================="); diff --git a/src/dns_cache.rs b/src/dns_cache.rs index 0609547..47181fc 100644 --- a/src/dns_cache.rs +++ b/src/dns_cache.rs @@ -8,8 +8,8 @@ use std::net::IpAddr; use std::sync::Arc; use std::time::{Duration, Instant}; use tokio::sync::RwLock; -use trust_dns_resolver::config::*; -use trust_dns_resolver::TokioAsyncResolver; +use hickory_resolver::config::*; +use hickory_resolver::TokioAsyncResolver; /// DNS cache entry with TTL #[derive(Clone, Debug)] diff --git a/tests/integration_tests.rs b/tests/integration_tests.rs new file mode 100644 index 0000000..5a078fc --- /dev/null +++ b/tests/integration_tests.rs @@ -0,0 +1,383 @@ +// Integration tests for polyfill-rs +// These tests hit the real Polymarket API and are ignored by default +// Run with: cargo test --test integration_tests -- --ignored --test-threads=1 + +use polyfill_rs::{ClobClient, OrderArgs, Side}; +use rust_decimal_macros::dec; +use std::env; + +const HOST: &str = "https://clob.polymarket.com"; +const CHAIN_ID: u64 = 137; + +fn load_env_vars() -> (String, Option, Option, Option) { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + let api_key = env::var("POLYMARKET_API_KEY").ok(); + let api_secret = env::var("POLYMARKET_API_SECRET").ok(); + let api_passphrase = env::var("POLYMARKET_API_PASSPHRASE").ok(); + + (private_key, api_key, api_secret, api_passphrase) +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_create_derive_api_key() { + let (private_key, _, _, _) = load_env_vars(); + + let client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + + // Test creating/deriving API key + let result = client.create_or_derive_api_key(None).await; + assert!(result.is_ok(), "Failed to create/derive API key: {:?}", result); + + let api_creds = result.unwrap(); + assert!(!api_creds.api_key.is_empty()); + assert!(!api_creds.secret.is_empty()); + assert!(!api_creds.passphrase.is_empty()); + + println!("PASS: Successfully created/derived API key"); +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_authenticated_order_flow() { + let (private_key, _, _, _) = load_env_vars(); + + // Initialize client with L1 headers + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + + // Step 1: Create/derive API credentials + println!("Step 1: Creating/deriving API credentials..."); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + println!("PASS: API credentials set"); + + // Step 2: Get a valid token_id from active markets + println!("Step 2: Fetching active markets..."); + let markets = client.get_sampling_markets(None).await + .expect("Failed to get markets"); + + let active_market = markets.data.iter() + .find(|m| m.active && !m.closed) + .expect("No active markets found"); + + let token_id = &active_market.tokens[0].token_id; + println!("PASS: Found active token: {}", token_id); + + // Step 3: Get current price to place a reasonable order + println!("Step 3: Getting current market price..."); + let midpoint = client.get_midpoint(token_id).await + .expect("Failed to get midpoint"); + println!("PASS: Current midpoint: {}", midpoint.mid); + + // Step 4: Create and post a small order well away from market price + // (so it won't fill immediately) + let order_price = if midpoint.mid > dec!(0.5) { + dec!(0.01) // Very low buy price, won't fill + } else { + dec!(0.99) // Very high sell price, won't fill + }; + + println!("Step 4: Posting order at price {}...", order_price); + let order_args = OrderArgs { + token_id: token_id.clone(), + price: order_price, + size: dec!(1.0), // Minimum size + side: Side::BUY, + }; + + let post_result = client.create_and_post_order(&order_args).await; + + // This is the critical test - did we get past the 401 error? + match &post_result { + Ok(response) => { + println!("PASS: Order posted successfully!"); + + // Step 5: Cancel the order + if let Some(order_id) = response.get("orderID").and_then(|v| v.as_str()) { + println!("Step 5: Canceling order {}...", order_id); + let cancel_result = client.cancel(order_id).await; + assert!(cancel_result.is_ok(), "Failed to cancel order: {:?}", cancel_result); + println!("PASS: Order canceled successfully"); + } else { + println!("WARNING: Order posted but no orderID in response: {:?}", response); + } + } + Err(e) => { + let err_str = format!("{:?}", e); + + // Check if it's a 401 (authentication failure) + if err_str.contains("401") { + panic!("FAIL: CRITICAL: 401 Unauthorized error - HMAC authentication is broken!"); + } + + // Check if it's a 400 with specific validation errors (these are OK) + if err_str.contains("400") && ( + err_str.contains("insufficient") || + err_str.contains("balance") || + err_str.contains("allowance") || + err_str.contains("POLY_AMOUNT_TOO_SMALL") + ) { + println!("PASS: Authentication successful (got expected validation error)"); + println!(" Error: {}", err_str); + } else { + panic!("FAIL: Unexpected error: {:?}", e); + } + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_get_orders() { + let (private_key, _, _, _) = load_env_vars(); + + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + + println!("Testing get_orders..."); + let result = client.get_orders(None, None).await; + + match result { + Ok(orders) => { + println!("PASS: Successfully fetched orders"); + println!(" Found {} orders", orders.len()); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("FAIL: 401 Unauthorized - authentication failed!"); + } + panic!("Failed to get orders: {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_get_trades() { + let (private_key, _, _, _) = load_env_vars(); + + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + + println!("Testing get_trades..."); + let result = client.get_trades(None, None).await; + + match result { + Ok(_trades) => { + println!("PASS: Successfully fetched trades"); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("FAIL: 401 Unauthorized - authentication failed!"); + } + panic!("Failed to get trades: {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_get_balance_allowance() { + let (private_key, _, _, _) = load_env_vars(); + + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + + println!("Testing get_balance_allowance..."); + + // Get a valid token_id first + let markets = client.get_sampling_markets(None).await + .expect("Failed to get markets"); + let token_id = &markets.data[0].tokens[0].token_id; + + use polyfill_rs::types::{BalanceAllowanceParams, AssetType}; + let params = BalanceAllowanceParams { + asset_type: Some(AssetType::CONDITIONAL), + token_id: Some(token_id.clone()), + signature_type: None, + }; + + let result = client.get_balance_allowance(Some(params)).await; + + match result { + Ok(balance) => { + println!("PASS: Successfully fetched balance/allowance"); + println!(" Balance: {:?}", balance); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("FAIL: 401 Unauthorized - authentication failed!"); + } + println!("WARNING: Balance check failed (may be expected): {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_get_api_keys() { + let (private_key, _, _, _) = load_env_vars(); + + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + + println!("Testing get_api_keys..."); + let result = client.get_api_keys().await; + + match result { + Ok(keys) => { + println!("PASS: Successfully fetched API keys"); + println!(" Found {} keys", keys.len()); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("FAIL: 401 Unauthorized - authentication failed!"); + } + panic!("Failed to get API keys: {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_get_notifications() { + let (private_key, _, _, _) = load_env_vars(); + + let mut client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + let api_creds = client.create_or_derive_api_key(None).await + .expect("Failed to create/derive API key"); + client.set_api_creds(api_creds); + + println!("Testing get_notifications..."); + let result = client.get_notifications().await; + + match result { + Ok(notifications) => { + println!("PASS: Successfully fetched notifications"); + println!(" Notifications: {:?}", notifications); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("FAIL: 401 Unauthorized - authentication failed!"); + } + panic!("Failed to get notifications: {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_market_data_endpoints() { + let (private_key, _, _, _) = load_env_vars(); + + let client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + + println!("Testing market data endpoints (no auth required)..."); + + // Get a valid token_id + let markets = client.get_sampling_markets(None).await + .expect("Failed to get markets"); + let token_id = &markets.data[0].tokens[0].token_id; + println!("PASS: Using token_id: {}", token_id); + + // Test multiple endpoints + println!("Testing get_order_book..."); + let book = client.get_order_book(token_id).await + .expect("Failed to get order book"); + println!("PASS: Order book: {} bids, {} asks", book.bids.len(), book.asks.len()); + + println!("Testing get_midpoint..."); + let midpoint = client.get_midpoint(token_id).await + .expect("Failed to get midpoint"); + println!("PASS: Midpoint: {}", midpoint.mid); + + println!("Testing get_spread..."); + let spread = client.get_spread(token_id).await + .expect("Failed to get spread"); + println!("PASS: Spread: {}", spread.spread); + + println!("Testing get_price..."); + let price = client.get_price(token_id, Side::BUY).await + .expect("Failed to get price"); + println!("PASS: Buy price: {}", price.price); + + println!("Testing get_tick_size..."); + let tick_size = client.get_tick_size(token_id).await + .expect("Failed to get tick size"); + println!("PASS: Tick size: {}", tick_size); + + println!("Testing get_markets..."); + let all_markets = client.get_markets(None).await + .expect("Failed to get all markets"); + println!("PASS: Found {} markets", all_markets.data.len()); + + println!("\nPASS: All market data endpoints working!"); +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_batch_endpoints() { + let (private_key, _, _, _) = load_env_vars(); + + let client = ClobClient::with_l1_headers(HOST, &private_key, CHAIN_ID); + + println!("Testing batch endpoints..."); + + // Get multiple valid token_ids + let markets = client.get_sampling_markets(None).await + .expect("Failed to get markets"); + let token_ids: Vec = markets.data[0..2.min(markets.data.len())] + .iter() + .map(|m| m.tokens[0].token_id.clone()) + .collect(); + + println!("Testing get_order_books (batch)..."); + let books = client.get_order_books(&token_ids).await + .expect("Failed to get order books"); + println!("PASS: Fetched {} order books", books.len()); + + println!("Testing get_midpoints (batch)..."); + let midpoints = client.get_midpoints(&token_ids).await + .expect("Failed to get midpoints"); + println!("PASS: Fetched {} midpoints", midpoints.len()); + + println!("Testing get_spreads (batch)..."); + let spreads = client.get_spreads(&token_ids).await + .expect("Failed to get spreads"); + println!("PASS: Fetched {} spreads", spreads.len()); + + println!("\nPASS: All batch endpoints working!"); +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_real_api_health_check() { + let client = ClobClient::new(HOST); + + println!("Testing health check endpoints..."); + + let ok = client.get_ok().await; + assert!(ok, "API health check failed!"); + println!("PASS: API is healthy"); + + let server_time = client.get_server_time().await + .expect("Failed to get server time"); + println!("PASS: Server time: {}", server_time); +} diff --git a/tests/order_posting_test.rs b/tests/order_posting_test.rs new file mode 100644 index 0000000..1b29f8f --- /dev/null +++ b/tests/order_posting_test.rs @@ -0,0 +1,88 @@ +// Test order posting - the critical endpoint that had the 401 bug +use polyfill_rs::{ClobClient, OrderArgs, Side}; +use rust_decimal::Decimal; +use std::env; +use std::str::FromStr; + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_post_order_authentication() { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + + let mut client = ClobClient::with_l1_headers( + "https://clob.polymarket.com", + &private_key, + 137 + ); + + println!("Step 1: Creating API credentials..."); + let creds = client.create_or_derive_api_key(None).await + .expect("Failed to create API key"); + client.set_api_creds(creds); + println!("API credentials set"); + + // Use a well-known token ID (we'll use an extreme price so it won't fill) + let token_id = "21742633143463906290569050155826241533067272736897614950488156847949938836455"; // Example token + + println!("\nStep 2: Attempting to post order (testing authentication)..."); + let order_args = OrderArgs { + token_id: token_id.to_string(), + price: Decimal::from_str("0.01").unwrap(), // Very low price, won't fill + size: Decimal::from_str("1.0").unwrap(), + side: Side::BUY, + }; + + let result = client.create_and_post_order(&order_args).await; + + match result { + Ok(response) => { + println!("AUTHENTICATION SUCCESSFUL! Order was accepted by API"); + println!(" Response: {:?}", response); + + // Try to cancel it if we got an order ID + if let Some(order_id) = response.get("orderID").and_then(|v| v.as_str()) { + println!("\nStep 3: Canceling order..."); + match client.cancel(order_id).await { + Ok(_) => println!("Order canceled successfully"), + Err(e) => println!("Cancel failed (order might have expired): {:?}", e), + } + } + } + Err(e) => { + let err_str = format!("{:?}", e); + + // The critical test: Is it a 401 error? + if err_str.contains("401") { + panic!("CRITICAL FAILURE: 401 Unauthorized!\n\ + The HMAC authentication bug is NOT fixed!\n\ + Error: {:?}", e); + } + + // If it's a 400 error with validation issues, that's actually GOOD + // It means authentication worked, but there's an issue with the order parameters + if err_str.contains("400") { + println!("AUTHENTICATION SUCCESSFUL!"); + println!(" (Got 400 validation error, which means auth passed)"); + println!(" Error details: {}", err_str); + + // These are expected validation errors when auth works + if err_str.contains("insufficient") || + err_str.contains("balance") || + err_str.contains("allowance") || + err_str.contains("POLY_AMOUNT_TOO_SMALL") || + err_str.contains("invalid") || + err_str.contains("market") { + println!(" This is an expected validation error - authentication is working!"); + return; + } + } + + // Any other error type + println!("Got unexpected error (not 401, so auth might be OK): {:?}", e); + } + } +} + diff --git a/tests/simple_auth_test.rs b/tests/simple_auth_test.rs new file mode 100644 index 0000000..ea2018e --- /dev/null +++ b/tests/simple_auth_test.rs @@ -0,0 +1,159 @@ +// Simple authentication test to verify HMAC works +use polyfill_rs::ClobClient; +use std::env; + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_create_api_key_simple() { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + + let mut client = ClobClient::with_l1_headers( + "https://clob.polymarket.com", + &private_key, + 137 + ); + + println!("Step 1: Creating/deriving API key..."); + let result = client.create_or_derive_api_key(None).await; + + match result { + Ok(creds) => { + println!("Successfully created/derived API key"); + println!(" API Key: {}", creds.api_key); + client.set_api_creds(creds); + + // Now try to get orders (requires auth) + println!("\nStep 2: Testing authenticated endpoint (get_orders)..."); + let orders_result = client.get_orders(None, None).await; + + match orders_result { + Ok(orders) => { + println!("Successfully authenticated! Got {} orders", orders.len()); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("CRITICAL: 401 Unauthorized - HMAC authentication is BROKEN!"); + } else { + println!("Authentication successful (non-401 error): {:?}", e); + } + } + } + } + Err(e) => { + panic!("Failed to create/derive API key: {:?}", e); + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_get_api_keys() { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + + let mut client = ClobClient::with_l1_headers( + "https://clob.polymarket.com", + &private_key, + 137 + ); + + let creds = client.create_or_derive_api_key(None).await + .expect("Failed to create API key"); + client.set_api_creds(creds); + + println!("Testing get_api_keys (requires HMAC auth)..."); + let result = client.get_api_keys().await; + + match result { + Ok(keys) => { + println!("Authentication successful! Found {} keys", keys.len()); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("CRITICAL: 401 Unauthorized - HMAC authentication is BROKEN!"); + } else { + panic!("Failed with non-401 error: {:?}", e); + } + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_get_trades() { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + + let mut client = ClobClient::with_l1_headers( + "https://clob.polymarket.com", + &private_key, + 137 + ); + + let creds = client.create_or_derive_api_key(None).await + .expect("Failed to create API key"); + client.set_api_creds(creds); + + println!("Testing get_trades (requires HMAC auth)..."); + let result = client.get_trades(None, None).await; + + match result { + Ok(_) => { + println!("Authentication successful!"); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("CRITICAL: 401 Unauthorized - HMAC authentication is BROKEN!"); + } else { + println!("Authentication successful (got non-401 error): {:?}", e); + } + } + } +} + +#[tokio::test(flavor = "multi_thread")] +#[ignore] +async fn test_get_notifications() { + dotenvy::dotenv().ok(); + + let private_key = env::var("POLYMARKET_PRIVATE_KEY") + .expect("POLYMARKET_PRIVATE_KEY must be set in .env"); + + let mut client = ClobClient::with_l1_headers( + "https://clob.polymarket.com", + &private_key, + 137 + ); + + let creds = client.create_or_derive_api_key(None).await + .expect("Failed to create API key"); + client.set_api_creds(creds); + + println!("Testing get_notifications (requires HMAC auth)..."); + let result = client.get_notifications().await; + + match result { + Ok(notifs) => { + println!("Authentication successful! Notifications: {:?}", notifs); + } + Err(e) => { + let err_str = format!("{:?}", e); + if err_str.contains("401") { + panic!("CRITICAL: 401 Unauthorized - HMAC authentication is BROKEN!"); + } else { + println!("Authentication successful (got non-401 error): {:?}", e); + } + } + } +} +