The download entry point the AGENT-CORE brief asked for on day one and that slipped. DAEMON has an RPC surface and a store and, until this is agreed, nothing in velox::core to call. vdm/task/download.hpp — DownloadSpec (the resolved subset DAEMON hands in: absolute save_path, verbatim browser headers, requested segments/buffer, optional probe_hint / checksum / auth, allow_resume), EngineState (the CORE-owned subset of the wire TaskState), Progress / SegmentProgress, DownloadCallbacks (on_progress <=4 Hz, on_state for every transition incl. auto-pauses, on_auth_required, on_decision_needed, on_finished last), DownloadHandle (pause/resume/cancel — idempotent per the ADR 0013 signature — plus provide_auth / decide / refresh_url, and synchronous state()/progress() snapshots). vdm/engine.hpp — Engine: start(spec, callbacks) -> handle, segment_budget() (DAEMON's sched/ admission surface, ADR 0011), live connection.* setters, a standalone probe() on the pool outside the segment budget. core/docs/engine-api-m1.md — the review doc: field semantics, the state machine, threading/lifetime rules (which thread callbacks arrive on, what is legal from inside one, handle/engine lifetime), the shared-`paused` idempotency contract as a signature, and five open questions for DAEMON. Value types compile and are covered by api_compiles_test; Engine / DownloadHandle bodies land in stage 8, built against whatever DAEMON signs off here. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
85 lines
2.6 KiB
C++
85 lines
2.6 KiB
C++
// The engine API sketch must compile and its value types must behave. Engine /
|
|
// DownloadHandle bodies land in stage 8; this only exercises the data shapes DAEMON
|
|
// builds against.
|
|
|
|
#include "vdm/engine.hpp"
|
|
#include "vdm/task/download.hpp"
|
|
|
|
#include <type_traits>
|
|
|
|
#include "vtest.hpp"
|
|
|
|
using namespace vdm;
|
|
using namespace vdm::task;
|
|
|
|
VT_TEST(api_download_spec_defaults) {
|
|
DownloadSpec s;
|
|
s.url = "https://example.com/big.iso";
|
|
s.save_path = "/home/u/Downloads/big.iso";
|
|
VT_CHECK(s.mirrors.empty());
|
|
VT_CHECK(!s.segments.has_value());
|
|
VT_CHECK(!s.buffer_bytes.has_value());
|
|
VT_CHECK(!s.checksum.has_value());
|
|
VT_CHECK(!s.probe_hint.has_value());
|
|
VT_CHECK(s.allow_resume);
|
|
VT_CHECK(s.proxy.kind == net::ProxyKind::none);
|
|
VT_CHECK(s.auth.scheme == net::AuthScheme::none);
|
|
}
|
|
|
|
VT_TEST(api_state_helpers) {
|
|
VT_CHECK(is_terminal(EngineState::complete));
|
|
VT_CHECK(is_terminal(EngineState::failed));
|
|
VT_CHECK(is_terminal(EngineState::cancelled));
|
|
VT_CHECK(!is_terminal(EngineState::paused));
|
|
VT_CHECK(!is_terminal(EngineState::downloading));
|
|
}
|
|
|
|
VT_TEST(api_callbacks_are_all_optional) {
|
|
DownloadCallbacks cb; // every std::function default-constructs empty
|
|
VT_CHECK(!cb.on_progress);
|
|
VT_CHECK(!cb.on_state);
|
|
VT_CHECK(!cb.on_auth_required);
|
|
VT_CHECK(!cb.on_decision_needed);
|
|
VT_CHECK(!cb.on_finished);
|
|
|
|
cb.on_state = [](EngineState, EngineState, const std::optional<vdm::ErrorInfo> &) {};
|
|
cb.on_finished = [](Result<DownloadOutcome>) {};
|
|
VT_CHECK(cb.on_state && cb.on_finished);
|
|
}
|
|
|
|
VT_TEST(api_value_types_roundtrip) {
|
|
Progress p;
|
|
p.downloaded = 1234;
|
|
p.total = 5000;
|
|
p.effective_segments = 4;
|
|
SegmentProgress sp;
|
|
sp.index = 0;
|
|
sp.end = 1249;
|
|
sp.completed = 1234;
|
|
p.segments.push_back(sp);
|
|
VT_CHECK_EQ(p.segments.size(), 1u);
|
|
VT_CHECK_EQ(p.segments[0].end, 1249u);
|
|
|
|
DownloadOutcome o;
|
|
o.final_path = "/x";
|
|
o.bytes = 5000;
|
|
VT_CHECK_EQ(o.bytes, 5000u);
|
|
|
|
AuthChallenge a;
|
|
a.host = "h";
|
|
a.scheme = AuthChallenge::Scheme::digest;
|
|
VT_CHECK(a.scheme == AuthChallenge::Scheme::digest);
|
|
|
|
DecisionRequest d;
|
|
d.kind = DecisionRequest::Kind::server_file_changed;
|
|
d.detail = "changed";
|
|
VT_CHECK(d.kind == DecisionRequest::Kind::server_file_changed);
|
|
}
|
|
|
|
VT_TEST(api_handle_and_engine_are_move_only_shaped) {
|
|
static_assert(!std::is_copy_constructible_v<Engine>, "Engine is non-copyable");
|
|
static_assert(std::is_copy_constructible_v<DownloadHandle>, "handle is a shared handle");
|
|
DownloadHandle h; // default handle is invalid until Engine::start() fills it
|
|
VT_CHECK(!h.valid());
|
|
}
|