#include "catch2/catch.hpp" #include "tools/replay/api.h" // These cover the konn3kt endpoints that replaced upstream's PyDownloader shell-outs. // Cabana's remote-routes dialog and jotpluggler's route-file listing both route through // here, so a silent change to a path or to the error envelope breaks konn3kt without // breaking the build. TEST_CASE("konn3kt api paths") { SECTION("route files") { REQUIRE(CommaApi2::routeFilesPath("a2a0ccea32023010|2023-07-27--13-01-19") == "v1/route/a2a0ccea32023010|2023-07-27--13-01-19/files"); } SECTION("devices") { REQUIRE(CommaApi2::devicesPath() == "v1/me/devices/"); } SECTION("device routes over a time range") { REQUIRE(CommaApi2::deviceRoutesPath("dongle", 1000, 2000, false) == "v1/devices/dongle/routes_segments?start=1000&end=2000"); } SECTION("device routes with only one bound") { REQUIRE(CommaApi2::deviceRoutesPath("dongle", 1000, 0, false) == "v1/devices/dongle/routes_segments?start=1000"); REQUIRE(CommaApi2::deviceRoutesPath("dongle", 0, 2000, false) == "v1/devices/dongle/routes_segments?end=2000"); } SECTION("device routes with no bounds omits the query entirely") { REQUIRE(CommaApi2::deviceRoutesPath("dongle", 0, 0, false) == "v1/devices/dongle/routes_segments"); } SECTION("preserved routes ignore the time range") { REQUIRE(CommaApi2::deviceRoutesPath("dongle", 1000, 2000, true) == "v1/devices/dongle/routes/preserved"); } } TEST_CASE("konn3kt api error envelope") { // Cabana and jotpluggler both branch on these exact strings. SECTION("success passes the body through") { REQUIRE(CommaApi2::apiResponse("[{\"dongle_id\": \"abc\"}]", 200) == "[{\"dongle_id\": \"abc\"}]"); } SECTION("401 and 403 both map to unauthorized") { REQUIRE(CommaApi2::apiResponse("", 401) == R"({"error": "unauthorized"})"); REQUIRE(CommaApi2::apiResponse("nope", 403) == R"({"error": "unauthorized"})"); } SECTION("404 maps to not_found") { REQUIRE(CommaApi2::apiResponse("", 404) == R"({"error": "not_found"})"); } SECTION("transport failure and 5xx map to network") { REQUIRE(CommaApi2::apiResponse("", 0) == R"({"error": "network"})"); REQUIRE(CommaApi2::apiResponse("boom", 500) == R"({"error": "network"})"); } SECTION("a 200 with an empty body is still an error") { REQUIRE(CommaApi2::apiResponse("", 200) == R"({"error": "network"})"); } } TEST_CASE("konn3kt base url") { // Everything is built off BASE_URL; an accidental revert to comma's host would // silently point every tool at api.comma.ai. REQUIRE(CommaApi2::BASE_URL.find("comma.ai") == std::string::npos); }