yams/tests/unit/daemon/embedding_lifecycle_manager_catch2_test.cpp

145 lines
5 KiB
C++

#include <catch2/catch_test_macros.hpp>
#include <thread>
#include <yams/daemon/components/ConfigResolver.h>
#include <yams/daemon/components/EmbeddingLifecycleManager.h>
using namespace yams::daemon;
static EmbeddingLifecycleManager::Dependencies nullDeps() {
return {
[] { return std::shared_ptr<IModelProvider>{}; },
[] { return std::shared_ptr<EmbeddingService>{}; },
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<const ResolvedEmbeddingConfig>(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());
}