diff --git a/src/middleware/orchestrator.cpp b/src/middleware/orchestrator.cpp index f526ac7..8803c8d 100644 --- a/src/middleware/orchestrator.cpp +++ b/src/middleware/orchestrator.cpp @@ -71,6 +71,19 @@ Orchestrator::Orchestrator(QObject *parent) connect(&timer_, &QTimer::timeout, this, &Orchestrator::processPendingTasks); } +Orchestrator::~Orchestrator() { + delete dal_; +} + +ki::PgDal& Orchestrator::dal() { + if (!dal_) { + dal_ = new ki::PgDal(); + const QByteArray dsn = qgetenv("PG_DSN"); + if (!dsn.isEmpty()) dal_->connect(dsn.toStdString()); else dal_->connect("stub://memory"); + } + return *dal_; +} + void Orchestrator::start(int intervalMs) { ensureResolvedDirs(); continuityHandshakeOnce(); @@ -178,11 +191,7 @@ bool Orchestrator::saveSnapshot(const QString &nameSpace, const QJsonObject &content, const QStringList &tags) { - ki::PgDal dal; - const QByteArray dsn = qgetenv("PG_DSN"); - if (!dsn.isEmpty()) dal.connect(dsn.toStdString()); else dal.connect("stub://memory"); - - auto nsRow = dal.ensureNamespace(nameSpace.toStdString()); + auto nsRow = dal().ensureNamespace(nameSpace.toStdString()); if (!nsRow) return false; ki::ItemRow row; @@ -194,22 +203,18 @@ bool Orchestrator::saveSnapshot(const QString &nameSpace, row.metadata_json = "{}"; row.created_at = std::chrono::system_clock::now(); - const std::string id = dal.upsertItem(row); + const std::string id = dal().upsertItem(row); return !id.empty(); } std::optional Orchestrator::loadSnapshot(const QString &nameSpace, const QString &key) { - ki::PgDal dal; - const QByteArray dsn = qgetenv("PG_DSN"); - if (!dsn.isEmpty()) dal.connect(dsn.toStdString()); else dal.connect("stub://memory"); - - auto nsRow = dal.findNamespace(nameSpace.toStdString()); + auto nsRow = dal().findNamespace(nameSpace.toStdString()); if (!nsRow) return std::nullopt; std::vector tags; tags.emplace_back("snapshot"); - auto rows = dal.fetchContext(nsRow->id, std::optional(key.toStdString()), tags, std::nullopt, 1); + auto rows = dal().fetchContext(nsRow->id, std::optional(key.toStdString()), tags, std::nullopt, 1); if (rows.empty()) return std::nullopt; const auto &row = rows.front(); if (row.content_json.empty()) return std::nullopt; diff --git a/src/middleware/orchestrator.h b/src/middleware/orchestrator.h index 1da47ee..de889c1 100644 --- a/src/middleware/orchestrator.h +++ b/src/middleware/orchestrator.h @@ -8,6 +8,8 @@ #include "kompanion_mw_export.h" +namespace ki { class PgDal; } + // Minimal model provider interface so tests can stub generation. class IModelProvider { public: @@ -49,6 +51,7 @@ class KOMPANION_MW_EXPORT Orchestrator : public QObject { Q_OBJECT public: explicit Orchestrator(QObject *parent=nullptr); + ~Orchestrator(); // Injectable model provider (Ollama by default). Ownership left to caller. void setModelProvider(IModelProvider *prov) { model_ = prov; } @@ -104,4 +107,7 @@ private: QTimer timer_; bool continuityDone_ = false; IModelProvider *model_ = nullptr; // not owned + // Reused DB handle so in-memory stub persists across calls in tests. + ki::PgDal* dal_ = nullptr; + ki::PgDal& dal(); }; diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index cd17fe0..d0b1884 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -11,3 +11,9 @@ qt_add_executable(test_orchestrator ) target_link_libraries(test_orchestrator PRIVATE Qt6::Core Qt6::Network Qt6::Test kompanion_mw) add_test(NAME test_orchestrator COMMAND test_orchestrator) + +qt_add_executable(test_snapshot + test_snapshot.cpp +) +target_link_libraries(test_snapshot PRIVATE Qt6::Core Qt6::Network Qt6::Test kompanion_mw) +add_test(NAME test_snapshot COMMAND test_snapshot) diff --git a/tests/test_snapshot.cpp b/tests/test_snapshot.cpp new file mode 100644 index 0000000..354cf8e --- /dev/null +++ b/tests/test_snapshot.cpp @@ -0,0 +1,25 @@ +#include +#include "../src/middleware/orchestrator.h" + +class SnapshotTest : public QObject { + Q_OBJECT +private slots: + void round_trip() { + // Ensure no PG_DSN is required; Orchestrator maintains a shared in-memory DAL per instance + Orchestrator orch; + const QString ns = QStringLiteral("tests"); + const QString key = QStringLiteral("session:last"); + + QJsonObject payload{{"a", 1}, {"b", QStringLiteral("x")}}; + QVERIFY2(orch.saveSnapshot(ns, key, payload), "saveSnapshot should succeed"); + + auto loaded = orch.loadSnapshot(ns, key); + QVERIFY2(loaded.has_value(), "loadSnapshot should return value"); + QCOMPARE(loaded->value("a").toInt(), 1); + QCOMPARE(loaded->value("b").toString(), QStringLiteral("x")); + } +}; + +QTEST_MAIN(SnapshotTest) +#include "test_snapshot.moc" +