cli: velox — add / ls / pause / resume / rm, with --json (build step 8, pulled forward)
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
This commit is contained in:
@@ -0,0 +1,13 @@
|
||||
# CLI integration test: a real in-process UdsServer on a temp socket, the real Client
|
||||
# against it. Links veloxd_rpc for the server half.
|
||||
|
||||
add_executable(velox_client_test client_test.cpp ${CMAKE_CURRENT_SOURCE_DIR}/../src/client.cpp)
|
||||
target_include_directories(velox_client_test PRIVATE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../src
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../../daemon/tests
|
||||
)
|
||||
target_compile_features(velox_client_test PRIVATE cxx_std_23)
|
||||
target_compile_options(velox_client_test PRIVATE -Wall -Wextra -Wpedantic -Werror)
|
||||
target_link_libraries(velox_client_test PRIVATE veloxd_rpc velox::proto nlohmann_json::nlohmann_json)
|
||||
add_test(NAME velox.client COMMAND velox_client_test)
|
||||
set_tests_properties(velox.client PROPERTIES TIMEOUT 30)
|
||||
@@ -0,0 +1,78 @@
|
||||
// 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()
|
||||
Reference in New Issue
Block a user