From 5fb3fb69cdf05bfd2c58c26fbe581820b6f0f266 Mon Sep 17 00:00:00 2001 From: justinpolygon <123573436+justinpolygon@users.noreply.github.com> Date: Thu, 15 Oct 2026 09:02:41 -0700 Subject: [PATCH] [SPT-1668] Don't split comma-separated str values for *_any_of params _get_params joined every *_any_of value with ",".join(val), which assumes a list. The spec defines every any_of param as a comma-separated string, and most methods type them as Optional[str], so passing e.g. ticker_any_of="AAPL,VZ" sent ticker.any_of=A,A,P,L,,,V,Z and silently returned data for the wrong tickers. Only join non-str iterables. Lists, tuples, etc. behave as before. --- massive/rest/base.py | 5 ++- .../v1/dividends&ticker.any_of=AAPL%2CVZ.json | 34 +++++++++++++++ test_rest/test_dividends.py | 42 ++++++++++++++++++- test_rest/test_snapshots.py | 2 + 4 files changed, 81 insertions(+), 2 deletions(-) create mode 100644 test_rest/mocks/stocks/v1/dividends&ticker.any_of=AAPL%2CVZ.json diff --git a/massive/rest/base.py b/massive/rest/base.py index 3349d7ef..ffff76a8 100644 --- a/massive/rest/base.py +++ b/massive/rest/base.py @@ -194,7 +194,10 @@ def _get_params( # using ., and not _ (removesuffix would work) # but that is python 3.9+ argname = argname[: -len(f"_{ext}")] + f".{ext}" - if argname.endswith("any_of"): + # any_of accepts a comma-separated str (per the spec) or a + # list of values; only join the latter, since joining a str + # would split it into single characters + if argname.endswith("any_of") and not isinstance(val, str): val = ",".join(val) params[argname] = val diff --git a/test_rest/mocks/stocks/v1/dividends&ticker.any_of=AAPL%2CVZ.json b/test_rest/mocks/stocks/v1/dividends&ticker.any_of=AAPL%2CVZ.json new file mode 100644 index 00000000..0affbb97 --- /dev/null +++ b/test_rest/mocks/stocks/v1/dividends&ticker.any_of=AAPL%2CVZ.json @@ -0,0 +1,34 @@ +{ + "results": [ + { + "id": "E1e778b36bcedb4d1589a970be52c16294ad4bc89c8627d56a3b970f6cf2a6e10", + "ticker": "AAPL", + "record_date": "2026-08-10", + "pay_date": "2026-08-13", + "declaration_date": "2026-07-30", + "ex_dividend_date": "2026-08-10", + "frequency": 4, + "cash_amount": 0.27, + "currency": "USD", + "distribution_type": "recurring", + "historical_adjustment_factor": 0.999138, + "split_adjusted_cash_amount": 0.27 + }, + { + "id": "Ebff20941049b81e8d1cc80d5ef0cf7b9e44318993597f922292327578e468954", + "ticker": "VZ", + "record_date": "2026-10-09", + "pay_date": "2026-11-02", + "declaration_date": "2026-09-09", + "ex_dividend_date": "2026-10-09", + "frequency": 4, + "cash_amount": 0.7075, + "currency": "USD", + "distribution_type": "recurring", + "historical_adjustment_factor": 0.984736, + "split_adjusted_cash_amount": 0.7075 + } + ], + "status": "OK", + "request_id": "6a7e466379af0a71039d60cc78e72282" +} \ No newline at end of file diff --git a/test_rest/test_dividends.py b/test_rest/test_dividends.py index 24dc9470..7b6575d0 100644 --- a/test_rest/test_dividends.py +++ b/test_rest/test_dividends.py @@ -1,4 +1,4 @@ -from massive.rest.models import Dividend +from massive.rest.models import Dividend, StockDividend from base import BaseTest @@ -108,3 +108,43 @@ def test_list_dividends(self): ), ] self.assertEqual(dividends, expected) + + def test_list_stocks_dividends_ticker_any_of(self): + expected = [ + StockDividend( + cash_amount=0.27, + currency="USD", + declaration_date="2026-07-30", + distribution_type="recurring", + ex_dividend_date="2026-08-10", + frequency=4, + historical_adjustment_factor=0.999138, + id="E1e778b36bcedb4d1589a970be52c16294ad4bc89c8627d56a3b970f6cf2a6e10", + pay_date="2026-08-13", + record_date="2026-08-10", + split_adjusted_cash_amount=0.27, + ticker="AAPL", + ), + StockDividend( + cash_amount=0.7075, + currency="USD", + declaration_date="2026-09-09", + distribution_type="recurring", + ex_dividend_date="2026-10-09", + frequency=4, + historical_adjustment_factor=0.984736, + id="Ebff20941049b81e8d1cc80d5ef0cf7b9e44318993597f922292327578e468954", + pay_date="2026-11-02", + record_date="2026-10-09", + split_adjusted_cash_amount=0.7075, + ticker="VZ", + ), + ] + # a comma-separated str must be sent as-is, not split into characters + dividends = [d for d in self.c.list_stocks_dividends(ticker_any_of="AAPL,VZ")] + self.assertEqual(dividends, expected) + # a list of values is still joined with commas + dividends = [ + d for d in self.c.list_stocks_dividends(ticker_any_of=["AAPL", "VZ"]) # type: ignore + ] + self.assertEqual(dividends, expected) diff --git a/test_rest/test_snapshots.py b/test_rest/test_snapshots.py index 3b016d71..4e501d2c 100644 --- a/test_rest/test_snapshots.py +++ b/test_rest/test_snapshots.py @@ -429,6 +429,8 @@ def test_get_snapshot_crypto_book(self): def test_get_snapshot_indices(self): ticker_any_of = ["SPX", "APx", "APy"] summary_results = self.c.get_snapshot_indices(ticker_any_of) + # the comma-separated str form must build the same request + self.assertEqual(self.c.get_snapshot_indices("SPX,APx,APy"), summary_results) expected = [ IndicesSnapshot( value=3822.39,