The scriptable client, built now rather than last: it is how the daemon gets exercised before the GUI is pointed at it (AGENT-DAEMON.md). - src/client — synchronous blocking RPC over the Unix socket: resolve $XDG_RUNTIME_DIR/velox/velox.sock, connect, session.hello, one framed request/reply per call. Distinguishes transport failure (exit 3) from a daemon-returned error (exit 1). - src/main — subcommands add/ls/pause/resume/rm; --json prints the raw JSON-RPC result or error; --dir/--out/--segments on add; --delete-file on rm. Usage errors exit 2. ls works end to end against veloxd today (empty table). add and the bulk verbs reach the daemon and surface its "not implemented" (-32603) cleanly until the store lands — the plumbing is done, the commands light up as handlers do. Test velox.client: the real Client against an in-process UdsServer — no-daemon path, session.hello, download.list, and a not-implemented method surfacing as an RPC error rather than a transport error. ASan+UBSan clean; full tree (21 tests, incl. conformance) green. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01Upd9WhG9oppieig5nRDLig
79 lines
2.2 KiB
C++
79 lines
2.2 KiB
C++
// The CLI's Client against a real in-process daemon RPC server.
|
|
|
|
#include <sys/stat.h>
|
|
#include <unistd.h>
|
|
|
|
#include <cstdlib>
|
|
#include <string>
|
|
#include <thread>
|
|
|
|
#include "check.hpp"
|
|
#include "client.hpp"
|
|
#include "rpc/dispatcher.hpp"
|
|
#include "rpc/event_loop.hpp"
|
|
#include "rpc/uds_server.hpp"
|
|
|
|
namespace rpc = velox::daemon::rpc;
|
|
using velox::cli::CallError;
|
|
using velox::cli::Client;
|
|
|
|
void run() {
|
|
// connect() with no daemon -> a kConnect error, not a crash.
|
|
{
|
|
::setenv("XDG_RUNTIME_DIR", "/tmp/velox-cli-test-nonexistent-xyz", 1);
|
|
Client c;
|
|
auto err = c.connect();
|
|
CHECK(err.has_value());
|
|
if (err) CHECK_EQ(err->code, CallError::kConnect);
|
|
}
|
|
|
|
// Point XDG_RUNTIME_DIR at a fresh temp dir; the client derives
|
|
// <XDG_RUNTIME_DIR>/velox/velox.sock and the server binds exactly that.
|
|
char tmpl[] = "/tmp/velox-cli-test-XXXXXX";
|
|
const char* xdg = ::mkdtemp(tmpl);
|
|
CHECK(xdg != nullptr);
|
|
if (xdg == nullptr) return;
|
|
::setenv("XDG_RUNTIME_DIR", xdg, 1);
|
|
const std::string velox_dir = std::string(xdg) + "/velox";
|
|
::mkdir(velox_dir.c_str(), 0700);
|
|
const std::string server_sock = velox_dir + "/velox.sock";
|
|
|
|
rpc::EventLoop loop;
|
|
rpc::VeloxDispatcher dispatcher;
|
|
rpc::UdsServer server(loop, dispatcher, server_sock);
|
|
const auto ec = server.start();
|
|
CHECK(!ec);
|
|
if (ec) return;
|
|
std::thread th([&loop] { loop.run(); });
|
|
|
|
{
|
|
Client c;
|
|
auto err = c.connect();
|
|
CHECK(!err.has_value());
|
|
if (err) {
|
|
loop.stop();
|
|
th.join();
|
|
return;
|
|
}
|
|
CHECK_EQ(c.hello_result().value("transport", ""), std::string("uds"));
|
|
|
|
auto ls = c.call("download.list", nlohmann::json::object());
|
|
CHECK(ls.has_value());
|
|
if (ls) {
|
|
CHECK_EQ(ls->value("total", -1), 0);
|
|
CHECK(ls->at("items").is_array());
|
|
}
|
|
|
|
// A not-yet-implemented method surfaces the daemon's error, not a transport error.
|
|
auto add = c.call("download.add", {{"url", "https://example.com/x"}});
|
|
CHECK(!add.has_value());
|
|
if (!add) CHECK_EQ(add.error().code, -32603);
|
|
}
|
|
|
|
loop.stop();
|
|
th.join();
|
|
::unlink(server_sock.c_str());
|
|
}
|
|
|
|
TEST_MAIN()
|