// DaemonMetrics unit tests — focused on testable surface without live daemon #include #include #include #include #include #include #include #include #include namespace yams::daemon::test { TEST_CASE("MetricsSnapshot default values are zero / empty", "[metrics][snapshot]") { MetricsSnapshot snap{}; REQUIRE(snap.running == true); REQUIRE(snap.ready == false); REQUIRE(snap.version.empty()); REQUIRE(snap.uptimeSeconds == 0); REQUIRE(snap.requestsProcessed == 0); REQUIRE(snap.activeConnections == 0); REQUIRE(snap.memoryUsageMb == 0.0); REQUIRE(snap.cpuUsagePercent == 0.0); REQUIRE(snap.fsmTransitions == 0); REQUIRE(snap.databasePhase.empty()); REQUIRE(snap.vectorDbDim == 0); } TEST_CASE("MetricsSnapshot has reasonable default readiness", "[metrics][snapshot]") { MetricsSnapshot snap{}; REQUIRE(snap.searchActive == 0); REQUIRE(snap.searchQueued == 0); REQUIRE(snap.searchExecuted == 0); REQUIRE(snap.searchCacheHitRate == 0.0); REQUIRE(snap.searchAvgLatencyUs == 0); REQUIRE(snap.embeddingAvailable == false); REQUIRE(snap.workerThreads == 0); REQUIRE(snap.workerActive == 0); REQUIRE(snap.workerQueued == 0); REQUIRE(snap.repairRunning == false); REQUIRE(snap.repairInProgress == false); REQUIRE(snap.repairQueueDepth == 0); REQUIRE(snap.topologyRebuildRunning == false); REQUIRE(snap.topologyArtifactsFresh == false); REQUIRE(snap.topologyLastRunSucceeded == false); REQUIRE(snap.governorPressureLevel == 0); REQUIRE(snap.governorHeadroomPct == 100); } TEST_CASE("MetricsSnapshot fields can be set", "[metrics][snapshot]") { MetricsSnapshot snap{}; snap.running = false; snap.ready = true; snap.version = "0.16.2"; snap.uptimeSeconds = 3600; snap.requestsProcessed = 1000; snap.activeConnections = 5; snap.memoryUsageMb = 512.0; snap.cpuUsagePercent = 25.5; REQUIRE(snap.running == false); REQUIRE(snap.ready == true); REQUIRE(snap.version == "0.16.2"); REQUIRE(snap.uptimeSeconds == 3600); REQUIRE(snap.requestsProcessed == 1000); REQUIRE(snap.activeConnections == 5); REQUIRE(snap.memoryUsageMb == Catch::Approx(512.0)); REQUIRE(snap.cpuUsagePercent == Catch::Approx(25.5)); } TEST_CASE("MetricsSnapshot nested structures are default-initialized", "[metrics][snapshot]") { MetricsSnapshot snap{}; REQUIRE(snap.glExtraction.limit == 0.0); REQUIRE(snap.glExtraction.smoothedRtt == 0.0); REQUIRE(snap.glExtraction.gradient == 0.0); REQUIRE(snap.glExtraction.inFlight == 0); REQUIRE(snap.glExtraction.acquireCount == 0); REQUIRE(snap.glExtraction.rejectCount == 0); REQUIRE(snap.glKg.limit == 0.0); REQUIRE(snap.glEntity.limit == 0.0); REQUIRE(snap.glEmbed.limit == 0.0); REQUIRE(snap.gradientLimitersEnabled == false); } TEST_CASE("MetricsSnapshot pipeline counters start at zero", "[metrics][snapshot]") { MetricsSnapshot snap{}; REQUIRE(snap.extractionInFlight == 0); REQUIRE(snap.kgQueued == 0); REQUIRE(snap.kgDropped == 0); REQUIRE(snap.kgConsumed == 0); REQUIRE(snap.kgInFlight == 0); REQUIRE(snap.entityQueued == 0); REQUIRE(snap.entityDropped == 0); REQUIRE(snap.embedQueued == 0); REQUIRE(snap.embedDropped == 0); REQUIRE(snap.embedInFlight == 0); } TEST_CASE("MetricsSnapshot DB pool defaults are sane", "[metrics][snapshot]") { MetricsSnapshot snap{}; REQUIRE(snap.dbWritePoolAvailable == false); REQUIRE(snap.dbWritePoolTotalConnections == 0); REQUIRE(snap.dbWritePoolAvailableConnections == 0); REQUIRE(snap.dbWritePoolActiveConnections == 0); REQUIRE(snap.dbWritePoolWaitingRequests == 0); REQUIRE(snap.dbReadPoolAvailable == false); REQUIRE(snap.dbReadPoolTotalConnections == 0); REQUIRE(snap.dbReadPoolAvailableConnections == 0); REQUIRE(snap.dbReadPoolActiveConnections == 0); } TEST_CASE("computePhysicalWalkTtl returns clamping behavior", "[metrics][ttl]") { // Zero duration → minimum TTL REQUIRE(computePhysicalWalkTtl(0) == 60'000); // Very fast walk → minimum TTL REQUIRE(computePhysicalWalkTtl(1) == 60'000); // Normal walk → amplified std::uint64_t ttl = computePhysicalWalkTtl(5'000); REQUIRE(ttl >= 60'000); REQUIRE(ttl <= 1'800'000); // Very slow walk → capped at max std::uint64_t maxTtl = computePhysicalWalkTtl(1'000'000); REQUIRE(maxTtl <= 1'800'000); } TEST_CASE("Polling completion stays unobservable until its final notifier finishes", "[metrics][polling-shutdown]") { detail::PollingCompletion completion; completion.markStarted(); std::promise beforeNotify; auto paused = beforeNotify.get_future(); std::promise allowNotify; auto resume = allowNotify.get_future().share(); yams::compat::jthread publisher([&] { completion.complete([&] { beforeNotify.set_value(); resume.wait(); }); }); // The event fixes the exact publication window; the snapshot never waits or sleeps. // Keep the owner alive and release/join the publisher before any failing assertion. const bool reachedBoundary = paused.wait_for(std::chrono::seconds(2)) == std::future_status::ready; const auto visible = reachedBoundary ? completion.tryStopped() : std::nullopt; allowNotify.set_value(); publisher.join(); REQUIRE(reachedBoundary); CHECK_FALSE(visible.value_or(false)); CHECK(completion.isStopped()); completion.wait(); } } // namespace yams::daemon::test