// Offline tests of the FinData client logic (no network): chunking, rate limiting, offline refusal, IPC page decoding. #include #include #include "raid/data/findata.hpp" #include "raid/data/io.hpp" #include "raid/core/error.hpp" #include using namespace raid; using namespace raid::findata; TEST_CASE("Date chunking respects the API caps", "[findata]") { auto c = chunk_dates(date_from_iso("2024-01-01"), date_from_iso("2024-03-15"), 31); REQUIRE(c.size() == 3); CHECK(c[0].first.iso() == "2024-01-01"); CHECK(c[0].second.iso() == "2024-01-31"); CHECK(c[1].first.iso() == "2024-02-01"); CHECK(c[2].second.iso() == "2024-03-15"); CHECK(chunk_dates(date_from_iso("2024-01-01"), date_from_iso("2024-01-01"), 31).size() == 1); CHECK(max_days_per_call(Interval::m1) == 31); CHECK(max_days_per_call(Interval::m5) == 186); CHECK(max_days_per_call(Interval::d1) == 3650); CHECK_THROWS_AS(chunk_dates(date_from_iso("2024-02-01"), date_from_iso("2024-01-01"), 31), RaidError); } TEST_CASE("Token bucket enforces the rate", "[findata]") { TokenBucket b(50.0, 2.0); const auto t0 = std::chrono::steady_clock::now(); for (int i = 0; i < 12; ++i) b.acquire(); const double s = std::chrono::duration(std::chrono::steady_clock::now() - t0).count(); CHECK(s >= 0.18); CHECK(s < 1.0); // 12 requests, burst 2, 50/s → ≥ 0.2 s } TEST_CASE("Offline mode refuses network and missing keys are reported", "[findata]") { Config c; c.offline = true; c.api_key = ""; c.cache_dir = "/tmp/raid-test-cache"; Client cl(c); CHECK(cl.key_fingerprint() == "none"); CHECK_THROWS_AS(cl.health(), RaidError); Config c2; c2.api_key = ""; c2.offline = false; CHECK_THROWS_AS(Client(c2), RaidError); } #ifdef RAID_WITH_ARROW TEST_CASE("Synthetic Arrow IPC page decodes with schema metadata", "[findata][arrow]") { auto pb = data::read_bars_arrow_ipc(std::string(RAID_SOURCE_DIR) + "/datasets/fixtures/synthetic_bars_page.arrow"); REQUIRE(pb.series.count("SYN1")); CHECK(pb.metadata["findata.timestamp_convention"] == "bar_open"); CHECK(pb.rows_read == 391); CHECK(pb.series["SYN1"].n_sessions() == 1); auto pq = data::read_bars_parquet(std::string(RAID_SOURCE_DIR) + "/datasets/fixtures/synthetic_bars_page.parquet"); CHECK(pq.rows_read == 391); CHECK(pq.series["SYN1"].close[0] == pb.series["SYN1"].close[0]); } #endif