// Tests for oatpp-authkit/util/RateLimiter.hpp — constructor validation // (authkit#16 M-7) and basic token-bucket behaviour. #include "oatpp-authkit/util/RateLimiter.hpp" #include #include #include namespace { int g_failures = 0; #define REQUIRE(expr) do { \ if (!(expr)) { \ std::fprintf(stderr, "FAIL %s:%d %s\n", __FILE__, __LINE__, #expr); \ ++g_failures; \ } \ } while (0) using namespace oatpp_authkit; template bool throwsInvalidArg(F&& f) { try { f(); } catch (const std::invalid_argument&) { return true; } catch (...) { return false; } return false; } void test_ctor_validation() { REQUIRE(throwsInvalidArg([]{ RateLimiter r(0.0, 1.0); })); // capacity < 1 REQUIRE(throwsInvalidArg([]{ RateLimiter r(-5.0, 1.0); })); // negative capacity REQUIRE(throwsInvalidArg([]{ RateLimiter r(10.0, 0.0); })); // refill 0 → silent disable REQUIRE(throwsInvalidArg([]{ RateLimiter r(10.0, -1.0); })); // negative refill REQUIRE(throwsInvalidArg([]{ RateLimiter r(std::nan(""), 1.0); })); // NaN capacity REQUIRE(throwsInvalidArg([]{ RateLimiter r(10.0, std::nan("")); })); // NaN refill REQUIRE(throwsInvalidArg([]{ RateLimiter r(1.0/0.0, 1.0); })); // inf capacity // Valid construction does not throw. bool ok = true; try { RateLimiter r(3.0, 0.5); (void)r; } catch (...) { ok = false; } REQUIRE(ok); } void test_burst_then_deny_and_key_isolation() { RateLimiter rl(3.0, 0.001); // 3 burst, negligible refill within the test REQUIRE(rl.allow("ip-a")); REQUIRE(rl.allow("ip-a")); REQUIRE(rl.allow("ip-a")); REQUIRE(!rl.allow("ip-a")); // 4th denied // Different key has its own independent bucket. REQUIRE(rl.allow("ip-b")); } } // namespace int main() { test_ctor_validation(); test_burst_then_deny_and_key_isolation(); std::printf("%s (%d failures)\n", g_failures ? "FAIL" : "OK", g_failures); return g_failures ? 1 : 0; }