Files
rtorrent/test/rpc/jsonrpc_test.cc
T
kannibalox 9f48226663 Add JSON-RPC capability
Inline nlohmann/json for the JSON parsing itself, and handle requests
with the same SCGI interface as XML-RPC.

Based off the work in https://github.com/jesec/rtorrent
2025-01-20 12:31:04 +01:00

139 lines
6.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "config.h"
#include <string>
#include "command_helpers.h"
#include "rpc/command_map.h"
#include "control.h"
#include "globals.h"
#include "jsonrpc_test.h"
CPPUNIT_TEST_SUITE_REGISTRATION(JsonrpcTest);
torrent::Object
jsonrpc_cmd_test_reflect(rpc::target_type t, const torrent::Object& obj) { return obj; }
void initialize_command_dynamic();
// Name, Request, Expected response
std::vector<std::tuple<std::string, std::string, std::string>> basic_jsonrpc_requests = {
std::make_tuple("Basic call",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[]})"),
std::make_tuple("Basic call with empty params",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[]})"),
std::make_tuple("Basic call without params",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[]})"),
std::make_tuple("Basic call with null id",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "id": null})",
R"({"id":null,"jsonrpc":"2.0","result":[]})"),
std::make_tuple("Basic call with string id",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "id": "1"})",
R"({"id":"1","jsonrpc":"2.0","result":[]})"),
std::make_tuple("UTF-8 string",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", "чао"], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":["чао"]})"),
std::make_tuple("Emoji string",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", "😊"], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":["😊"]})"),
std::make_tuple("Small int",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", 41], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[41]})"),
std::make_tuple("Large int",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", 2247483647], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[2247483647]})"),
std::make_tuple("Boolean",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", true], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[1]})"),
std::make_tuple("Negative large ints",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", -2247483647], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[-2247483647]})"),
std::make_tuple("Simple array",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", [2247483647]], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[[2247483647]]})"),
std::make_tuple("Empty array",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", []], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[[]]})"),
std::make_tuple("Empty struct",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", {}], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[{}]})"),
std::make_tuple("Simple struct",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", {"lowerBound": 18}], "id": 1})",
R"({"id":1,"jsonrpc":"2.0","result":[{"lowerBound":18}]})"),
std::make_tuple("Notification",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""]})",
""),
std::make_tuple("Batch",
R"([{"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""], "id": 1},{"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""], "id": 2}])",
R"([{"id":1,"jsonrpc":"2.0","result":[]},{"id":2,"jsonrpc":"2.0","result":[]}])"),
std::make_tuple("Batch with notification",
R"([{"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""], "id": 1},{"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [""]}])",
R"([{"id":1,"jsonrpc":"2.0","result":[]}])"),
std::make_tuple("Invalid - empty batch",
"[]",
R"({"error":{"code":-32600,"message":"invalid request: empty batch"},"id":null,"jsonrpc":"2.0"})"),
std::make_tuple("Invalid - missing method",
R"({"jsonrpc": "2.0", "method": "no_such_method", "id": 1})",
R"({"error":{"code":-32601,"message":"method not found: no_such_method"},"id":1,"jsonrpc":"2.0"})"),
std::make_tuple("Invalid - i8 target",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": [41], "id": 1})",
R"({"error":{"code":-32602,"message":"invalid parameters: target must be a string"},"id":1,"jsonrpc":"2.0"})"),
std::make_tuple("Invalid - broken JSON",
R"(nrpc": "2.0", "method": "jsonrpc_reflect", "params": [41], ")",
R"({"error":{"code":-32700,"message":"[json.exception.parse_error.101] parse error at line 1, column 2: syntax error while parsing value - invalid literal; last read: 'nr'"},"id":null,"jsonrpc":"2.0"})"),
std::make_tuple("Invalid - float",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", 3.14], "id": 1})",
R"({"error":{"code":-32602,"message":"invalid parameters: unexpected data type float"},"id":1,"jsonrpc":"2.0"})"),
std::make_tuple("Invalid - invalid UTF-8",
R"({"jsonrpc": "2.0", "method": "jsonrpc_reflect", "params": ["", ")"
"\xc3\x28"
R"("], "id": 1})",
R"({"error":{"code":-32700,"message":"[json.exception.parse_error.101] parse error at line 1, column 66: syntax error while parsing value - invalid string: ill-formed UTF-8 byte; last read: '\"('"},"id":null,"jsonrpc":"2.0"})"),
};
void
JsonrpcTest::setUp() {
m_jsonrpc = rpc::JsonRpc();
m_jsonrpc.initialize();
setlocale(LC_ALL, "");
cachedTime = rak::timer::current();
control = new Control;
if (rpc::commands.find("jsonrpc_reflect") == rpc::commands.end()) {
CMD2_ANY("jsonrpc_reflect", &jsonrpc_cmd_test_reflect);
}
}
void
JsonrpcTest::test_basics() {
for (auto& test : basic_jsonrpc_requests) {
std::string output;
m_jsonrpc.process(std::get<1>(test).c_str(), std::get<1>(test).size(), [&output](const char* c, uint32_t l) { output.append(c, l); return true; });
CPPUNIT_ASSERT_EQUAL_MESSAGE(std::get<0>(test), std::get<2>(test), output);
}
}