// Copyright (c) 2025 YAMS Contributors // SPDX-License-Identifier: GPL-3.0-or-later // // Unit tests for ConfigResolver component (PBI-090) // // Catch2 migration from GTest (yams-3s4 / yams-zns) // pi-lens-ignore: fatal error #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../../../common/test_helpers_catch2.h" using namespace yams::daemon; using Catch::Matchers::ContainsSubstring; namespace { struct ConfigResolverFixture { std::filesystem::path tempDir; ConfigResolverFixture() { tempDir = std::filesystem::temp_directory_path() / ("yams_config_test_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); std::filesystem::create_directories(tempDir); } ~ConfigResolverFixture() { std::error_code ec; std::filesystem::remove_all(tempDir, ec); } std::filesystem::path writeToml(const std::string& filename, const std::string& content) { auto path = tempDir / filename; std::ofstream out(path); out << content; return path; } }; class PostIngestStageActivityGuard { public: PostIngestStageActivityGuard() : previousMask_(yams::daemon::TuneAdvisor::postIngestStageActiveMask()) { for (std::uint8_t i = 0; i < 6; ++i) { yams::daemon::TuneAdvisor::setPostIngestStageActive( static_cast(i), true); } } ~PostIngestStageActivityGuard() { for (std::uint8_t i = 0; i < 6; ++i) { yams::daemon::TuneAdvisor::setPostIngestStageActive( static_cast(i), (previousMask_ & (1u << i)) != 0); } } PostIngestStageActivityGuard(const PostIngestStageActivityGuard&) = delete; PostIngestStageActivityGuard& operator=(const PostIngestStageActivityGuard&) = delete; private: std::uint32_t previousMask_; }; class ProfileGuard { yams::daemon::TuneAdvisor::Profile prev_; public: explicit ProfileGuard(yams::daemon::TuneAdvisor::Profile profile) : prev_(yams::daemon::TuneAdvisor::tuningProfile()) { yams::daemon::TuneAdvisor::setTuningProfile(profile); } ~ProfileGuard() { yams::daemon::TuneAdvisor::setTuningProfile(prev_); } ProfileGuard(const ProfileGuard&) = delete; ProfileGuard& operator=(const ProfileGuard&) = delete; }; using EnvGuard = yams::test::ScopedEnvVar; std::vector unsetEnvironment(std::initializer_list names) { std::vector guards; guards.reserve(names.size()); for (const char* name : names) { guards.emplace_back(name, std::nullopt); } return guards; } TuningConfig makeExplicitTuningBaseline() { TuningConfig baseline; baseline.targetCpuPercent = 173; baseline.postIngestCapacity = 2345; baseline.postIngestThreadsMin = 3; baseline.postIngestThreadsMax = 11; baseline.postIngestCoalesceMs = 7; baseline.ingestStoreBatchSize = 31; baseline.admitWarnThreshold = 1701; baseline.admitStopThreshold = 2201; baseline.controlIntervalMs = 777; baseline.holdMs = 2222; baseline.topologyAlgorithm = "multiscale"; baseline.tuneAdvisorOverridesResolved = false; baseline.provenance.clear(); return baseline; } class RuntimeTuningResetGuard { public: RuntimeTuningResetGuard() = default; ~RuntimeTuningResetGuard() { // Clear fixture overrides rather than publishing its arbitrary baseline // into subsequent cases. Keep the normal coherent publication protocol. const auto update = TuneAdvisor::beginConfiguredOverrideUpdate(); TuneAdvisor::resetConfiguredOverrides(); } RuntimeTuningResetGuard(const RuntimeTuningResetGuard&) = delete; RuntimeTuningResetGuard& operator=(const RuntimeTuningResetGuard&) = delete; }; } // namespace TEST_CASE("TuneAdvisor bounded override readers share env and override behavior", "[daemon][components][tuning][catch2]") { TuneAdvisor::setWorkerPollMs(0); TuneAdvisor::setPoolScaleStep(0); TuneAdvisor::setConnectionSlotsScaleStep(0); { EnvGuard env("YAMS_WORKER_POLL_MS", "75"); CHECK((TuneAdvisor::workerPollMs() == 75u)); TuneAdvisor::setWorkerPollMs(90u); CHECK((TuneAdvisor::workerPollMs() == 90u)); TuneAdvisor::setWorkerPollMs(0); CHECK((TuneAdvisor::workerPollMs() == 75u)); } { EnvGuard env("YAMS_WORKER_POLL_MS", "49"); CHECK((TuneAdvisor::workerPollMs() == 150u)); } { EnvGuard env("YAMS_POOL_SCALE_STEP", "16"); CHECK((TuneAdvisor::poolScaleStep() == 16)); } { EnvGuard env("YAMS_POOL_SCALE_STEP", "17"); CHECK((TuneAdvisor::poolScaleStep() == 1)); } { EnvGuard env("YAMS_CONN_SLOTS_STEP", "128"); CHECK((TuneAdvisor::connectionSlotsScaleStep() == 128u)); } { EnvGuard env("YAMS_CONN_SLOTS_STEP", "129"); CHECK((TuneAdvisor::connectionSlotsScaleStep() == 16u)); } TuneAdvisor::setWorkerPollMs(0); TuneAdvisor::setPoolScaleStep(0); TuneAdvisor::setConnectionSlotsScaleStep(0); } TEST_CASE("Fresh typed tuning lifecycle does not inherit prior process overrides", "[daemon][components][config][tuning][lifecycle][catch2]") { EnvGuard ipcCompatibility{"YAMS_IPC_TIMEOUT_MS", ""}; EnvGuard resourceCompatibility{"YAMS_MEMORY_WARNING_PCT", ""}; EnvGuard rpcCompatibility{"YAMS_POST_INGEST_RPC_QUEUE_MAX", ""}; EnvGuard governorCompatibility{"YAMS_ENABLE_RESOURCE_GOVERNOR", ""}; const auto baseline = makeExplicitTuningBaseline(); const RuntimeTuningResetGuard reset; (void)ConfigResolver::applyRuntimeTuning({}, baseline); const auto baselineIpcTimeout = TuneAdvisor::ipcTimeoutMs(); const auto baselineMemoryWarning = TuneAdvisor::memoryWarningThreshold(); const auto baselineRpcQueueMax = TuneAdvisor::postIngestRpcQueueMax(); const auto baselineGovernorEnabled = TuneAdvisor::enableResourceGovernor(); const auto overriddenIpcTimeout = baselineIpcTimeout == 4321u ? 4322u : 4321u; const auto overriddenMemoryWarning = baselineMemoryWarning > 0.8 ? 0.61 : 0.91; const auto overriddenRpcQueueMax = baselineRpcQueueMax == 333u ? 334u : 333u; TuneAdvisor::setIpcTimeoutMs(overriddenIpcTimeout); TuneAdvisor::setMemoryWarningThreshold(overriddenMemoryWarning); TuneAdvisor::setPostIngestRpcQueueMax(overriddenRpcQueueMax); TuneAdvisor::setEnableResourceGovernor(!baselineGovernorEnabled); REQUIRE(TuneAdvisor::ipcTimeoutMs() == overriddenIpcTimeout); REQUIRE(TuneAdvisor::memoryWarningThreshold() == Catch::Approx(overriddenMemoryWarning)); REQUIRE(TuneAdvisor::postIngestRpcQueueMax() == overriddenRpcQueueMax); REQUIRE(TuneAdvisor::enableResourceGovernor() != baselineGovernorEnabled); const auto versionBefore = TuneAdvisor::configuredOverridesVersion(); REQUIRE_FALSE(versionBefore & 1U); const auto fresh = ConfigResolver::applyRuntimeTuning({}, baseline); CHECK(TuneAdvisor::configuredOverridesVersion() == versionBefore + 2); CHECK(fresh.targetCpuPercent == baseline.targetCpuPercent); CHECK(fresh.postIngestCapacity == baseline.postIngestCapacity); CHECK(fresh.ingestStoreBatchSize == baseline.ingestStoreBatchSize); CHECK(fresh.controlIntervalMs == baseline.controlIntervalMs); CHECK(fresh.topologyAlgorithm == baseline.topologyAlgorithm); CHECK(fresh.tuneAdvisorOverridesResolved); CHECK(fresh.provenance.empty()); CHECK(TuneAdvisor::ipcTimeoutMs() == baselineIpcTimeout); CHECK(TuneAdvisor::memoryWarningThreshold() == Catch::Approx(baselineMemoryWarning)); CHECK(TuneAdvisor::postIngestRpcQueueMax() == baselineRpcQueueMax); CHECK(TuneAdvisor::enableResourceGovernor() == baselineGovernorEnabled); } TEST_CASE("Fresh runtime tuning resolution revokes removed configured overrides", "[daemon][components][config][tuning][reload][catch2]") { EnvGuard ipcCompatibility{"YAMS_IPC_TIMEOUT_MS", ""}; EnvGuard admissionCompatibility{"YAMS_ADMISSION_CONTROL", ""}; EnvGuard memoryCompatibility{"YAMS_MEMORY_WARNING_PCT", ""}; EnvGuard postIngestCompatibility{"YAMS_POST_INGEST_RPC_QUEUE_MAX", ""}; const auto baseline = makeExplicitTuningBaseline(); const RuntimeTuningResetGuard reset; (void)ConfigResolver::applyRuntimeTuning({}, baseline); const auto baselineIpcTimeout = TuneAdvisor::ipcTimeoutMs(); const auto baselineAdmissionControl = TuneAdvisor::enableAdmissionControl(); const auto baselineMemoryWarning = TuneAdvisor::memoryWarningThreshold(); const auto baselineRpcQueueMax = TuneAdvisor::postIngestRpcQueueMax(); const auto overriddenIpcTimeout = baselineIpcTimeout == 4321u ? 4322u : 4321u; const auto overriddenMemoryWarning = baselineMemoryWarning > 0.8 ? 0.61 : 0.91; const auto overriddenRpcQueueMax = baselineRpcQueueMax == 333u ? 334u : 333u; ConfigResolver::ConfigSections configured; configured["tuning"] = {{"target_cpu_percent", "321"}, {"post_ingest_capacity", "4096"}}; configured["tuning.ipc"] = {{"timeout_ms", std::to_string(overriddenIpcTimeout)}}; configured["tuning.resource"] = { {"admission_control", baselineAdmissionControl ? "false" : "true"}, {"memory_warning_threshold", std::to_string(overriddenMemoryWarning)}}; configured["tuning.post_ingest"] = {{"rpc_queue_max", std::to_string(overriddenRpcQueueMax)}}; const auto applied = ConfigResolver::applyRuntimeTuning(configured, baseline); CHECK(applied.targetCpuPercent == 321u); CHECK(applied.postIngestCapacity == 4096u); REQUIRE(applied.provenance.at("tuning.target_cpu_percent") == "config:tuning.target_cpu_percent"); REQUIRE(applied.provenance.at("tuning.ipc.timeout_ms") == "config:tuning.ipc.timeout_ms"); CHECK(TuneAdvisor::ipcTimeoutMs() == overriddenIpcTimeout); CHECK(TuneAdvisor::enableAdmissionControl() != baselineAdmissionControl); CHECK(TuneAdvisor::memoryWarningThreshold() == Catch::Approx(overriddenMemoryWarning)); CHECK(TuneAdvisor::postIngestRpcQueueMax() == overriddenRpcQueueMax); const auto versionBeforeRemoval = TuneAdvisor::configuredOverridesVersion(); const auto reverted = ConfigResolver::applyRuntimeTuning({}, baseline); CHECK(TuneAdvisor::configuredOverridesVersion() == versionBeforeRemoval + 2); CHECK(reverted.targetCpuPercent == baseline.targetCpuPercent); CHECK(reverted.postIngestCapacity == baseline.postIngestCapacity); CHECK(reverted.ingestStoreBatchSize == baseline.ingestStoreBatchSize); CHECK(reverted.controlIntervalMs == baseline.controlIntervalMs); CHECK(reverted.topologyAlgorithm == baseline.topologyAlgorithm); CHECK(reverted.tuneAdvisorOverridesResolved); CHECK(reverted.provenance.empty()); CHECK(TuneAdvisor::ipcTimeoutMs() == baselineIpcTimeout); CHECK(TuneAdvisor::enableAdmissionControl() == baselineAdmissionControl); CHECK(TuneAdvisor::memoryWarningThreshold() == Catch::Approx(baselineMemoryWarning)); CHECK(TuneAdvisor::postIngestRpcQueueMax() == baselineRpcQueueMax); } TEST_CASE("ConfigResolver applies one typed tuning snapshot for startup and reload", "[daemon][components][config][tuning][catch2]") { EnvGuard ipcCompatibility{"YAMS_IPC_TIMEOUT_MS", "8765"}; EnvGuard resourceCompatibility{"YAMS_MEMORY_WARNING_PCT", "80"}; ConfigResolver::ConfigSections sections; sections["tuning"] = {{"target_cpu_percent", "175"}, {"post_ingest_capacity", "4096"}, {"post_ingest_threads_min", "3"}, {"control_interval_ms", "250"}}; sections["tuning.ingest"] = {{"store_batch_size", "23"}}; sections["tuning.ipc"] = {{"timeout_ms", "4321"}, {"stream_chunk_timeout_ms", "9876"}}; sections["tuning.resource"] = {{"memory_warning_threshold", "0.77"}}; sections["tuning.post_ingest"] = {{"coalesce_ms", "3"}, {"total_concurrent", "12"}, {"rpc_queue_max", "333"}, {"rpc_max_per_batch", "7"}}; TuningConfig base; base.postIngestThreadsMax = 12; base.topologyAlgorithm = "multiscale"; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, base); CHECK((resolved.targetCpuPercent == 175)); CHECK((resolved.postIngestCapacity == 4096)); CHECK((resolved.postIngestThreadsMin == 3)); CHECK((resolved.ingestStoreBatchSize == 23)); CHECK((resolved.postIngestCoalesceMs == 3)); CHECK((resolved.controlIntervalMs == 250)); CHECK((resolved.postIngestThreadsMax == 12)); CHECK((resolved.topologyAlgorithm == "multiscale")); CHECK((resolved.provenance.at("tuning.target_cpu_percent") == "config:tuning.target_cpu_percent")); CHECK((resolved.provenance.at("tuning.post_ingest.total_concurrent") == "config:tuning.post_ingest.total_concurrent")); CHECK((TuneAdvisor::postIngestTotalConcurrent() == 12u)); CHECK((TuneAdvisor::postIngestRpcQueueMax() == 333u)); CHECK((TuneAdvisor::postIngestRpcMaxPerBatch() == 7u)); CHECK((TuneAdvisor::ipcTimeoutMs() == 4321u)); CHECK((TuneAdvisor::streamChunkTimeoutMs() == 9876u)); CHECK((TuneAdvisor::memoryWarningThreshold() == Catch::Approx(0.77))); sections["tuning"] = {{"target_cpu_percent", "-1"}, {"post_ingest_capacity", "-2"}, {"control_interval_ms", "4294967296"}}; sections["tuning.ingest"] = {{"store_batch_size", "257"}}; sections["tuning.post_ingest"] = {{"coalesce_ms", "21"}}; base.targetCpuPercent = 80; base.postIngestCapacity = 2048; base.ingestStoreBatchSize = 32; base.controlIntervalMs = 500; base.postIngestCoalesceMs = 4; const auto rejected = ConfigResolver::applyRuntimeTuning(sections, base); CHECK((rejected.targetCpuPercent == 80)); CHECK((rejected.postIngestCapacity == 2048)); CHECK((rejected.ingestStoreBatchSize == 32)); CHECK((rejected.controlIntervalMs == 500)); CHECK((rejected.postIngestCoalesceMs == 4)); TuneAdvisor::setPostIngestTotalConcurrent(0); TuneAdvisor::setPostIngestRpcQueueMax(0); TuneAdvisor::setPostIngestRpcMaxPerBatch(0); TuneAdvisor::setIpcTimeoutMs(0); TuneAdvisor::setStreamChunkTimeoutMs(0); TuneAdvisor::setMemoryWarningThreshold(0.0); } TEST_CASE("ConfigResolver applies typed host pressure thresholds", "[daemon][components][config][tuning][host_pressure][catch2]") { const HostPressureThresholds defaults{}; ConfigResolver::ConfigSections sections; sections["tuning.resource"] = { {"host_pressure", "false"}, {"host_cpu_pressure_pct", "25"}, {"host_io_pressure_pct", "15.5"}, {"host_memory_pressure_pct", "5"}, {"host_load_per_cpu", "2.5"}, {"background_max_deferral_s", "90"}}; const auto applied = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK_FALSE(TuneAdvisor::hostPressureEnabled()); auto thresholds = TuneAdvisor::hostPressureThresholds(); CHECK((thresholds.cpuSomePct == Catch::Approx(25.0))); CHECK((thresholds.ioSomePct == Catch::Approx(15.5))); CHECK((thresholds.memorySomePct == Catch::Approx(5.0))); CHECK((thresholds.loadPerCpu == Catch::Approx(2.5))); CHECK((TuneAdvisor::backgroundMaxDeferralMs() == 90'000u)); CHECK((applied.provenance.at("tuning.resource.host_cpu_pressure_pct") == "config:tuning.resource.host_cpu_pressure_pct")); CHECK((applied.provenance.at("tuning.resource.host_pressure") == "config:tuning.resource.host_pressure")); // Out-of-range values are rejected and leave the defaults in place. sections["tuning.resource"] = {{"host_cpu_pressure_pct", "0"}, {"host_io_pressure_pct", "101"}, {"host_memory_pressure_pct", "-3"}, {"host_load_per_cpu", "0.01"}, {"background_max_deferral_s", "86401"}}; const auto rejected = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(TuneAdvisor::hostPressureEnabled()); thresholds = TuneAdvisor::hostPressureThresholds(); CHECK((thresholds.cpuSomePct == Catch::Approx(defaults.cpuSomePct))); CHECK((thresholds.ioSomePct == Catch::Approx(defaults.ioSomePct))); CHECK((thresholds.memorySomePct == Catch::Approx(defaults.memorySomePct))); CHECK((thresholds.loadPerCpu == Catch::Approx(defaults.loadPerCpu))); CHECK_FALSE(rejected.provenance.contains("tuning.resource.host_cpu_pressure_pct")); CHECK((TuneAdvisor::backgroundMaxDeferralMs() == 300'000u)); // Zero is a valid setting: it turns background deferral off. sections["tuning.resource"] = {{"background_max_deferral_s", "0"}}; (void)ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK((TuneAdvisor::backgroundMaxDeferralMs() == 0u)); // Removing the keys on reload restores the defaults. (void)ConfigResolver::applyRuntimeTuning({}, TuningConfig{}); CHECK(TuneAdvisor::hostPressureEnabled()); CHECK((TuneAdvisor::backgroundMaxDeferralMs() == 300'000u)); CHECK((TuneAdvisor::hostPressureThresholds().cpuSomePct == Catch::Approx(defaults.cpuSomePct))); } TEST_CASE_METHOD(ConfigResolverFixture, "Compatibility embedding runtime policy parses as strictly as the typed resolver", "[daemon][components][config][embeddings][catch2]") { // Two resolvers read the same keys; the compatibility one used stoull in a bare catch, // so "12abc" became 12 on one path and a rejected value on the other. const auto configPath = writeToml("strict-runtime.toml", "[embeddings]\nbackend = \"simeon\"\n"); EnvGuard configEnv{"YAMS_CONFIG_PATH", configPath.string()}; EnvGuard batch{"YAMS_EMBED_BATCH", "12abc"}; EnvGuard target{"YAMS_EMBED_BATCH_TARGET", "2048"}; const auto policy = ConfigResolver::resolveEmbeddingRuntimePolicy(); CHECK_FALSE(policy.batchSize.has_value()); REQUIRE(policy.batchTarget.has_value()); CHECK((*policy.batchTarget == 2048)); DaemonConfig config; const auto typed = ConfigResolver::resolveEmbeddingConfig(config, {}); CHECK((typed.runtime.batchSize.has_value() == policy.batchSize.has_value())); CHECK((typed.runtime.batchTarget == policy.batchTarget)); } TEST_CASE("ConfigResolver rejects 0 for [tuning] integer keys instead of ignoring it", "[daemon][components][config][tuning][catch2]") { // For the uint32 overrides 0 is the unset sentinel and below every minimum, so "= 0" // cannot mean what the operator wrote and is reported, not swallowed. The two keys where // 0 is a documented value (post_ingest_threads = auto, seeded by the migrator; // connection_lifetime_s = no recycling) must stay silent and keep their provenance. ConfigResolver::ConfigSections sections; sections["tuning"] = {{"pool_ipc_min", "0"}, {"worker_poll_ms", "0"}, {"pool_ipc_max", "48"}, {"post_ingest_threads", "0"}, {"connection_lifetime_s", "0"}}; TuningConfig base; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, base); CHECK((TuneAdvisor::poolMinSizeIpc() == 1u)); CHECK((TuneAdvisor::workerPollMs() == 150u)); CHECK((TuneAdvisor::poolMaxSizeIpc() == 48u)); CHECK((TuneAdvisor::connectionLifetimeSeconds() == 0u)); CHECK((resolved.provenance.count("tuning.pool_ipc_min") == 0)); CHECK((resolved.provenance.count("tuning.worker_poll_ms") == 0)); CHECK((resolved.provenance.at("tuning.pool_ipc_max") == "config:tuning.pool_ipc_max")); CHECK((resolved.provenance.at("tuning.post_ingest_threads") == "config:tuning.post_ingest_threads")); CHECK((resolved.provenance.at("tuning.connection_lifetime_s") == "config:tuning.connection_lifetime_s")); sections["tuning"] = {}; (void)ConfigResolver::applyRuntimeTuning(sections, base); } TEST_CASE("ConfigResolver wires [tuning] keys for every setter that was env-only", "[daemon][components][config][tuning][catch2]") { ConfigResolver::ConfigSections sections; sections["tuning"] = {{"conn_slots_min", "300"}, {"conn_slots_max", "5000"}, {"conn_slots_step", "24"}, {"cpu_high_pct", "77.5"}, {"onnx_max_concurrent", "7"}, {"onnx_gliner_reserved", "2"}, {"onnx_embed_reserved", "3"}, {"onnx_reranker_reserved", "4"}, {"onnx_sessions_per_model", "5"}, {"model_evict_warning_threshold", "0.61"}, {"model_evict_critical_threshold", "0.72"}, {"model_evict_emergency_threshold", "0.93"}, {"indexing_workers_max", "9"}, {"store_document_channel_capacity", "1024"}, {"work_coordinator_threads", "11"}, {"embed_channel_capacity", "4096"}, {"connection_lifetime_s", "1234"}}; // Several defaults derive from the host's core count and can equal a sentinel below // (e.g. onnx_max_concurrent is 7 on a 48-thread host), so "reverted" means "back to the // value observed before the override", not "different from the sentinel". const auto defaults = [] { return std::make_tuple( TuneAdvisor::connectionSlotsMin(), TuneAdvisor::connectionSlotsMax(), TuneAdvisor::connectionSlotsScaleStep(), TuneAdvisor::cpuHighThresholdPercent(), TuneAdvisor::onnxMaxConcurrent(), TuneAdvisor::onnxGlinerReserved(), TuneAdvisor::onnxEmbedReserved(), TuneAdvisor::onnxRerankerReserved(), TuneAdvisor::onnxSessionsPerModel(false), TuneAdvisor::modelEvictWarningThreshold(), TuneAdvisor::maxIngestWorkers(), TuneAdvisor::storeDocumentChannelCapacity(), TuneAdvisor::workCoordinatorThreads(), TuneAdvisor::embedChannelCapacity(), TuneAdvisor::connectionLifetimeSeconds()); }; const auto before = defaults(); TuningConfig base; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, base); CHECK((TuneAdvisor::connectionSlotsMin() == 300u)); CHECK((TuneAdvisor::connectionSlotsMax() == 5000u)); CHECK((TuneAdvisor::connectionSlotsScaleStep() == 24u)); CHECK((TuneAdvisor::cpuHighThresholdPercent() == Catch::Approx(77.5))); CHECK((TuneAdvisor::onnxMaxConcurrent() == 7u)); CHECK((TuneAdvisor::onnxGlinerReserved() == 2u)); CHECK((TuneAdvisor::onnxEmbedReserved() == 3u)); CHECK((TuneAdvisor::onnxRerankerReserved() == 4u)); CHECK((TuneAdvisor::onnxSessionsPerModel(false) == 5u)); CHECK((TuneAdvisor::modelEvictWarningThreshold() == Catch::Approx(0.61))); CHECK((TuneAdvisor::modelEvictCriticalThreshold() == Catch::Approx(0.72))); CHECK((TuneAdvisor::modelEvictEmergencyThreshold() == Catch::Approx(0.93))); CHECK((TuneAdvisor::maxIngestWorkers() == 9u)); CHECK((TuneAdvisor::storeDocumentChannelCapacity() == 1024u)); CHECK((TuneAdvisor::workCoordinatorThreads() == 11u)); CHECK((TuneAdvisor::embedChannelCapacity() == 4096u)); CHECK((TuneAdvisor::connectionLifetimeSeconds() == 1234u)); CHECK((resolved.provenance.at("tuning.conn_slots_min") == "config:tuning.conn_slots_min")); CHECK((resolved.provenance.at("tuning.cpu_high_pct") == "config:tuning.cpu_high_pct")); // A reload that drops the keys reverts every one of them. sections["tuning"] = {}; (void)ConfigResolver::applyRuntimeTuning(sections, base); CHECK((defaults() == before)); } TEST_CASE("Typed post-ingest configuration outranks the compatibility environment", "[daemon][components][config][tuning][precedence][catch2]") { EnvGuard compatibility{"YAMS_POST_INGEST_TOTAL_CONCURRENT", "3"}; TuneAdvisor::setPostIngestTotalConcurrent(0); ConfigResolver::ConfigSections sections; sections["tuning.post_ingest"] = {{"total_concurrent", "12"}}; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, {}); CHECK((TuneAdvisor::postIngestTotalConcurrent() == 12u)); CHECK((resolved.provenance.at("tuning.post_ingest.total_concurrent") == "config:tuning.post_ingest.total_concurrent")); TuneAdvisor::setPostIngestTotalConcurrent(0); } TEST_CASE("ConfigResolver reads a legacy embeddings.batch_size silently", "[daemon][config][embeddings][catch2]") { ConfigResolverFixture fx; // Older configs carry batch_size under [embeddings]; only [embeddings.runtime] is // typed. The legacy spelling resolves without a warning when the runtime key is absent // and yields to the runtime key when both are present. const auto legacyOnly = fx.writeToml("legacy_batch.toml", R"toml( [embeddings] batch_size = 24 )toml"); DaemonConfig legacyConfig; legacyConfig.configFilePath = legacyOnly; const auto legacy = ConfigResolver::resolveEmbeddingConfig(legacyConfig, fx.tempDir); REQUIRE(legacy.runtime.batchSize.has_value()); CHECK((*legacy.runtime.batchSize == 24)); CHECK(legacy.warnings.empty()); const auto both = fx.writeToml("both_batch.toml", R"toml( [embeddings] batch_size = 24 [embeddings.runtime] batch_size = 8 )toml"); DaemonConfig bothConfig; bothConfig.configFilePath = both; const auto resolved = ConfigResolver::resolveEmbeddingConfig(bothConfig, fx.tempDir); REQUIRE(resolved.runtime.batchSize.has_value()); CHECK((*resolved.runtime.batchSize == 8)); } TEST_CASE("ConfigResolver applies typed search maintenance policy with provenance", "[daemon][components][config][search][catch2]") { ConfigResolver::ConfigSections sections; sections["search"] = {{"automatic_rebuilds", "false"}}; DaemonConfig config; ConfigResolver::applySearchMaintenance(sections, config); REQUIRE(config.searchMaintenance.automaticRebuildsEnabled.has_value()); CHECK_FALSE(*config.searchMaintenance.automaticRebuildsEnabled); CHECK((config.searchMaintenance.automaticRebuildsSource == "config:search.automatic_rebuilds")); sections["search"]["automatic_rebuilds"] = "invalid"; DaemonConfig invalid; ConfigResolver::applySearchMaintenance(sections, invalid); CHECK_FALSE(invalid.searchMaintenance.automaticRebuildsEnabled.has_value()); CHECK(invalid.searchMaintenance.automaticRebuildsSource.empty()); } TEST_CASE("ConfigResolver applies typed disk pressure policy transactionally", "[daemon][components][config][disk-pressure][catch2]") { ConfigResolver::ConfigSections sections; sections["storage.disk_pressure"] = { {"warning_free_percent", "12.5"}, {"minimum_write_admission_bytes", "536870912"}, {"emergency_reserve_bytes", "67108864"}, }; DaemonConfig config; REQUIRE(ConfigResolver::applyStorageDiskPressure(sections, config)); CHECK(config.diskPressure.warningFreePercent == Catch::Approx(12.5)); CHECK(config.diskPressure.minimumWriteAdmissionBytes == 536870912ULL); CHECK(config.diskPressure.emergencyReserveBytes == 67108864ULL); const auto accepted = config.diskPressure; sections["storage.disk_pressure"]["emergency_reserve_bytes"] = "1073741824"; CHECK_FALSE(ConfigResolver::applyStorageDiskPressure(sections, config)); CHECK(config.diskPressure.warningFreePercent == accepted.warningFreePercent); CHECK(config.diskPressure.minimumWriteAdmissionBytes == accepted.minimumWriteAdmissionBytes); CHECK(config.diskPressure.emergencyReserveBytes == accepted.emergencyReserveBytes); } TEST_CASE("ConfigResolver applies typed daemon maintenance policy", "[daemon][components][config][maintenance][catch2]") { DaemonConfig defaults; CHECK(defaults.maintenance.vectorVacuumInterval == std::chrono::hours{24}); ConfigResolver::ConfigSections sections; sections["daemon.maintenance"] = {{"vector_vacuum_interval_hours", "6"}}; DaemonConfig config; ConfigResolver::applyDaemonMaintenance(sections, config); CHECK(config.maintenance.vectorVacuumInterval == std::chrono::hours{6}); sections["daemon.maintenance"] = {{"vector_vacuum_interval_hours", "0"}}; ConfigResolver::applyDaemonMaintenance(sections, config); CHECK(config.maintenance.vectorVacuumInterval.count() == 0); DaemonConfig invalid; sections["daemon.maintenance"] = {{"vector_vacuum_interval_hours", "-3"}}; ConfigResolver::applyDaemonMaintenance(sections, invalid); CHECK(invalid.maintenance.vectorVacuumInterval == std::chrono::hours{24}); CHECK(defaults.maintenance.sessionExpiry == std::chrono::days{30}); sections["daemon.maintenance"] = {{"session_expiry_days", "7"}}; ConfigResolver::applyDaemonMaintenance(sections, config); CHECK(config.maintenance.sessionExpiry == std::chrono::days{7}); sections["daemon.maintenance"] = {{"session_expiry_days", "soon"}}; ConfigResolver::applyDaemonMaintenance(sections, config); CHECK(config.maintenance.sessionExpiry == std::chrono::days{7}); } TEST_CASE("ConfigResolver disk pressure policy rejects unsafe values", "[daemon][components][config][disk-pressure][catch2]") { const std::vector> invalidValues = { {"warning_free_percent", "0"}, {"warning_free_percent", "101"}, {"warning_free_percent", "nan"}, {"warning_free_percent", "0.001"}, {"minimum_write_admission_bytes", "-1"}, {"emergency_reserve_bytes", "invalid"}, }; for (const auto& [key, value] : invalidValues) { CAPTURE(key, value); ConfigResolver::ConfigSections sections; sections["storage.disk_pressure"] = {{key, value}}; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyStorageDiskPressure(sections, config)); } ConfigResolver::ConfigSections unknown; unknown["storage.disk_pressure"] = {{"warning_free_precent", "5"}}; DaemonConfig typo; CHECK_FALSE(ConfigResolver::applyStorageDiskPressure(unknown, typo)); // storage.disk_pressure is applied independently (rejected above); applyMemorySync // is scoped to the memory_sync section only, so a section set without memory_sync // entries is a no-op. CHECK(ConfigResolver::applyMemorySync(unknown, typo)); DaemonConfig defaults; REQUIRE(ConfigResolver::applyStorageDiskPressure({}, defaults)); CHECK(defaults.diskPressure.warningFreePercent == Catch::Approx(10.0)); CHECK(defaults.diskPressure.minimumWriteAdmissionBytes == 256ULL * 1024ULL * 1024ULL); CHECK(defaults.diskPressure.emergencyReserveBytes == 100ULL * 1024ULL * 1024ULL); } TEST_CASE("ConfigResolver::resolveEmbeddingChunkingPolicy defaults are embedding-safe", "[daemon][components][config][chunking][catch2]") { // Ensure config file discovery doesn't accidentally pick up a user config during local dev. EnvGuard cfg("YAMS_CONFIG_PATH", ""); auto policy = ConfigResolver::resolveEmbeddingChunkingPolicy(); CHECK((policy.strategy == yams::vector::ChunkingStrategy::PARAGRAPH_BASED)); CHECK_FALSE(policy.config.preserve_sentences); CHECK_FALSE(policy.config.use_token_count); } TEST_CASE("ConfigResolver::resolveEmbeddingChunkingPolicy supports all embed strategies", "[daemon][components][config][chunking][catch2]") { struct Case { const char* value; yams::vector::ChunkingStrategy expected; }; const std::vector cases = { {"fixed", yams::vector::ChunkingStrategy::FIXED_SIZE}, {"sentence", yams::vector::ChunkingStrategy::SENTENCE_BASED}, {"paragraph", yams::vector::ChunkingStrategy::PARAGRAPH_BASED}, {"recursive", yams::vector::ChunkingStrategy::RECURSIVE}, {"sliding_window", yams::vector::ChunkingStrategy::SLIDING_WINDOW}, {"markdown", yams::vector::ChunkingStrategy::MARKDOWN_AWARE}, }; const std::string sample = "# Title\n\n" "Intro paragraph with enough text to chunk reasonably. " "Second sentence here. Third sentence here.\n\n" "## Section\n" "- bullet one\n- bullet two\n\n" "Final paragraph."; for (const auto& tc : cases) { DYNAMIC_SECTION("strategy=" << tc.value) { EnvGuard g("YAMS_EMBED_CHUNK_STRATEGY", tc.value); auto policy = ConfigResolver::resolveEmbeddingChunkingPolicy(); CHECK((policy.strategy == tc.expected)); auto chunker = yams::vector::createChunker(policy.strategy, policy.config, nullptr); REQUIRE(chunker); auto chunks = chunker->chunkDocument(sample, "hash"); // Some strategies may return empty and rely on caller fallback, but for this // representative input we expect at least one chunk. REQUIRE_FALSE(chunks.empty()); for (const auto& c : chunks) { CHECK((c.strategy_used == tc.expected)); CHECK_FALSE(c.content.empty()); } } } } TEST_CASE("ConfigResolver::resolveEmbeddingChunkingPolicy reads from config file", "[daemon][components][config][chunking][catch2]") { ConfigResolverFixture fx; auto configPath = fx.writeToml("config.toml", R"TOML( [embeddings.chunking] strategy = "fixed" target = 256 min = 64 max = 512 overlap = 0 use_tokens = 0 preserve_sentences = 0 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveEmbeddingChunkingPolicy(); CHECK((policy.strategy == yams::vector::ChunkingStrategy::FIXED_SIZE)); CHECK((policy.config.target_chunk_size == 256)); CHECK((policy.config.min_chunk_size == 64)); CHECK((policy.config.max_chunk_size == 512)); CHECK((policy.config.overlap_size == 0)); CHECK((policy.config.overlap_percentage == 0.0)); CHECK_FALSE(policy.config.use_token_count); CHECK_FALSE(policy.config.preserve_sentences); } TEST_CASE("ConfigResolver::envTruthy correctly parses truthy values", "[daemon][components][config][catch2]") { SECTION("truthy values") { CHECK(ConfigResolver::envTruthy("1")); CHECK(ConfigResolver::envTruthy("true")); CHECK(ConfigResolver::envTruthy("TRUE")); CHECK(ConfigResolver::envTruthy("True")); CHECK(ConfigResolver::envTruthy("yes")); CHECK(ConfigResolver::envTruthy("YES")); CHECK(ConfigResolver::envTruthy("Yes")); CHECK(ConfigResolver::envTruthy("on")); CHECK(ConfigResolver::envTruthy("ON")); CHECK(ConfigResolver::envTruthy("On")); } SECTION("falsy values") { CHECK_FALSE(ConfigResolver::envTruthy("0")); CHECK_FALSE(ConfigResolver::envTruthy("false")); CHECK_FALSE(ConfigResolver::envTruthy("FALSE")); CHECK_FALSE(ConfigResolver::envTruthy("no")); CHECK_FALSE(ConfigResolver::envTruthy("NO")); CHECK_FALSE(ConfigResolver::envTruthy("off")); CHECK_FALSE(ConfigResolver::envTruthy("OFF")); CHECK_FALSE(ConfigResolver::envTruthy("")); CHECK_FALSE(ConfigResolver::envTruthy(nullptr)); } SECTION("unrecognized values are rejected") { CHECK_FALSE(ConfigResolver::envTruthy("garbage")); CHECK_FALSE(ConfigResolver::envTruthy("maybe")); CHECK_FALSE(ConfigResolver::envTruthy("123")); CHECK_FALSE(ConfigResolver::envTruthy("yep")); CHECK_FALSE(ConfigResolver::envTruthy("nope")); } } TEST_CASE("ConfigResolver resolves plugin strict mode once from typed config and environment", "[daemon][components][config][plugin][catch2]") { SECTION("typed config is used without an environment override") { EnvGuard strictEnv("YAMS_PLUGIN_DIR_STRICT", ""); CHECK(ConfigResolver::resolvePluginDirStrict(true)); } SECTION("environment can enable strict mode") { EnvGuard strictEnv("YAMS_PLUGIN_DIR_STRICT", "on"); CHECK(ConfigResolver::resolvePluginDirStrict(false)); } SECTION("environment can disable strict mode") { EnvGuard strictEnv("YAMS_PLUGIN_DIR_STRICT", "off"); CHECK_FALSE(ConfigResolver::resolvePluginDirStrict(true)); } SECTION("invalid environment values preserve the typed default") { EnvGuard strictEnv("YAMS_PLUGIN_DIR_STRICT", "tru"); CHECK(ConfigResolver::resolvePluginDirStrict(true)); CHECK_FALSE(ConfigResolver::resolvePluginDirStrict(false)); } } TEST_CASE_METHOD(ConfigResolverFixture, "shared flat TOML parser handles daemon policy files", "[daemon][components][config][catch2]") { auto configPath = writeToml("shared_semantics.toml", R"( top_level = "root" # inline comment [search.path_tree] enable = true mode = "preferred" quoted_hash = "# not a comment" [daemon] socket_path = "/tmp/yams.sock" )"); auto config = yams::config::parse_simple_toml(configPath); CHECK((config["top_level"] == "root")); CHECK((config["search.path_tree.enable"] == "true")); CHECK((config["search.path_tree.mode"] == "preferred")); CHECK((config["search.path_tree.quoted_hash"] == "# not a comment")); CHECK((config["daemon.socket_path"] == "/tmp/yams.sock")); CHECK(yams::config::parse_simple_toml(tempDir / "nonexistent.toml").empty()); } TEST_CASE_METHOD(ConfigResolverFixture, "installed ConfigResolver compatibility lookups remain", "[daemon][components][config][compatibility][catch2]") { const auto configPath = writeToml("compatibility.toml", R"toml( [search] reranker_model = "compat-reranker" [plugins.symbol_extraction] enable = false [tuning.post_ingest] total_concurrent = 12 embed_concurrent = 4 batch_size = 32 )toml"); EnvGuard configEnvironment{"YAMS_CONFIG_PATH", configPath.string()}; EnvGuard rerankerEnvironment{"YAMS_RERANKER_MODEL", std::nullopt}; const auto values = ConfigResolver::parseSimpleTomlFlat(configPath); CHECK((values.at("search.reranker_model") == "compat-reranker")); DaemonConfig daemonConfig; daemonConfig.configFilePath = configPath; CHECK((ConfigResolver::resolveRerankerModel(daemonConfig) == "compat-reranker")); const auto caps = ConfigResolver::resolvePostIngestCaps(); REQUIRE(caps.totalConcurrent.has_value()); CHECK((*caps.totalConcurrent == 12U)); REQUIRE(caps.embedConcurrent.has_value()); CHECK((*caps.embedConcurrent == 4U)); REQUIRE(caps.batchSize.has_value()); CHECK((*caps.batchSize == 32U)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver resolveDefaultConfigPath searches standard paths", "[daemon][components][config][catch2]") { SECTION("YAMS_CONFIG_PATH env var takes precedence") { auto configPath = writeToml("custom.toml", "[daemon]\nlog_level = \"trace\"\n"); EnvGuard guard("YAMS_CONFIG_PATH", configPath.string()); auto resolved = ConfigResolver::resolveDefaultConfigPath(); CHECK((resolved == configPath)); } SECTION("returns empty path if no config found") { // Clear env var to test fallback EnvGuard guard("YAMS_CONFIG_PATH", ""); // Note: this test may find system config, so we just check it returns a valid result auto resolved = ConfigResolver::resolveDefaultConfigPath(); // Either found or empty - both are valid outcomes CHECK((resolved.empty() || std::filesystem::exists(resolved) || !resolved.empty())); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver resolveEmbeddingBackend defaults to simeon for new installs", "[daemon][components][config][catch2]") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", ""); auto configPath = writeToml("embedding_backend_default.toml", R"( [core] data_dir = "~/.yams/data" )"); EnvGuard configGuard("YAMS_CONFIG_PATH", configPath.string()); CHECK((ConfigResolver::resolveEmbeddingBackend() == "simeon")); CHECK((ConfigResolver::resolveEmbeddingBackend("auto") == "auto")); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver resolveEmbeddingBackend reads and normalizes configured backend", "[daemon][components][config][catch2]") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", ""); SECTION("reads daemon from config") { auto configPath = writeToml("embedding_backend_daemon.toml", R"( [embeddings] backend = "daemon" )"); EnvGuard configGuard("YAMS_CONFIG_PATH", configPath.string()); CHECK((ConfigResolver::resolveEmbeddingBackend() == "daemon")); } SECTION("normalizes uppercase simeon from config") { auto configPath = writeToml("embedding_backend_simeon_upper.toml", R"( [embeddings] backend = "SIMEON" )"); EnvGuard configGuard("YAMS_CONFIG_PATH", configPath.string()); CHECK((ConfigResolver::resolveEmbeddingBackend() == "simeon")); } SECTION("maps legacy onnx backend names to onnxruntime") { auto configPath = writeToml("embedding_backend_legacy_onnx.toml", R"( [embeddings] backend = "local_onnx" )"); EnvGuard configGuard("YAMS_CONFIG_PATH", configPath.string()); CHECK((ConfigResolver::resolveEmbeddingBackend() == "onnxruntime")); } SECTION("normalizes explicit onnxruntime from config") { auto configPath = writeToml("embedding_backend_onnxruntime.toml", R"( [embeddings] backend = "ONNX-RUNTIME" )"); EnvGuard configGuard("YAMS_CONFIG_PATH", configPath.string()); CHECK((ConfigResolver::resolveEmbeddingBackend() == "onnxruntime")); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver resolveEmbeddingBackend maps ONNX env aliases to onnxruntime", "[daemon][components][config][catch2]") { SECTION("maps onnx env alias") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", "onnx"); CHECK((ConfigResolver::resolveEmbeddingBackend() == "onnxruntime")); } SECTION("maps local_onnx env alias") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", "local_onnx"); CHECK((ConfigResolver::resolveEmbeddingBackend() == "onnxruntime")); } SECTION("maps ort env alias") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", "ort"); CHECK((ConfigResolver::resolveEmbeddingBackend() == "onnxruntime")); } SECTION("keeps daemon env selection distinct") { EnvGuard backendGuard("YAMS_EMBED_BACKEND", "daemon"); CHECK((ConfigResolver::resolveEmbeddingBackend() == "daemon")); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver resolves one effective embedding snapshot from explicit config", "[daemon][components][config][embeddings][snapshot][catch2]") { auto environment = unsetEnvironment({"YAMS_EMBED_BACKEND", "YAMS_PREFERRED_MODEL", "YAMS_EMBED_PRELOAD_ON_STARTUP", "YAMS_EMBED_BATCH", "YAMS_EMBED_BATCH_TARGET", "YAMS_REPAIR_LOCK_TIMEOUT_MS", "YAMS_EMBED_DIM"}); const auto ambientPath = writeToml("ambient.toml", R"toml( [embeddings] backend = "simeon" preferred_model = "ambient-model" )toml"); EnvGuard ambientConfig{"YAMS_CONFIG_PATH", ambientPath.string()}; const auto explicitPath = writeToml("explicit.toml", R"toml( [embeddings] backend = "ONNX" preferred_model = "canonical-model" preload_on_startup = true embedding_dim = 768 [embeddings.runtime] backend = "simeon" preferred_model = "legacy-model" batch_size = 24 batch_target = 2048 repair_lock_timeout_ms = 4321 [daemon.models] preload_models = ["preload-model"] )toml"); DaemonConfig config; config.configFilePath = explicitPath; const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, tempDir); CHECK((resolved.backend == "onnxruntime")); CHECK((resolved.preferredModel == "canonical-model")); CHECK_FALSE(resolved.isTrainingFree); CHECK(resolved.preloadOnStartup); REQUIRE(resolved.dimension.has_value()); CHECK((*resolved.dimension == 768U)); CHECK((resolved.dimensionSource == EmbeddingDimensionSource::Config)); CHECK((resolved.effectiveConfigPath == explicitPath)); CHECK((resolved.provenance.at("backend") == "config:embeddings.backend")); CHECK((resolved.provenance.at("preferred_model") == "config:embeddings.preferred_model")); CHECK((resolved.provenance.at("dimension") == "config:embeddings.embedding_dim")); CHECK((resolved.policyIdentity == "onnxruntime:canonical-model:768")); REQUIRE(resolved.runtime.backend.has_value()); CHECK((*resolved.runtime.backend == "onnxruntime")); REQUIRE(resolved.runtime.preferredModel.has_value()); CHECK((*resolved.runtime.preferredModel == "canonical-model")); CHECK((resolved.runtime.batchSize == std::optional{24U})); CHECK((resolved.runtime.batchTarget == std::optional{2048U})); CHECK((resolved.runtime.repairLockTimeoutMs == std::optional{4321U})); CHECK((resolved.warnings.size() >= 2U)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver embedding snapshot applies compatibility env overlays once", "[daemon][components][config][embeddings][snapshot][catch2]") { const auto configPath = writeToml("config.toml", R"toml( [embeddings] backend = "onnxruntime" preferred_model = "configured-model" preload_on_startup = true embedding_dim = 384 [embeddings.runtime] batch_size = 32 batch_target = 4096 repair_lock_timeout_ms = 60000 )toml"); EnvGuard configEnvironment{"YAMS_CONFIG_PATH", configPath.string()}; EnvGuard backendEnvironment{"YAMS_EMBED_BACKEND", "SIMEON"}; EnvGuard modelEnvironment{"YAMS_PREFERRED_MODEL", "configured-model"}; EnvGuard preloadEnvironment{"YAMS_EMBED_PRELOAD_ON_STARTUP", "false"}; EnvGuard batchEnvironment{"YAMS_EMBED_BATCH", "7"}; EnvGuard targetEnvironment{"YAMS_EMBED_BATCH_TARGET", "99"}; EnvGuard lockEnvironment{"YAMS_REPAIR_LOCK_TIMEOUT_MS", "1234"}; EnvGuard dimensionEnvironment{"YAMS_EMBED_DIM", "1024"}; DaemonConfig config; config.configFilePath = configPath; const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, tempDir); CHECK((resolved.backend == "simeon")); CHECK((resolved.preferredModel == "simeon-default")); CHECK(resolved.isTrainingFree); CHECK_FALSE(resolved.preloadOnStartup); CHECK((resolved.dimension == std::optional{384U})); CHECK((resolved.dimensionSource == EmbeddingDimensionSource::Config)); CHECK((resolved.provenance.at("backend") == "environment:YAMS_EMBED_BACKEND")); CHECK((resolved.provenance.at("preferred_model") == "normalization:simeon-backend(environment:YAMS_PREFERRED_MODEL)")); CHECK((resolved.provenance.at("preload") == "environment:YAMS_EMBED_PRELOAD_ON_STARTUP")); CHECK((resolved.runtime.batchSize == std::optional{7U})); CHECK((resolved.runtime.batchTarget == std::optional{99U})); CHECK((resolved.runtime.repairLockTimeoutMs == std::optional{1234U})); CHECK((resolved.policyIdentity == "simeon:simeon-default:384")); const auto compatibilityPolicy = ConfigResolver::resolveEmbeddingRuntimePolicy(); CHECK((compatibilityPolicy.backend == std::optional{"simeon"})); CHECK((compatibilityPolicy.preferredModel == std::optional{"simeon-default"})); CHECK((compatibilityPolicy.batchSize == resolved.runtime.batchSize)); CHECK((compatibilityPolicy.batchTarget == resolved.runtime.batchTarget)); CHECK((compatibilityPolicy.repairLockTimeoutMs == resolved.runtime.repairLockTimeoutMs)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver normalizes model identity before deriving dimension", "[daemon][components][config][embeddings][snapshot][catch2]") { auto environment = unsetEnvironment({"YAMS_EMBED_BACKEND", "YAMS_PREFERRED_MODEL", "YAMS_EMBED_DIM"}); const auto configPath = writeToml("normalized-model.toml", R"toml( [embeddings] backend = "simeon" preferred_model = "all-MiniLM-L6-v2" )toml"); DaemonConfig config; config.configFilePath = configPath; const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, {}); CHECK((resolved.preferredModel == "simeon-default")); CHECK_FALSE(resolved.dimension.has_value()); CHECK((resolved.dimensionSource == EmbeddingDimensionSource::Unresolved)); CHECK((resolved.provenance.at("preferred_model") == "normalization:simeon-backend(config:embeddings.preferred_model)")); CHECK((resolved.policyIdentity == "simeon:simeon-default:unresolved")); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver rejects a simeon sentinel for an ONNX backend", "[daemon][components][config][embeddings][snapshot][catch2]") { auto environment = unsetEnvironment({"YAMS_EMBED_BACKEND", "YAMS_PREFERRED_MODEL"}); const auto configPath = writeToml("onnx-sentinel.toml", R"toml( [embeddings] backend = "onnxruntime" preferred_model = "simeon-default" )toml"); const auto modelDir = tempDir / "models" / "all-MiniLM-L6-v2"; std::filesystem::create_directories(modelDir); std::ofstream(modelDir / "model.onnx") << "fixture"; DaemonConfig config; config.configFilePath = configPath; const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, tempDir); CHECK((resolved.backend == "onnxruntime")); CHECK((resolved.preferredModel == "all-MiniLM-L6-v2")); CHECK((resolved.dimension == std::optional{384U})); CHECK((resolved.provenance.at("preferred_model") == "data_directory:models")); CHECK((resolved.policyIdentity == "onnxruntime:all-MiniLM-L6-v2:384")); CHECK_FALSE(resolved.warnings.empty()); } TEST_CASE("EmbeddingGenerator preserves a resolved backend against ambient changes", "[daemon][components][config][embeddings][snapshot][catch2]") { EnvGuard backendEnvironment{"YAMS_EMBED_BACKEND", "daemon"}; yams::vector::EmbeddingConfig config; config.backend = yams::vector::EmbeddingConfig::Backend::Simeon; config.backend_is_resolved = true; config.embedding_dim = 128; yams::vector::EmbeddingGenerator generator(config); REQUIRE(generator.initialize()); CHECK((generator.getBackendName() == "Simeon")); CHECK((generator.getEmbeddingDimension() == 128U)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver embedding snapshot records persisted dimension authority", "[daemon][components][config][embeddings][snapshot][catch2]") { auto environment = unsetEnvironment({"YAMS_EMBED_BACKEND", "YAMS_PREFERRED_MODEL", "YAMS_EMBED_PRELOAD_ON_STARTUP", "YAMS_EMBED_BATCH", "YAMS_EMBED_BATCH_TARGET", "YAMS_REPAIR_LOCK_TIMEOUT_MS", "YAMS_EMBED_DIM"}); const auto configPath = writeToml("config.toml", R"toml( [embeddings] backend = "simeon" embedding_dim = 384 )toml"); ConfigResolver::writeVectorSentinel(tempDir, 1024, "embeddings", 1); DaemonConfig config; config.configFilePath = configPath; const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, tempDir); CHECK((resolved.dimension == std::optional{1024U})); CHECK((resolved.dimensionSource == EmbeddingDimensionSource::Sentinel)); CHECK((resolved.provenance.at("dimension") == "sentinel:vectors_sentinel.json")); CHECK((resolved.policyIdentity == "simeon:simeon-default:1024")); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyTunerPolicy defaults are empty when missing", "[daemon][components][config][topology_tuner][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [daemon] socket_path = "/tmp/test.sock" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyTunerPolicy(); CHECK_FALSE(policy.enabled.has_value()); CHECK_FALSE(policy.cooldownMinutes.has_value()); CHECK_FALSE(policy.docCountDelta.has_value()); CHECK_FALSE(policy.rewardAlphaSingleton.has_value()); CHECK_FALSE(policy.rewardBetaGiantCluster.has_value()); CHECK_FALSE(policy.rewardGammaGiniDeviation.has_value()); CHECK_FALSE(policy.rewardDeltaIntraEdge.has_value()); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyEnginePolicy reads routing representatives", "[daemon][components][config][topology][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [topology] engine = "connected" routing_representatives = 4 sgc_hops = 2 sgc_normalize = false boundary_spill = true boundary_spill_limit = 1 boundary_spill_distance_ratio = +1.2 boundary_spill_residual_penalty = 1.5e0 road_max_per_cluster = 3 road_order = "session" [topology.features] entity_fusion = true entity_signature_k = 12 entity_fusion_alpha = 0.2 entity_min_confidence = 0.5 matryoshka_coarse_view = true matryoshka_target_dim = 256 minhash_sketch = true minhash_sketch_dim = 24 minhash_alpha = 0.15 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); REQUIRE(policy.engine.has_value()); CHECK((*policy.engine == "connected")); REQUIRE(policy.routingRepresentativeCount.has_value()); CHECK((*policy.routingRepresentativeCount == 4)); REQUIRE(policy.sgcHops.has_value()); CHECK((*policy.sgcHops == 2)); REQUIRE(policy.sgcNormalize.has_value()); CHECK_FALSE(*policy.sgcNormalize); REQUIRE(policy.boundarySpillEnabled.has_value()); CHECK(*policy.boundarySpillEnabled); REQUIRE(policy.boundarySpillLimit.has_value()); CHECK((*policy.boundarySpillLimit == 1)); REQUIRE(policy.boundarySpillDistanceRatio.has_value()); CHECK((*policy.boundarySpillDistanceRatio == Catch::Approx(1.2))); REQUIRE(policy.boundarySpillResidualPenalty.has_value()); CHECK((*policy.boundarySpillResidualPenalty == Catch::Approx(1.5))); REQUIRE(policy.roadMaxPerCluster.has_value()); CHECK((*policy.roadMaxPerCluster == 3)); REQUIRE(policy.roadOrder.has_value()); CHECK((*policy.roadOrder == yams::topology::RoadOrderKind::Session)); CHECK(*policy.featureEntityFusion); CHECK((*policy.featureEntitySignatureK == 12)); CHECK((*policy.featureEntityFusionAlpha == Catch::Approx(0.2F))); CHECK((*policy.featureEntityMinConfidence == Catch::Approx(0.5F))); CHECK(*policy.featureMatryoshkaCoarseView); CHECK((*policy.featureMatryoshkaTargetDim == 256)); CHECK(*policy.featureMinHashSketch); CHECK((*policy.featureMinHashSketchDim == 24)); CHECK((*policy.featureMinHashAlpha == Catch::Approx(0.15F))); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyEnginePolicy reads the dirty-region expansion", "[daemon][components][config][topology][catch2]") { // TopologyBuildConfig::dirtyRegionExpansion had no config path, so incremental rebuilds // always used the default and NeighborsOnly / Adaptive were unreachable. SECTION("a recognized mode is resolved") { auto configPath = writeToml("config.toml", R"TOML( [topology] dirty_region_expansion = "neighbors_only" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); REQUIRE(policy.dirtyRegionExpansion.has_value()); CHECK((*policy.dirtyRegionExpansion == yams::topology::DirtyRegionExpansionMode::NeighborsOnly)); } SECTION("an unrecognized mode is ignored, not guessed") { auto configPath = writeToml("config.toml", R"TOML( [topology] dirty_region_expansion = "sideways" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); CHECK_FALSE(policy.dirtyRegionExpansion.has_value()); } SECTION("absent means the engine default") { auto configPath = writeToml("config.toml", R"TOML( [topology] engine = "connected" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); CHECK_FALSE(policy.dirtyRegionExpansion.has_value()); // Roads and the road order stay off unless the keys are present. CHECK_FALSE(policy.roadMaxPerCluster.has_value()); CHECK_FALSE(policy.roadOrder.has_value()); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyEnginePolicy reads the road order", "[daemon][components][config][topology][roads][catch2]") { SECTION("every spelling resolves") { for (const auto [spelling, expected] : {std::pair{"none", yams::topology::RoadOrderKind::None}, std::pair{"time", yams::topology::RoadOrderKind::Time}, std::pair{"Session", yams::topology::RoadOrderKind::Session}, std::pair{"similarity_chain", yams::topology::RoadOrderKind::SimilarityChain}}) { auto configPath = writeToml("config.toml", std::string("[topology]\nroad_order = \"") + spelling + "\"\n"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); REQUIRE(policy.roadOrder.has_value()); CHECK((*policy.roadOrder == expected)); } } SECTION("an unrecognized order is ignored, not guessed") { auto configPath = writeToml("config.toml", R"TOML( [topology] road_order = "alphabetical" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); CHECK_FALSE(policy.roadOrder.has_value()); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyRoutingPolicy reads populated TOML block", "[daemon][components][config][topology_routing][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] enable_weak_query_routing = true vector_policy = shadow min_clusters = 1 max_clusters = 3 max_seed_documents = 24 representative_limit = 2 ann_candidate_limit = 16 bq_candidate_limit = 12 bq_prefix_dim = 64 bq_rotation = fwht graph_community_source = topology_snapshot adaptive_probe_score_gap = 0.07 narrow_min_boundary_margin = 0.03 max_docs = 42 expansion_output_limit = 96 graph_weighted_seed_ranking = true medoid_boost = 0.2 route_calibration_fingerprint = "atlas-123" route_calibration_policy_fingerprint = "policy-456" route_calibration_dataset_identity = "held-out-split-v1" route_calibration_queries = 100 route_calibration_protected_candidates = 250 route_calibration_missed_protected_candidates = 2 route_min_calibration_queries = 75 route_max_misses_per_thousand = 10 route_calibration_min_boundary_margin = 0.2 route_calibration_min_seed_hits = 2 route_work_max_rows_visited = 64 route_work_max_exact_distance_evaluations = 32 route_work_max_ann_candidates = 48 rrf_k = 33 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(policy.enableWeakQueryRouting.has_value()); CHECK((*policy.enableWeakQueryRouting == true)); REQUIRE(policy.vectorPolicy.has_value()); CHECK((*policy.vectorPolicy == "shadow")); REQUIRE(policy.maxClusters.has_value()); CHECK((*policy.maxClusters == 3U)); REQUIRE(policy.minClusters.has_value()); CHECK((*policy.minClusters == 1U)); REQUIRE(policy.maxSeedDocuments.has_value()); CHECK((*policy.maxSeedDocuments == 24U)); REQUIRE(policy.representativeLimit.has_value()); CHECK((*policy.representativeLimit == 2U)); REQUIRE(policy.annCandidateLimit.has_value()); CHECK((*policy.annCandidateLimit == 16U)); REQUIRE(policy.bqCandidateLimit.has_value()); CHECK((*policy.bqCandidateLimit == 12U)); REQUIRE(policy.bqPrefixDimension.has_value()); CHECK((*policy.bqPrefixDimension == 64U)); REQUIRE(policy.bqRotation.has_value()); CHECK((*policy.bqRotation == "fwht")); REQUIRE(policy.graphCommunitySource.has_value()); CHECK((*policy.graphCommunitySource == "topology_snapshot")); REQUIRE(policy.adaptiveProbeScoreGap.has_value()); CHECK((*policy.adaptiveProbeScoreGap == Catch::Approx(0.07F))); REQUIRE(policy.narrowMinBoundaryMargin.has_value()); CHECK((*policy.narrowMinBoundaryMargin == Catch::Approx(0.03F))); REQUIRE(policy.maxDocs.has_value()); CHECK((*policy.maxDocs == 42U)); REQUIRE(policy.expansionOutputLimit.has_value()); CHECK((*policy.expansionOutputLimit == 96U)); REQUIRE(policy.graphWeightedSeedRanking.has_value()); CHECK(*policy.graphWeightedSeedRanking); REQUIRE(policy.medoidBoost.has_value()); CHECK((*policy.medoidBoost > 0.19f)); CHECK((*policy.medoidBoost < 0.21f)); CHECK((*policy.routeCalibrationFingerprint == "atlas-123")); CHECK(policy.routeCalibrationPolicyFingerprint == "policy-456"); CHECK(policy.routeCalibrationDatasetIdentity == "held-out-split-v1"); CHECK((*policy.routeCalibrationQueries == 100U)); CHECK((*policy.routeCalibrationProtectedCandidates == 250U)); CHECK((*policy.routeCalibrationMissedProtectedCandidates == 2U)); CHECK((*policy.routeMinCalibrationQueries == 75U)); CHECK((*policy.routeMaxMissesPerThousand == 10U)); CHECK((*policy.routeCalibrationMinBoundaryMargin == Catch::Approx(0.2F))); CHECK((*policy.routeCalibrationMinSeedHits == 2U)); CHECK((*policy.routeWorkMaxRowsVisited == 64U)); CHECK((*policy.routeWorkMaxExactDistanceEvaluations == 32U)); CHECK((*policy.routeWorkMaxAnnCandidates == 48U)); REQUIRE(policy.rrfK.has_value()); CHECK((*policy.rrfK == 33.0f)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyRoutingPolicy reads the RouteOnly evaluation gate", "[daemon][components][config][topology_routing][route-only][catch2]") { SECTION("both keys populated") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] vector_policy = "route_only" [search.evaluation] allow_uncertified_routes = true )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(policy.vectorPolicy.has_value()); CHECK((*policy.vectorPolicy == "route_only")); REQUIRE(policy.allowUncertifiedRouteOnly.has_value()); CHECK(*policy.allowUncertifiedRouteOnly); } SECTION("gate explicitly closed") { auto configPath = writeToml("config.toml", R"TOML( [search.evaluation] allow_uncertified_routes = false )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); CHECK_FALSE(policy.vectorPolicy.has_value()); REQUIRE(policy.allowUncertifiedRouteOnly.has_value()); CHECK_FALSE(*policy.allowUncertifiedRouteOnly); } SECTION("absent keys stay unset") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] max_clusters = 2 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); CHECK_FALSE(policy.vectorPolicy.has_value()); CHECK_FALSE(policy.allowUncertifiedRouteOnly.has_value()); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyRoutingPolicy reads the road beam keys", "[daemon][components][config][topology_routing][road-beam][catch2]") { SECTION("both keys populated") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] road_hops = 2 road_beam_width = 3 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(policy.roadHops.has_value()); CHECK((*policy.roadHops == 2U)); REQUIRE(policy.roadBeamWidth.has_value()); CHECK((*policy.roadBeamWidth == 3U)); } SECTION("absent keys stay unset") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] max_clusters = 2 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); CHECK_FALSE(policy.roadHops.has_value()); CHECK_FALSE(policy.roadBeamWidth.has_value()); CHECK_FALSE(policy.roadWalkWindow.has_value()); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyRoutingPolicy reads the road walk window", "[daemon][components][config][topology_routing][road-walk][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] road_walk_window = 4 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(policy.roadWalkWindow.has_value()); CHECK((*policy.roadWalkWindow == 4U)); // The walk window travels alone: the beam keys stay unset. CHECK_FALSE(policy.roadHops.has_value()); CHECK_FALSE(policy.roadBeamWidth.has_value()); } TEST_CASE("Generated config keeps topology-assisted hybrid search as the product default", "[config][search][topology][catch2]") { const auto defaults = yams::config::ConfigMigrator::getLatestConfigDefaults(); const auto topologyIt = defaults.find("search.topology"); REQUIRE((topologyIt != defaults.end())); const auto& topology = topologyIt->second; CHECK((topology.at("mode") == "hybrid_assist")); CHECK((topology.at("vector_policy") == "shadow")); CHECK((topology.at("graph_weighted_seed_ranking") == "false")); CHECK_FALSE(topology.contains("enable_weak_query_routing")); CHECK_FALSE(topology.contains("routing_variant")); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver reads topology evidence weight", "[daemon][components][config][topology_routing][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [search.topology] evidence_weight = 0.03 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto topology = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(topology.evidenceWeight.has_value()); CHECK((*topology.evidenceWeight == Catch::Approx(0.03F))); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyRoutingPolicy honors legacy env overlays", "[daemon][components][config][topology_routing][catch2]") { EnvGuard cfg("YAMS_CONFIG_PATH", ""); EnvGuard enable("YAMS_SEARCH_ENABLE_TOPOLOGY_WEAK_ROUTING", "1"); EnvGuard clusters("YAMS_SEARCH_TOPOLOGY_MAX_CLUSTERS", "5"); EnvGuard docs("YAMS_SEARCH_TOPOLOGY_MAX_DOCS", "17"); EnvGuard medoid("YAMS_SEARCH_TOPOLOGY_MEDOID_BOOST", "0.15"); auto policy = ConfigResolver::resolveTopologyRoutingPolicy(); REQUIRE(policy.enableWeakQueryRouting.has_value()); CHECK((*policy.enableWeakQueryRouting == true)); REQUIRE(policy.maxClusters.has_value()); CHECK((*policy.maxClusters == 5U)); REQUIRE(policy.maxDocs.has_value()); CHECK((*policy.maxDocs == 17U)); REQUIRE(policy.medoidBoost.has_value()); CHECK((*policy.medoidBoost > 0.14f)); CHECK((*policy.medoidBoost < 0.16f)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveEmbeddingSelectionPolicy reads semantic graph ingest flag", "[daemon][components][config][embeddings][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [embeddings.selection] update_semantic_graph_during_ingest = false )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveEmbeddingSelectionPolicy(); CHECK((policy.updateSemanticGraphDuringIngest == false)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyTunerPolicy reads populated TOML block", "[daemon][components][config][topology_tuner][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [topology.tuner] enabled = true cooldown_minutes = 30 doc_count_delta = 250 [topology.tuner.reward] alpha_singleton = 0.5 beta_giant_cluster = 0.3 gamma_gini_deviation = 0.15 delta_intra_edge = 0.05 )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyTunerPolicy(); REQUIRE(policy.enabled.has_value()); CHECK((*policy.enabled == true)); REQUIRE(policy.cooldownMinutes.has_value()); CHECK((*policy.cooldownMinutes == 30u)); REQUIRE(policy.docCountDelta.has_value()); CHECK((*policy.docCountDelta == 250u)); REQUIRE(policy.rewardAlphaSingleton.has_value()); CHECK((*policy.rewardAlphaSingleton == 0.5)); REQUIRE(policy.rewardBetaGiantCluster.has_value()); CHECK((*policy.rewardBetaGiantCluster == 0.3)); REQUIRE(policy.rewardGammaGiniDeviation.has_value()); CHECK((*policy.rewardGammaGiniDeviation == 0.15)); REQUIRE(policy.rewardDeltaIntraEdge.has_value()); CHECK((*policy.rewardDeltaIntraEdge == 0.05)); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyTunerPolicy honors disabled flag explicitly", "[daemon][components][config][topology_tuner][catch2]") { auto configPath = writeToml("config.toml", R"TOML( [topology.tuner] enabled = false )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyTunerPolicy(); REQUIRE(policy.enabled.has_value()); CHECK((*policy.enabled == false)); } TEST_CASE("ConfigResolver vector sentinel operations", "[daemon][components][config][catch2][sentinel]") { auto tempDir = std::filesystem::temp_directory_path() / ("yams_sentinel_test_" + std::to_string(std::chrono::steady_clock::now().time_since_epoch().count())); std::filesystem::create_directories(tempDir); SECTION("readVectorSentinelDim returns nullopt for missing file") { auto dim = ConfigResolver::readVectorSentinelDim(tempDir); CHECK_FALSE(dim.has_value()); } SECTION("writeVectorSentinel then readVectorSentinelDim round-trips") { ConfigResolver::writeVectorSentinel(tempDir, 384, "test_table", 1); auto dim = ConfigResolver::readVectorSentinelDim(tempDir); REQUIRE(dim.has_value()); CHECK((dim.value() == 384)); } SECTION("different dimensions are preserved") { for (size_t testDim : {128, 256, 384, 768, 1024}) { ConfigResolver::writeVectorSentinel(tempDir, testDim, "test", 1); auto dim = ConfigResolver::readVectorSentinelDim(tempDir); REQUIRE(dim.has_value()); CHECK((dim.value() == testDim)); } } std::error_code ec; std::filesystem::remove_all(tempDir, ec); } TEST_CASE_METHOD(ConfigResolverFixture, "shared flat TOML parser reads tuning sections", "[daemon][components][config][catch2]") { auto configPath = writeToml("multi_tuning.toml", R"( [tuning] profile = "aggressive" pool_cooldown_ms = 250 worker_poll_ms = 100 )"); auto config = yams::config::parse_simple_toml(configPath); CHECK((config["tuning.profile"] == "aggressive")); CHECK((config["tuning.pool_cooldown_ms"] == "250")); CHECK((config["tuning.worker_poll_ms"] == "100")); } TEST_CASE("Tuning profile from config affects TuneAdvisor methods", "[daemon][components][config][catch2]") { PostIngestStageActivityGuard stageActivityGuard; SECTION("efficient profile scales post-ingest concurrency down") { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Efficient); EnvGuard maxThreadsGuard("YAMS_MAX_THREADS", "0"); EnvGuard postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0"); yams::daemon::TuneAdvisor::setHardwareConcurrencyForTests(8); // Efficient profile scale is 0.0, old totalBudget = 2 // With active-stage floor: total = max(2, 6 active stages) = 6 // Each stage gets 1 slot; batching remains a cap and does not reserve work. CHECK((TuneAdvisor::postExtractionConcurrent() == 1u)); CHECK((TuneAdvisor::postKgConcurrent() == 1u)); CHECK((TuneAdvisor::postSymbolConcurrent() == 1u)); CHECK((TuneAdvisor::postEntityConcurrent() == 1u)); CHECK((TuneAdvisor::postTitleConcurrent() == 1u)); CHECK((TuneAdvisor::postEmbedConcurrent() == 1u)); CHECK((TuneAdvisor::postIngestBatchSize() == 32u)); } SECTION("balanced profile uses medium values") { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Balanced); EnvGuard maxThreadsGuard("YAMS_MAX_THREADS", "0"); EnvGuard postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0"); yams::daemon::TuneAdvisor::setHardwareConcurrencyForTests(8); // Balanced profile scale is 0.5, old totalBudget = 2 // With active-stage floor: total = max(2, 6 active stages) = 6 // Each stage gets 1 slot; batching remains a cap and does not reserve work. CHECK((TuneAdvisor::postExtractionConcurrent() == 1u)); CHECK((TuneAdvisor::postKgConcurrent() == 1u)); CHECK((TuneAdvisor::postSymbolConcurrent() == 1u)); CHECK((TuneAdvisor::postEntityConcurrent() == 1u)); CHECK((TuneAdvisor::postTitleConcurrent() == 1u)); CHECK((TuneAdvisor::postEmbedConcurrent() == 1u)); CHECK((TuneAdvisor::postIngestBatchSize() == 32u)); } SECTION("aggressive profile uses maximum values") { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Aggressive); EnvGuard maxThreadsGuard("YAMS_MAX_THREADS", "0"); EnvGuard postIngestGuard("YAMS_POST_INGEST_TOTAL_CONCURRENT", "0"); yams::daemon::TuneAdvisor::setHardwareConcurrencyForTests(8); // Aggressive profile scale is 1.0, old totalBudget = 3 // With active-stage floor: total = max(3, 6 active stages) = 6 // Each stage gets 1 slot; batching remains a cap and does not reserve work. CHECK((TuneAdvisor::postExtractionConcurrent() == 1u)); CHECK((TuneAdvisor::postKgConcurrent() == 1u)); CHECK((TuneAdvisor::postSymbolConcurrent() == 1u)); CHECK((TuneAdvisor::postEntityConcurrent() == 1u)); CHECK((TuneAdvisor::postTitleConcurrent() == 1u)); CHECK((TuneAdvisor::postEmbedConcurrent() == 1u)); CHECK((TuneAdvisor::postIngestBatchSize() == 32u)); } SECTION("profile affects cpuBudgetPercent") { { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Efficient); CHECK((TuneAdvisor::cpuBudgetPercent() == 40u)); } { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Balanced); CHECK((TuneAdvisor::cpuBudgetPercent() == 50u)); } { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Aggressive); // 40.0 + 1.0 * 20.0 = 60 CHECK((TuneAdvisor::cpuBudgetPercent() == 60u)); } } SECTION("profile affects poolCooldownMs") { { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Efficient); CHECK((TuneAdvisor::poolCooldownMs() == 750u)); } { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Balanced); CHECK((TuneAdvisor::poolCooldownMs() == 500u)); } { ProfileGuard guard(yams::daemon::TuneAdvisor::Profile::Aggressive); CHECK((TuneAdvisor::poolCooldownMs() == 250u)); } } } TEST_CASE("YAMS_TUNING_PROFILE env var overrides config", "[daemon][components][config][catch2]") { auto clearProfileOverride = []() { yams::daemon::TuneAdvisor::tuningProfileOverride_.store(0, std::memory_order_relaxed); }; SECTION("efficient profile from env var") { clearProfileOverride(); EnvGuard envGuard("YAMS_TUNING_PROFILE", "efficient"); auto profile = yams::daemon::TuneAdvisor::tuningProfile(); CHECK((profile == yams::daemon::TuneAdvisor::Profile::Efficient)); } SECTION("aggressive profile from env var") { clearProfileOverride(); EnvGuard envGuard("YAMS_TUNING_PROFILE", "aggressive"); auto profile = yams::daemon::TuneAdvisor::tuningProfile(); CHECK((profile == yams::daemon::TuneAdvisor::Profile::Aggressive)); } SECTION("conservative alias maps to efficient") { clearProfileOverride(); EnvGuard envGuard("YAMS_TUNING_PROFILE", "conservative"); auto profile = yams::daemon::TuneAdvisor::Profile::Efficient; CHECK((yams::daemon::TuneAdvisor::tuningProfile() == profile)); } SECTION("invalid env var falls back to balanced") { clearProfileOverride(); EnvGuard envGuard("YAMS_TUNING_PROFILE", "invalid_profile"); auto profile = yams::daemon::TuneAdvisor::tuningProfile(); CHECK((profile == yams::daemon::TuneAdvisor::Profile::Balanced)); } SECTION("empty env var falls back to balanced") { clearProfileOverride(); EnvGuard envGuard("YAMS_TUNING_PROFILE", ""); auto profile = yams::daemon::TuneAdvisor::tuningProfile(); CHECK((profile == yams::daemon::TuneAdvisor::Profile::Balanced)); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveSimeonEncoderPolicy reads [embeddings.simeon]", "[daemon][components][config][simeon][catch2]") { SECTION("missing section returns all nullopt") { auto configPath = writeToml("no_simeon.toml", R"( [daemon] log_level = "info" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto cleared = unsetEnvironment({"YAMS_SIMEON_NGRAM_MODE", "YAMS_SIMEON_NGRAM_MIN", "YAMS_SIMEON_NGRAM_MAX", "YAMS_SIMEON_SKETCH_DIM", "YAMS_SIMEON_OUTPUT_DIM", "YAMS_SIMEON_PROJECTION", "YAMS_SIMEON_PQ_BYTES"}); auto policy = ConfigResolver::resolveSimeonEncoderPolicy(); CHECK_FALSE(policy.ngramMode.has_value()); CHECK_FALSE(policy.ngramMin.has_value()); CHECK_FALSE(policy.ngramMax.has_value()); CHECK_FALSE(policy.sketchDim.has_value()); CHECK_FALSE(policy.outputDim.has_value()); CHECK_FALSE(policy.projection.has_value()); CHECK_FALSE(policy.l2Normalize.has_value()); CHECK_FALSE(policy.pqBytes.has_value()); } SECTION("populated TOML section fills every field") { auto configPath = writeToml("simeon_encoder.toml", R"( [embeddings.simeon] ngram_mode = "char_and_word" ngram_min = 3 ngram_max = 5 sketch_dim = 4096 output_dim = 384 projection = "fwht" l2_normalize = true pq_bytes = 64 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto cleared = unsetEnvironment({"YAMS_SIMEON_NGRAM_MODE", "YAMS_SIMEON_NGRAM_MIN", "YAMS_SIMEON_NGRAM_MAX", "YAMS_SIMEON_SKETCH_DIM", "YAMS_SIMEON_OUTPUT_DIM", "YAMS_SIMEON_PROJECTION", "YAMS_SIMEON_PQ_BYTES"}); auto policy = ConfigResolver::resolveSimeonEncoderPolicy(); REQUIRE(policy.ngramMode.has_value()); CHECK((*policy.ngramMode == "char_and_word")); REQUIRE(policy.ngramMin.has_value()); CHECK((*policy.ngramMin == 3u)); REQUIRE(policy.ngramMax.has_value()); CHECK((*policy.ngramMax == 5u)); REQUIRE(policy.sketchDim.has_value()); CHECK((*policy.sketchDim == 4096u)); REQUIRE(policy.outputDim.has_value()); CHECK((*policy.outputDim == 384u)); REQUIRE(policy.projection.has_value()); CHECK((*policy.projection == "fwht")); REQUIRE(policy.l2Normalize.has_value()); CHECK(*policy.l2Normalize); REQUIRE(policy.pqBytes.has_value()); CHECK((*policy.pqBytes == 64u)); } SECTION("env vars override TOML values") { auto configPath = writeToml("override_encoder.toml", R"( [embeddings.simeon] projection = "achlioptas_sparse" sketch_dim = 2048 output_dim = 256 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); EnvGuard proj("YAMS_SIMEON_PROJECTION", "fwht"); EnvGuard sketch("YAMS_SIMEON_SKETCH_DIM", "8192"); EnvGuard out("YAMS_SIMEON_OUTPUT_DIM", "1024"); auto policy = ConfigResolver::resolveSimeonEncoderPolicy(); REQUIRE(policy.projection.has_value()); CHECK((*policy.projection == "fwht")); REQUIRE(policy.sketchDim.has_value()); CHECK((*policy.sketchDim == 8192u)); REQUIRE(policy.outputDim.has_value()); CHECK((*policy.outputDim == 1024u)); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveSimeonBm25Policy reads [embeddings.simeon.bm25]", "[daemon][components][config][simeon][catch2]") { SECTION("missing section returns all nullopt") { auto configPath = writeToml("no_bm25.toml", R"( [daemon] log_level = "info" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto cleared = unsetEnvironment({"YAMS_SIMEON_BM25_ENABLED", "YAMS_SIMEON_BM25_VARIANT", "YAMS_SIMEON_BM25_SUBWORD_GAMMA", "YAMS_SIMEON_BM25_MAX_CORPUS_DOCS"}); auto policy = ConfigResolver::resolveSimeonBm25Policy(); CHECK_FALSE(policy.enabled.has_value()); CHECK_FALSE(policy.variant.has_value()); CHECK_FALSE(policy.subwordGamma.has_value()); CHECK_FALSE(policy.maxCorpusDocs.has_value()); } SECTION("populated TOML section fills every field") { auto configPath = writeToml("bm25.toml", R"( [embeddings.simeon.bm25] enabled = true variant = "sab_smooth" subword_gamma = 5.0 max_corpus_docs = 200000 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto cleared = unsetEnvironment({"YAMS_SIMEON_BM25_ENABLED", "YAMS_SIMEON_BM25_VARIANT", "YAMS_SIMEON_BM25_SUBWORD_GAMMA", "YAMS_SIMEON_BM25_MAX_CORPUS_DOCS"}); auto policy = ConfigResolver::resolveSimeonBm25Policy(); REQUIRE(policy.enabled.has_value()); CHECK(*policy.enabled); REQUIRE(policy.variant.has_value()); CHECK((*policy.variant == "sab_smooth")); REQUIRE(policy.subwordGamma.has_value()); CHECK((*policy.subwordGamma == 5.0f)); REQUIRE(policy.maxCorpusDocs.has_value()); CHECK((*policy.maxCorpusDocs == 200000u)); } SECTION("env vars override TOML values") { auto configPath = writeToml("override_bm25.toml", R"( [embeddings.simeon.bm25] variant = "atire" subword_gamma = 3.0 max_corpus_docs = 50000 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); EnvGuard variantEnv("YAMS_SIMEON_BM25_VARIANT", "sab_smooth"); EnvGuard gammaEnv("YAMS_SIMEON_BM25_SUBWORD_GAMMA", "7.5"); EnvGuard capEnv("YAMS_SIMEON_BM25_MAX_CORPUS_DOCS", "123456"); auto policy = ConfigResolver::resolveSimeonBm25Policy(); REQUIRE(policy.variant.has_value()); CHECK((*policy.variant == "sab_smooth")); REQUIRE(policy.subwordGamma.has_value()); CHECK((*policy.subwordGamma == 7.5f)); REQUIRE(policy.maxCorpusDocs.has_value()); CHECK((*policy.maxCorpusDocs == 123456u)); } SECTION("fragment geometry encoder profile is read from TOML") { auto configPath = writeToml("bm25_fragment_encoder.toml", R"( [embeddings.simeon.bm25.fragment_geometry] encoder_profile = "fixed_hash_384" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveSimeonBm25Policy(); REQUIRE(policy.fragmentGeometryEncoderProfile.has_value()); CHECK((*policy.fragmentGeometryEncoderProfile == "fixed_hash_384")); } SECTION("embedding encoder profile is read from TOML") { auto configPath = writeToml("simeon_encoder_profile.toml", R"( [embeddings.simeon] encoder_profile = "fixed_hash_384" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveSimeonEncoderPolicy(); REQUIRE(policy.encoderProfile.has_value()); CHECK((*policy.encoderProfile == "fixed_hash_384")); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveRerankerBackendPolicy reads [search]", "[daemon][components][config][reranker][catch2]") { SECTION("returns unset when not configured") { auto configPath = writeToml("reranker_default.toml", R"( [search] default_limit = 10 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveRerankerBackendPolicy(DaemonConfig{}); CHECK_FALSE(policy.backend.has_value()); } SECTION("reads explicit reranker backend") { auto configPath = writeToml("reranker_backend.toml", R"( [search] reranker_backend = "colbert" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveRerankerBackendPolicy(DaemonConfig{}); REQUIRE(policy.backend.has_value()); CHECK((*policy.backend == "colbert")); } SECTION("normalizes reranker backend to lowercase") { auto configPath = writeToml("reranker_backend_upper.toml", R"( [search] reranker_backend = "ONNX" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveRerankerBackendPolicy(DaemonConfig{}); REQUIRE(policy.backend.has_value()); CHECK((*policy.backend == "onnx")); } } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveInstrumentationPolicy applies memory profile", "[daemon][components][config][instrumentation][catch2]") { SECTION("auto profile activates when MallocStackLoggingNoCompact is present") { auto configPath = writeToml("instrumentation_auto.toml", R"( [daemon] log_level = "info" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); EnvGuard msl("MallocStackLoggingNoCompact", "1"); EnvGuard mslCompact("MallocStackLogging", "0"); auto policy = ConfigResolver::resolveInstrumentationPolicy(DaemonConfig{}); CHECK((policy.profile == "auto")); CHECK(policy.memoryProfileActive); CHECK(policy.suppressAutoRepair); CHECK(policy.suppressSimeonLexicalBuild); CHECK(policy.suppressVectorIndexBuild); } SECTION("normal profile disables auto MSL suppression") { auto configPath = writeToml("instrumentation_normal.toml", R"( [daemon.instrumentation] profile = "normal" )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); EnvGuard msl("MallocStackLoggingNoCompact", "1"); auto policy = ConfigResolver::resolveInstrumentationPolicy(DaemonConfig{}); CHECK((policy.profile == "normal")); CHECK_FALSE(policy.memoryProfileActive); CHECK_FALSE(policy.suppressAutoRepair); CHECK_FALSE(policy.suppressSimeonLexicalBuild); CHECK_FALSE(policy.suppressVectorIndexBuild); } SECTION("memory profile supports explicit overrides and stack-log threshold") { auto configPath = writeToml("instrumentation_memory.toml", R"( [daemon.instrumentation] profile = "memory" suppress_auto_repair = false suppress_simeon_lexical_build = true suppress_vector_index_build = true msl_stack_log_warn_mb = 1536 )"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); EnvGuard msl("MallocStackLogging", "0"); EnvGuard mslCompact("MallocStackLoggingNoCompact", "0"); auto policy = ConfigResolver::resolveInstrumentationPolicy(DaemonConfig{}); CHECK((policy.profile == "memory")); CHECK(policy.memoryProfileActive); CHECK_FALSE(policy.suppressAutoRepair); CHECK(policy.suppressSimeonLexicalBuild); CHECK(policy.suppressVectorIndexBuild); CHECK((policy.mslStackLogWarnBytes == 1536ULL * 1024ULL * 1024ULL)); } } TEST_CASE("ConfigResolver applies typed memory-sync policy", "[daemon][config][memory-sync]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}, {"sync_interval_ms", "250"}, {"max_index_objects_per_sync", "17"}, {"max_envelope_bytes", "2048"}, {"max_value_bytes", "4096"}, {"max_merged_keys", "23"}, {"max_cache_bytes", "8192"}, {"max_tracked_identities", "31"}, {"max_scan_bytes", "16384"}, {"mode", "temporary"}, {"session_id", "run-123"}, {"writer_auth_required", "true"}, {"writer_auth_manifest", "/secure/writers.json"}}}, }; DaemonConfig config; ConfigResolver::applyMemorySync(sections, config); CHECK(config.memorySync.enabled); CHECK(config.memorySync.nodeId == "123e4567-e89b-42d3-a456-426614174000"); CHECK(config.memorySync.corpusId == "corpus-a"); CHECK(config.memorySync.corpusEpoch == 9); CHECK(config.memorySync.backend == "filesystem"); CHECK(config.memorySync.path == "shared-memory"); CHECK(config.memorySync.syncIntervalMs == 250); CHECK(config.memorySync.limits.maxIndexObjectsPerSync == 17); CHECK(config.memorySync.limits.maxEnvelopeBytes == 2048); CHECK(config.memorySync.limits.maxValueBytes == 4096); CHECK(config.memorySync.limits.maxMergedKeys == 23); CHECK(config.memorySync.limits.maxCacheBytes == 8192); CHECK(config.memorySync.limits.maxTrackedIdentities == 31); CHECK(config.memorySync.limits.maxScanBytes == 16384); CHECK(config.memorySync.mode == "temporary"); CHECK(config.memorySync.sessionId == "run-123"); CHECK(config.memorySync.writerAuthRequired); CHECK(config.memorySync.writerAuthManifestPath == "/secure/writers.json"); } TEST_CASE("ConfigResolver rejects invalid complete-sweep byte budgets", "[daemon][config][memory-sync][scan-safety]") { for (const auto* value : {"0", "-1", "1.5", "184467440737095516160"}) { CAPTURE(value); ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "false"}, {"max_scan_bytes", value}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } } TEST_CASE("ConfigResolver requires explicit shared corpus scope for replication", "[daemon][config][memory-sync][sharing]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"transport", "shared-store"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_epoch", "1"}, {"path", "shared-memory"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); sections["memory_sync"]["corpus_scope"] = "shared"; CHECK(ConfigResolver::applyMemorySync(sections, config)); CHECK(config.memorySync.enabled); sections["memory_sync"]["corpus_scope"] = "personal"; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("Direct P2P defaults to operator-approved first contact", "[daemon][config][memory-sync][p2p][security]") { DaemonConfig config; CHECK_FALSE(config.memorySync.allowFirstContact); } TEST_CASE("ConfigResolver rejects direct P2P without usable writer authentication", "[daemon][config][memory-sync][p2p][security]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"transport", "direct"}, {"listen", "0.0.0.0:9721"}, {"identity_key", "/secure/p2p.pem"}, {"allow_first_contact", "false"}, {"max_peers", "12"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver accepts authenticated direct P2P without a shared path", "[daemon][config][memory-sync][p2p][security]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"transport", "direct"}, {"listen", "0.0.0.0:9721"}, {"allow_first_contact", "false"}, {"max_peers", "12"}, {"writer_auth_required", "true"}, {"writer_auth_manifest", "/secure/writers.json"}}}, }; DaemonConfig config; REQUIRE(ConfigResolver::applyMemorySync(sections, config)); CHECK(config.memorySync.enabled); CHECK(config.memorySync.transport == "direct"); CHECK(config.memorySync.listen == "0.0.0.0:9721"); CHECK_FALSE(config.memorySync.allowFirstContact); CHECK(config.memorySync.maxPeers == 12); CHECK(config.memorySync.path.empty()); CHECK(config.memorySync.writerAuthRequired); CHECK(config.memorySync.writerAuthManifestPath == "/secure/writers.json"); } TEST_CASE("ConfigResolver rejects direct P2P sync_interval_ms above the reconnect ceiling", "[daemon][config][memory-sync][p2p][security]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"transport", "direct"}, {"listen", "0.0.0.0:9721"}, {"sync_interval_ms", "300001"}, {"writer_auth_required", "true"}, {"writer_auth_manifest", "/secure/writers.json"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver infers shared-store for legacy backend path", "[daemon][config][memory-sync]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}}}, }; DaemonConfig config; REQUIRE(ConfigResolver::applyMemorySync(sections, config)); CHECK(config.memorySync.transport == "shared-store"); } TEST_CASE("ConfigResolver exposes explicit legacy migration mode", "[daemon][config][memory-sync]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}, {"allow_legacy_unbound", "true"}}}, }; DaemonConfig config; ConfigResolver::applyMemorySync(sections, config); CHECK(config.memorySync.enabled); CHECK(config.memorySync.mode == "persistent-migration"); } TEST_CASE("ConfigResolver rejects legacy migration for temporary mode", "[daemon][config][memory-sync]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}, {"mode", "temporary"}, {"session_id", "run-123"}, {"allow_legacy_unbound", "true"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver rejects writer authentication during legacy migration", "[daemon][config][memory-sync][auth]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}, {"allow_legacy_unbound", "true"}, {"writer_auth_required", "true"}, {"writer_auth_manifest", "/secure/writers.json"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver rejects malformed writer authentication flag", "[daemon][config][memory-sync][auth]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"writer_auth_required", "sometimes"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver bounds temporary session expiry", "[daemon][config][memory-sync][temporary]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "123e4567-e89b-42d3-a456-426614174000"}, {"corpus_id", "corpus-a"}, {"corpus_scope", "shared"}, {"corpus_epoch", "9"}, {"backend", "filesystem"}, {"path", "shared-memory"}, {"mode", "temporary"}, {"session_id", "run-123"}, {"sync_interval_ms", "100"}, {"temporary_session_ttl_ms", "300"}}}, }; DaemonConfig config; ConfigResolver::applyMemorySync(sections, config); REQUIRE(config.memorySync.enabled); // pi-lens-ignore: clang:no_member CHECK(config.memorySync.temporarySessionTtlMs == 300); sections["memory_sync"]["temporary_session_ttl_ms"] = "299"; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver rejects malformed opt-in memory sync", "[daemon][config][memory-sync]") { ConfigResolver::ConfigSections sections = { {"memory_sync", {{"enabled", "true"}, {"node_id", "node-a"}, {"backend", "s3"}, {"path", "s3://bucket/memory"}, {"sync_interval_ms", "0"}}}, }; DaemonConfig config; CHECK_FALSE(ConfigResolver::applyMemorySync(sections, config)); CHECK_FALSE(config.memorySync.enabled); } TEST_CASE("ConfigResolver preserves tuning bounds and zero reservations", "[daemon][components][config][tuning-boundaries][catch2]") { struct ResetTuning { ~ResetTuning() { (void)ConfigResolver::applyRuntimeTuning({}, TuningConfig{}); } } reset; (void)ConfigResolver::applyRuntimeTuning({}, TuningConfig{}); ConfigResolver::ConfigSections sections; SECTION("pending KG capacity is applied through typed configuration") { sections["tuning"]["post_ingest_pending_kg_max"] = "7"; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(TuneAdvisor::postIngestPendingKgMax() == 7u); CHECK(resolved.provenance.count("tuning.post_ingest_pending_kg_max") == 1); } SECTION("connection growth step cannot exceed its supported range") { sections["tuning"]["conn_slots_step"] = "4294967295"; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(resolved.provenance.count("tuning.conn_slots_step") == 0); CHECK(TuneAdvisor::connectionSlotsScaleStep() <= 128u); } SECTION("typed integer boundaries agree with provenance") { struct Range { const char* key; uint64_t minimum; uint64_t maximum; }; const Range ranges[] = { {"backpressure_read_pause_ms", 1, 1000}, {"worker_poll_ms", 50, 2000}, {"idle_shrink_hold_ms", 500, 60000}, {"pool_cooldown_ms", 1, 60000}, {"pool_ipc_min", 1, 1024}, {"pool_ipc_max", 1, 4096}, {"pool_io_min", 1, 1024}, {"pool_io_max", 1, 4096}, {"io_conn_per_thread", 1, 1024}, {"post_ingest_threads", 0, 64}, {"post_ingest_queue_max", 10, 1000000}, {"post_ingest_pending_kg_max", 1, UINT32_MAX}, {"list_inflight_limit", 1, 1024}, {"list_admission_wait_ms", 1, 120000}, {"grep_inflight_limit", 1, 1024}, {"grep_admission_wait_ms", 1, 120000}, {"conn_slots_min", 1, 1024}, {"conn_slots_max", 64, 16384}, {"conn_slots_step", 1, 128}, {"onnx_max_concurrent", 1, 64}, {"onnx_gliner_reserved", 0, 8}, {"onnx_embed_reserved", 0, 8}, {"onnx_reranker_reserved", 0, 8}, {"onnx_sessions_per_model", 1, 32}, {"indexing_workers_max", 1, UINT32_MAX}, {"store_document_channel_capacity", 64, 1000000}, {"work_coordinator_threads", 1, 512}, {"embed_channel_capacity", 256, 65536}, }; for (const auto& range : ranges) { const uint64_t values[] = {0, range.minimum, range.maximum, range.minimum > 0 ? range.minimum - 1 : 0, range.maximum + 1}; for (auto value : values) { CAPTURE(range.key, value); sections["tuning"].clear(); const std::string key = range.key; // Keep the paired endpoint compatible while testing scalar boundaries. if (key == "conn_slots_min") sections["tuning"]["conn_slots_max"] = "16384"; if (key == "conn_slots_max") sections["tuning"]["conn_slots_min"] = "1"; if (key == "pool_ipc_min") sections["tuning"]["pool_ipc_max"] = "4096"; if (key == "pool_io_min") sections["tuning"]["pool_io_max"] = "4096"; sections["tuning"][key] = std::to_string(value); const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); const bool accepted = value >= range.minimum && value <= range.maximum; CHECK(resolved.provenance.count("tuning." + key) == (accepted ? 1 : 0)); } } } SECTION("inverted minimum and maximum are rejected together") { struct Pair { const char* minimum; const char* maximum; uint32_t (*getMinimum)(); uint32_t (*getMaximum)(); }; const Pair pairs[] = { {"conn_slots_min", "conn_slots_max", &TuneAdvisor::connectionSlotsMin, &TuneAdvisor::connectionSlotsMax}, {"pool_ipc_min", "pool_ipc_max", &TuneAdvisor::poolMinSizeIpc, &TuneAdvisor::poolMaxSizeIpc}, {"pool_io_min", "pool_io_max", &TuneAdvisor::poolMinSizeIpcIo, &TuneAdvisor::poolMaxSizeIpcIo}, }; for (const auto& pair : pairs) { CAPTURE(pair.minimum, pair.maximum); sections["tuning"] = {{pair.minimum, "1024"}, {pair.maximum, "64"}}; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(resolved.provenance.count(std::string("tuning.") + pair.minimum) == 0); CHECK(resolved.provenance.count(std::string("tuning.") + pair.maximum) == 0); CHECK(pair.getMinimum() <= pair.getMaximum()); } } SECTION("a maximum below the inherited minimum is rejected") { EnvGuard minimum{"YAMS_CONN_SLOTS_MIN", "256"}; const auto originalMaximum = TuneAdvisor::connectionSlotsMax(); sections["tuning"]["conn_slots_max"] = "64"; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(resolved.provenance.count("tuning.conn_slots_max") == 0); CHECK(TuneAdvisor::connectionSlotsMax() == originalMaximum); } SECTION("zero reserved ONNX slots are a value, not unset") { const auto glinerDefault = TuneAdvisor::onnxGlinerReserved(); const auto embedDefault = TuneAdvisor::onnxEmbedReserved(); const auto rerankerDefault = TuneAdvisor::onnxRerankerReserved(); sections["tuning"] = {{"onnx_gliner_reserved", "0"}, {"onnx_embed_reserved", "0"}, {"onnx_reranker_reserved", "0"}}; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, TuningConfig{}); CHECK(resolved.provenance.count("tuning.onnx_gliner_reserved") == 1); CHECK(resolved.provenance.count("tuning.onnx_embed_reserved") == 1); CHECK(resolved.provenance.count("tuning.onnx_reranker_reserved") == 1); CHECK(TuneAdvisor::onnxGlinerReserved() == 0u); CHECK(TuneAdvisor::onnxEmbedReserved() == 0u); CHECK(TuneAdvisor::onnxRerankerReserved() == 0u); (void)ConfigResolver::applyRuntimeTuning({}, TuningConfig{}); CHECK(TuneAdvisor::onnxGlinerReserved() == glinerDefault); CHECK(TuneAdvisor::onnxEmbedReserved() == embedDefault); CHECK(TuneAdvisor::onnxRerankerReserved() == rerankerDefault); } } TEST_CASE_METHOD(ConfigResolverFixture, "Embedding backend environment selection does not conflict with the default", "[daemon][components][config][tuning-boundaries][catch2]") { EnvGuard backend{"YAMS_EMBED_BACKEND", "simeon"}; DaemonConfig config; config.configFilePath = writeToml("default-backend.toml", "[embeddings]\n"); const auto resolved = ConfigResolver::resolveEmbeddingConfig(config, tempDir); CHECK(resolved.backend == "simeon"); for (const auto& warning : resolved.warnings) { CHECK(warning.find("default:auto") == std::string::npos); } } TEST_CASE("ConfigResolver reports out-of-range values for the ranged [tuning] setters", "[daemon][components][config][tuning][catch2]") { // The setters keep their default outside their ranges; the resolver must not record // config provenance for a value that was never applied. ConfigResolver::ConfigSections sections; sections["tuning"] = {{"cpu_high_pct", "5"}, {"model_evict_warning_threshold", "1.5"}, {"connection_lifetime_s", "99999"}}; TuningConfig base; const auto resolved = ConfigResolver::applyRuntimeTuning(sections, base); CHECK((resolved.provenance.count("tuning.cpu_high_pct") == 0)); CHECK((resolved.provenance.count("tuning.model_evict_warning_threshold") == 0)); CHECK((resolved.provenance.count("tuning.connection_lifetime_s") == 0)); CHECK((TuneAdvisor::cpuHighThresholdPercent() >= 10.0)); CHECK((TuneAdvisor::modelEvictWarningThreshold() < 1.0)); CHECK((TuneAdvisor::connectionLifetimeSeconds() <= 86400u)); sections["tuning"] = {}; (void)ConfigResolver::applyRuntimeTuning(sections, base); } TEST_CASE_METHOD(ConfigResolverFixture, "ConfigResolver::resolveTopologyEnginePolicy reads the representative rule", "[daemon][components][config][topology][catch2]") { SECTION("medoid is resolved") { auto configPath = writeToml("config.toml", R"TOML( [topology] representative_rule = "medoid" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); REQUIRE(policy.representativeRule.has_value()); CHECK((*policy.representativeRule == yams::topology::RepresentativeRule::Medoid)); } SECTION("degree is resolved case-insensitively") { auto configPath = writeToml("config.toml", R"TOML( [topology] representative_rule = "Degree" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); REQUIRE(policy.representativeRule.has_value()); CHECK((*policy.representativeRule == yams::topology::RepresentativeRule::Degree)); } SECTION("an unrecognized rule is ignored") { auto configPath = writeToml("config.toml", R"TOML( [topology] representative_rule = "centroid" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); CHECK_FALSE(policy.representativeRule.has_value()); } SECTION("absent keeps the engine default") { auto configPath = writeToml("config.toml", R"TOML( [topology] engine = "connected" )TOML"); EnvGuard cfg("YAMS_CONFIG_PATH", configPath.string()); auto policy = ConfigResolver::resolveTopologyEnginePolicy(); CHECK_FALSE(policy.representativeRule.has_value()); } }