mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-08-07 01:33:52 +00:00
test(api): add yahoo fixtures, mock-provider errors, and CLI integration coverage
This commit is contained in:
parent
c0a3d744b2
commit
639ee4b041
6 changed files with 233 additions and 51 deletions
|
|
@ -27,30 +27,49 @@ pub fn resolve_symbol(symbol: &str, exchange: &str) -> String {
|
|||
|
||||
pub fn default_provider() -> Box<dyn MarketDataProvider> {
|
||||
if std::env::var("IDX_USE_MOCK_PROVIDER").is_ok() {
|
||||
Box::new(MockProvider)
|
||||
Box::new(MockProvider::from_fixtures())
|
||||
} else {
|
||||
Box::new(yahoo::YahooProvider::new())
|
||||
}
|
||||
}
|
||||
|
||||
struct MockProvider;
|
||||
pub struct MockProvider {
|
||||
quote: Result<Quote, IdxError>,
|
||||
history: Result<Vec<Ohlc>, IdxError>,
|
||||
}
|
||||
|
||||
impl MockProvider {
|
||||
pub fn from_fixtures() -> Self {
|
||||
if std::env::var("IDX_MOCK_ERROR").is_ok() {
|
||||
return Self::with_error(IdxError::ProviderUnavailable);
|
||||
}
|
||||
|
||||
let quote_raw = std::fs::read_to_string("tests/fixtures/chart_bbca_1d.json")
|
||||
.unwrap_or_else(|_| "{}".to_string());
|
||||
let history_raw = std::fs::read_to_string("tests/fixtures/chart_bbca_3mo.json")
|
||||
.unwrap_or_else(|_| "{}".to_string());
|
||||
|
||||
let quote = yahoo::parse_quote_from_str("BBCA.JK", "e_raw)
|
||||
.map_err(|e| IdxError::ParseError(e.to_string()));
|
||||
let history = yahoo::parse_history_from_str(&history_raw)
|
||||
.map_err(|e| IdxError::ParseError(e.to_string()));
|
||||
|
||||
Self { quote, history }
|
||||
}
|
||||
|
||||
pub fn with_error(err: IdxError) -> Self {
|
||||
Self {
|
||||
quote: Err(err),
|
||||
history: Err(IdxError::ProviderUnavailable),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl MarketDataProvider for MockProvider {
|
||||
fn quote(&self, symbol: &str) -> Result<Quote, IdxError> {
|
||||
Ok(Quote {
|
||||
symbol: symbol.to_string(),
|
||||
price: 9875.0,
|
||||
change: 117.0,
|
||||
change_pct: 1.2,
|
||||
volume: 12_300_000,
|
||||
market_cap: Some(1_215_200_000_000_000.0),
|
||||
week52_high: Some(10_250.0),
|
||||
week52_low: Some(7_800.0),
|
||||
week52_position: Some(0.73),
|
||||
range_signal: Some("upper".to_string()),
|
||||
prev_close: Some(9_758.0),
|
||||
avg_volume: Some(10_000_000),
|
||||
})
|
||||
let mut q = self.quote.clone()?;
|
||||
q.symbol = symbol.to_string();
|
||||
Ok(q)
|
||||
}
|
||||
|
||||
fn history(
|
||||
|
|
@ -59,14 +78,7 @@ impl MarketDataProvider for MockProvider {
|
|||
_period: &Period,
|
||||
_interval: &Interval,
|
||||
) -> Result<Vec<Ohlc>, IdxError> {
|
||||
Ok(vec![Ohlc {
|
||||
date: chrono::NaiveDate::from_ymd_opt(2026, 3, 1).expect("valid date"),
|
||||
open: 9800.0,
|
||||
high: 9900.0,
|
||||
low: 9750.0,
|
||||
close: 9875.0,
|
||||
volume: 12_300_000,
|
||||
}])
|
||||
self.history.clone()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -81,5 +93,6 @@ mod tests {
|
|||
assert_eq!(resolve_symbol("TLKM.us", "JK"), "TLKM.US");
|
||||
assert_eq!(resolve_symbol("abcd.ef.gh", "JK"), "ABCD.EF.GH");
|
||||
assert_eq!(resolve_symbol(" bbri ", "jk"), "BBRI.JK");
|
||||
assert_eq!(resolve_symbol("", "JK"), ".JK");
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -78,6 +78,12 @@ impl MarketDataProvider for YahooProvider {
|
|||
}
|
||||
}
|
||||
|
||||
pub(crate) fn parse_quote_from_str(symbol: &str, raw: &str) -> Result<Quote, IdxError> {
|
||||
let chart: ChartResponse =
|
||||
serde_json::from_str(raw).map_err(|e| IdxError::ParseError(e.to_string()))?;
|
||||
parse_quote(symbol, &chart)
|
||||
}
|
||||
|
||||
fn parse_quote(symbol: &str, chart: &ChartResponse) -> Result<Quote, IdxError> {
|
||||
let result = chart
|
||||
.chart
|
||||
|
|
@ -122,6 +128,12 @@ fn parse_quote(symbol: &str, chart: &ChartResponse) -> Result<Quote, IdxError> {
|
|||
})
|
||||
}
|
||||
|
||||
pub(crate) fn parse_history_from_str(raw: &str) -> Result<Vec<Ohlc>, IdxError> {
|
||||
let chart: ChartResponse =
|
||||
serde_json::from_str(raw).map_err(|e| IdxError::ParseError(e.to_string()))?;
|
||||
parse_history(&chart)
|
||||
}
|
||||
|
||||
fn parse_history(chart: &ChartResponse) -> Result<Vec<Ohlc>, IdxError> {
|
||||
let result = chart
|
||||
.chart
|
||||
|
|
@ -212,7 +224,7 @@ struct IndicatorQuote {
|
|||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::{parse_history, parse_quote, ChartResponse};
|
||||
use super::{parse_history, parse_history_from_str, parse_quote, parse_quote_from_str, ChartResponse};
|
||||
|
||||
const SAMPLE: &str = r#"{
|
||||
"chart": {
|
||||
|
|
@ -249,4 +261,16 @@ mod tests {
|
|||
assert_eq!(history.len(), 2);
|
||||
assert_eq!(history[0].close, 9875.0);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn parses_realistic_fixture_json() {
|
||||
let quote_raw = std::fs::read_to_string("tests/fixtures/chart_bbca_1d.json").expect("fixture exists");
|
||||
let history_raw = std::fs::read_to_string("tests/fixtures/chart_bbca_3mo.json").expect("fixture exists");
|
||||
|
||||
let quote = parse_quote_from_str("BBCA.JK", "e_raw).expect("fixture quote parsed");
|
||||
assert_eq!(quote.symbol, "BBCA.JK");
|
||||
|
||||
let history = parse_history_from_str(&history_raw).expect("fixture history parsed");
|
||||
assert!(!history.is_empty());
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ use serde::Serialize;
|
|||
use thiserror::Error;
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[derive(Debug, Error)]
|
||||
#[derive(Debug, Error, Clone)]
|
||||
pub enum IdxError {
|
||||
#[error("symbol not found: {0}")]
|
||||
SymbolNotFound(String),
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue