#include #include #include #include using namespace yams::daemon; static EmbeddingLifecycleManager::Dependencies nullDeps() { return { [] { return std::shared_ptr{}; }, [] { return std::shared_ptr{}; }, nullptr, nullptr, {}, }; } TEST_CASE("EmbeddingLifecycleManager initial state", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); CHECK(mgr.modelName().empty()); CHECK(mgr.adoptedPluginName().empty()); CHECK_FALSE(mgr.isAutoOnAdd()); CHECK_FALSE(mgr.preloadOnStartup()); CHECK((mgr.getEmbeddingDimension() == 0)); } TEST_CASE("EmbeddingLifecycleManager FSM starts Unavailable", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); auto snap = mgr.fsmSnapshot(); CHECK((snap.state == EmbeddingProviderState::Unavailable)); CHECK_FALSE(mgr.isDegraded()); CHECK_FALSE(mgr.isLoadingOrReady()); } TEST_CASE("EmbeddingLifecycleManager FSM transitions", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); mgr.fsm().dispatch(ProviderAdoptedEvent{"test-plugin"}); CHECK((mgr.fsmSnapshot().state == EmbeddingProviderState::ProviderAdopted)); mgr.fsm().dispatch(ModelLoadStartedEvent{"model-a"}); CHECK(mgr.isLoadingOrReady()); mgr.fsm().dispatch(ModelLoadedEvent{"model-a", 384}); CHECK(mgr.isLoadingOrReady()); auto snap = mgr.fsmSnapshot(); CHECK((snap.state == EmbeddingProviderState::ModelReady)); CHECK((snap.embeddingDimension == 384)); } TEST_CASE("EmbeddingLifecycleManager model name and plugin name setters", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); mgr.setModelName("bge-small"); CHECK((mgr.modelName() == "bge-small")); mgr.setAdoptedPluginName("onnx-provider"); CHECK((mgr.adoptedPluginName() == "onnx-provider")); } TEST_CASE("EmbeddingLifecycleManager auto-on-add and preload flags", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); mgr.setAutoOnAdd(true); CHECK(mgr.isAutoOnAdd()); mgr.setPreloadOnStartup(true); CHECK(mgr.preloadOnStartup()); mgr.setPreloadOnStartup(false); CHECK_FALSE(mgr.preloadOnStartup()); } TEST_CASE("EmbeddingLifecycleManager metrics return 0 with null service", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); CHECK((mgr.inFlightJobs() == 0)); CHECK((mgr.queuedJobs() == 0)); CHECK((mgr.activeInferSubBatches() == 0)); CHECK((mgr.inferOldestMs() == 0)); CHECK((mgr.inferStartedCount() == 0)); CHECK((mgr.inferCompletedCount() == 0)); CHECK((mgr.inferLastMs() == 0)); CHECK((mgr.inferMaxMs() == 0)); CHECK((mgr.inferWarnCount() == 0)); CHECK((mgr.semanticEdgesCreated() == 0)); CHECK((mgr.semanticDocsProcessed() == 0)); CHECK((mgr.semanticUpdateErrors() == 0)); } TEST_CASE("EmbeddingLifecycleManager ensureModelReadySync fails without provider", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); auto result = mgr.ensureModelReadySync("test-model"); CHECK_FALSE(result.has_value()); CHECK((result.error().code == yams::ErrorCode::InvalidState)); } TEST_CASE("EmbeddingLifecycleManager resetWarmupState clears state", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); mgr.setModelName("model-a"); mgr.setAdoptedPluginName("plugin-a"); mgr.resetWarmupState(); CHECK((mgr.modelName() == "model-a")); CHECK((mgr.adoptedPluginName() == "plugin-a")); } TEST_CASE("EmbeddingLifecycleManager resolvePreferredModel returns empty with null config", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); CHECK(mgr.resolvePreferredModel().empty()); } TEST_CASE("EmbeddingLifecycleManager detectPreloadFlag returns false with null config", "[daemon][embedding-lifecycle][catch2]") { EmbeddingLifecycleManager mgr(nullDeps()); CHECK_FALSE(mgr.detectPreloadFlag()); } TEST_CASE("EmbeddingLifecycleManager uses the immutable effective embedding snapshot", "[daemon][embedding-lifecycle][config][snapshot][catch2]") { ResolvedEmbeddingConfig mutableConfig; mutableConfig.backend = "onnxruntime"; mutableConfig.preferredModel = "frozen-model"; mutableConfig.preloadOnStartup = true; const auto snapshot = std::make_shared(mutableConfig); auto deps = nullDeps(); deps.getEmbeddingConfig = [snapshot] { return snapshot; }; EmbeddingLifecycleManager mgr(std::move(deps)); mutableConfig.preferredModel = "changed-after-snapshot"; mutableConfig.preloadOnStartup = false; CHECK((mgr.resolvePreferredModel() == "frozen-model")); CHECK(mgr.detectPreloadFlag()); }