| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899 |
- #pragma once
- #include "db_test_util.hpp"
- #include "server.hpp"
- #include <gtest/gtest.h>
- #include <httplib.h>
- #include <chrono>
- #include <filesystem>
- #include <fstream>
- #include <memory>
- #include <thread>
- #include <vector>
- namespace svapi::testutil {
- class ApiFixture : public ::testing::Test {
- protected:
- std::unique_ptr<DbGateway> db_;
- std::unique_ptr<CollectionRegistry> registry_;
- std::unique_ptr<SettingsStore> settings_;
- std::unique_ptr<KeyStore> keys_;
- std::unique_ptr<SessionStore> sessions_;
- std::unique_ptr<ApiServer> server_;
- std::thread serverThread_;
- int port_ = 0;
- std::string adminKey_;
- std::string project_; // a project the admin owns; tests create collections under it
- httplib::Server mockOpenAi_;
- std::thread mockThread_;
- int mockPort_ = 0, mockDim_ = 4;
- std::string settingsColl_ = "svapitest_api_settings";
- std::string keysColl_ = "svapitest_api_keys";
- std::string webuiDir_ = "/tmp/svapi_webui_test"; // sentinel SPA root for fallback tests
- void SetUp() override {
- db_ = connectOrNull();
- if (!db_) GTEST_SKIP() << "smartbotic-database not reachable";
- project_ = tmpName("apiproj");
- db_->client().dropCollection(settingsColl_);
- db_->client().dropCollection(keysColl_);
- db_->dropProject(project_);
- // DB v2.3.1 Stage-F caveat: dropProject does not delete collection data.
- // Drop the project's registry collection explicitly so ensure() starts fresh.
- db_->client().dropCollection(qualify(project_, "vectorapi_collections"));
- db_->ensureProject(project_);
- registry_ = std::make_unique<CollectionRegistry>(*db_);
- registry_->ensure(project_);
- settings_ = std::make_unique<SettingsStore>(*db_, settingsColl_, "current");
- settings_->bootstrap("");
- keys_ = std::make_unique<KeyStore>(*db_, keysColl_);
- keys_->bootstrap("");
- adminKey_ = keys_->list().at(0).key;
- sessions_ = std::make_unique<SessionStore>(std::chrono::minutes(60));
- mockOpenAi_.Post("/v1/embeddings", [this](const httplib::Request&, httplib::Response& res) {
- nlohmann::json emb = nlohmann::json::array();
- for (int i = 0; i < mockDim_; ++i) emb.push_back(0.1f * (i + 1));
- res.set_content(nlohmann::json{{"data", {{{"embedding", emb}}}}}.dump(), "application/json");
- });
- mockPort_ = mockOpenAi_.bind_to_any_port("127.0.0.1");
- mockThread_ = std::thread([this]{ mockOpenAi_.listen_after_bind(); });
- Settings s = *settings_->snapshot();
- s.openaiApiBase = "http://127.0.0.1:" + std::to_string(mockPort_);
- s.openaiApiKey = "test";
- settings_->save(s);
- std::filesystem::create_directories(webuiDir_);
- std::ofstream(webuiDir_ + "/index.html")
- << "<!doctype html><html><head><title>SVAPI_SPA_OK</title></head><body><div id=\"root\"></div></body></html>";
- ServerDeps deps{*db_, *settings_, *keys_, *registry_, *sessions_, webuiDir_, apiDocsDir()};
- server_ = std::make_unique<ApiServer>(std::move(deps));
- server_->registerRoutes();
- port_ = server_->bindToAnyPort("127.0.0.1");
- serverThread_ = std::thread([this]{ server_->listenAfterBind(); });
- httplib::Client probe("127.0.0.1", port_);
- for (int i = 0; i < 200; ++i) { if (probe.Get("/healthz")) break;
- std::this_thread::sleep_for(std::chrono::milliseconds(10)); }
- }
- void TearDown() override {
- if (server_) server_->stop();
- if (serverThread_.joinable()) serverThread_.join();
- mockOpenAi_.stop();
- if (mockThread_.joinable()) mockThread_.join();
- if (db_) { db_->client().dropCollection(settingsColl_);
- db_->client().dropCollection(keysColl_);
- db_->dropProject(project_); }
- }
- httplib::Client http(const std::string& bearer) {
- httplib::Client c("127.0.0.1", port_);
- c.set_default_headers({{"Authorization", "Bearer " + bearer}});
- return c;
- }
- httplib::Client admin() { return http(adminKey_); }
- httplib::Client noAuth() { return httplib::Client("127.0.0.1", port_); }
- static std::string apiDocsDir() { return SVAPI_API_DOCS_DIR; }
- };
- } // namespace svapi::testutil
|