// settings.get / settings.set: the field <-> SettingKey <-> JSON-type mapping table in // dispatcher.cpp, called directly (no socket) against an in-memory store. #include #include "check.hpp" #include "rpc/dispatcher.hpp" #include "rpc/event_hub.hpp" #include "store/migrations.hpp" #include "store/sqlite.hpp" #include "velox_proto.hpp" using namespace velox::daemon; namespace proto = velox::proto; void run() { auto db = store::Db::open(":memory:"); CHECK(db.has_value()); if (!db) return; CHECK(store::migrate_to_head(*db).has_value()); rpc::EventHub hub; rpc::VeloxDispatcher dispatcher(*db, hub); // --- get with keys=nullopt: every one of the 43 SettingKeys comes back ----------- { proto::SettingsGetParams p; auto r = dispatcher.on_settings_get(p); CHECK(r.has_value()); if (r) { int present = 0; present += r->values.general_launchOnLogin.has_value(); present += r->values.capture_enabled.has_value(); present += r->values.saveTo_allowedRoots.has_value(); present += r->values.connection_maxConcurrentDownloads.has_value(); present += r->values.proxy_mode.has_value(); present += r->values.sounds_onError.has_value(); CHECK_EQ(present, 6); // A default, sight-checked: connection.maxConcurrentDownloads is 5 (settings.cpp). CHECK_EQ(r->values.connection_maxConcurrentDownloads.value_or(-1), std::int64_t{5}); CHECK(r->values.saveTo_allowedRoots.has_value()); if (r->values.saveTo_allowedRoots) CHECK_EQ(r->values.saveTo_allowedRoots->size(), std::size_t{1}); // Enum-typed default round-trips through parse_XXX correctly. CHECK(r->values.proxy_mode == proto::SettingsProxyMode::System); } } // --- get with an explicit key list: only those fields are populated -------------- { proto::SettingsGetParams p; p.keys = {proto::SettingKey::GeneralLaunchOnLogin, proto::SettingKey::ConnectionMaxConcurrentDownloads}; auto r = dispatcher.on_settings_get(p); CHECK(r.has_value()); if (r) { CHECK(r->values.general_launchOnLogin.has_value()); CHECK(r->values.connection_maxConcurrentDownloads.has_value()); CHECK(!r->values.capture_enabled.has_value()); CHECK(!r->values.proxy_mode.has_value()); } } // --- set: a valid change is persisted, reported in `changed`, and readable back --- { proto::SettingsSetParams p; p.values.connection_maxConcurrentDownloads = 12; p.values.general_launchOnLogin = true; auto r = dispatcher.on_settings_set(p); CHECK(r.has_value()); if (r) { CHECK_EQ(r->changed.size(), std::size_t{2}); CHECK_EQ(r->values.connection_maxConcurrentDownloads.value_or(-1), std::int64_t{12}); CHECK_EQ(r->values.general_launchOnLogin.value_or(false), true); } proto::SettingsGetParams g; g.keys = {proto::SettingKey::ConnectionMaxConcurrentDownloads}; auto g_r = dispatcher.on_settings_get(g); CHECK(g_r.has_value()); if (g_r) CHECK_EQ(g_r->values.connection_maxConcurrentDownloads.value_or(-1), std::int64_t{12}); } // --- set: setting the same value again reports it unchanged ---------------------- { proto::SettingsSetParams p; p.values.connection_maxConcurrentDownloads = 12; auto r = dispatcher.on_settings_set(p); CHECK(r.has_value()); if (r) CHECK(r->changed.empty()); } // --- set: out of range is rejected, and nothing else in the same call lands ------ { proto::SettingsSetParams p; p.values.connection_maxConcurrentDownloads = 999; // max 64 p.values.general_minimizeToTray = true; // would otherwise succeed auto r = dispatcher.on_settings_set(p); CHECK(!r.has_value()); if (!r) CHECK(r.error().code == proto::ErrorCode::InvalidParams); proto::SettingsGetParams g; g.keys = {proto::SettingKey::GeneralMinimizeToTray}; auto g_r = dispatcher.on_settings_get(g); CHECK(g_r.has_value()); // Rejected as a whole: minimizeToTray was never written even though its own value // was valid on its own. if (g_r) CHECK_EQ(g_r->values.general_minimizeToTray.value_or(true), false); } // --- set: an enum field round-trips ------------------------------------------------ { proto::SettingsSetParams p; p.values.proxy_mode = proto::SettingsProxyMode::Socks5; auto r = dispatcher.on_settings_set(p); CHECK(r.has_value()); if (r) { CHECK_EQ(r->changed.size(), std::size_t{1}); CHECK(r->values.proxy_mode == proto::SettingsProxyMode::Socks5); } } // --- set: saveTo.defaultDir outside every allowed root is rejected with -32011 ---- { proto::SettingsSetParams p; p.values.saveTo_defaultDir = "/definitely/not/an/allowed/root"; auto r = dispatcher.on_settings_set(p); CHECK(!r.has_value()); if (!r) CHECK(r.error().code == proto::ErrorCode::InvalidPath); } // --- set: saveTo.allowedRoots with an unresolvable entry is rejected with -32011 -- { proto::SettingsSetParams p; p.values.saveTo_allowedRoots = {"/this/path/should/never/exist/anywhere"}; auto r = dispatcher.on_settings_set(p); CHECK(!r.has_value()); if (!r) CHECK(r.error().code == proto::ErrorCode::InvalidPath); } } TEST_MAIN()