// Catch2 tests for embedding repair scan // Migrated from GTest: embedding_repair_scan_test.cpp #include #include #include #include #include #include #include #include namespace yams::repair::test { namespace { class InMemoryMetadata : public metadata::MetadataRepository { public: InMemoryMetadata() : metadata::MetadataRepository(*getPool()) {} void add(const metadata::DocumentInfo& d) { docs_.push_back(d); } Result> queryDocuments(const metadata::DocumentQueryOptions&) override { return docs_; } Result> findDocumentByExactPath(const std::string& path) override { for (auto& d : docs_) { if (d.filePath == path) return std::optional(d); } return std::optional(std::nullopt); } Result> getDocumentTags(int64_t) override { return std::vector{}; } Result> getDocumentByHash(const std::string& h) override { for (auto& d : docs_) if (d.sha256Hash == h) return std::optional(d); return std::optional(std::nullopt); } private: static std::shared_ptr getPool() { static std::shared_ptr p = [] { metadata::ConnectionPoolConfig cfg{}; cfg.enableWAL = false; cfg.enableForeignKeys = false; auto pool = std::make_shared(":memory:", cfg); (void)pool->initialize(); return pool; }(); return p; } std::vector docs_; }; } // namespace TEST_CASE("RepairUtilScan missing embeddings list identifies missing documents", "[repair][embedding][catch2]") { if (std::getenv("YAMS_DISABLE_VECTORS") || std::getenv("YAMS_DISABLE_VECTOR_DB")) { SKIP("Vectors disabled via environment"); } // Prepare temp dir for vector DB auto tmp = std::filesystem::temp_directory_path() / ("yams_repair_catch2_" + std::to_string(std::rand())); // Clean up any existing directory from previous runs std::error_code ec; std::filesystem::remove_all(tmp, ec); std::filesystem::create_directories(tmp); // Seed metadata with two docs; pre-insert one embedding auto repo = std::make_shared(); metadata::DocumentInfo d1{}; d1.id = 1; d1.sha256Hash = "h1"; d1.fileName = "a.txt"; d1.mimeType = "text/plain"; metadata::DocumentInfo d2{}; d2.id = 2; d2.sha256Hash = "h2"; d2.fileName = "b.txt"; d2.mimeType = "text/plain"; repo->add(d1); repo->add(d2); // Create vector DB and insert embedding for h1 yams::vector::VectorDatabaseConfig cfg{}; cfg.database_path = (tmp / "vectors.db").string(); cfg.create_if_missing = true; cfg.embedding_dim = 384; auto vdb = std::make_unique(cfg); REQUIRE(vdb->initialize()); yams::vector::VectorRecord rec; rec.document_hash = "h1"; rec.chunk_id = "c-1"; rec.embedding = std::vector(384, 0.1f); rec.content = "hello"; REQUIRE(vdb->insertVector(rec)); // Close the vector database to ensure all data is committed before scanning vdb.reset(); // Call repair scan auto missing = getDocumentsMissingEmbeddings(repo, tmp); REQUIRE(missing); auto list = missing.value(); // Only h2 should be missing REQUIRE(list.size() == 1u); CHECK(list[0] == std::string("h2")); // Cleanup std::filesystem::remove_all(tmp, ec); } TEST_CASE("RepairUtilScan prioritizes extractable documents ahead of skipped binaries", "[repair][embedding][catch2]") { if (std::getenv("YAMS_DISABLE_VECTORS") || std::getenv("YAMS_DISABLE_VECTOR_DB")) { SKIP("Vectors disabled via environment"); } auto tmp = std::filesystem::temp_directory_path() / ("yams_repair_priority_catch2_" + std::to_string(std::rand())); std::error_code ec; std::filesystem::remove_all(tmp, ec); std::filesystem::create_directories(tmp); auto repo = std::make_shared(); metadata::DocumentInfo skipped{}; skipped.id = 1; skipped.sha256Hash = "skipped-doc"; skipped.fileName = "image.png"; skipped.filePath = "/tmp/image.png"; skipped.fileSize = 128; skipped.mimeType = "image/png"; skipped.extractionStatus = metadata::ExtractionStatus::Skipped; metadata::DocumentInfo success{}; success.id = 2; success.sha256Hash = "success-doc"; success.fileName = "notes.txt"; success.filePath = "/tmp/notes.txt"; success.fileSize = 256; success.mimeType = "text/plain"; success.contentExtracted = true; success.extractionStatus = metadata::ExtractionStatus::Success; metadata::DocumentInfo failed{}; failed.id = 3; failed.sha256Hash = "failed-doc"; failed.fileName = "empty.cpp"; failed.filePath = "/tmp/empty.cpp"; failed.fileSize = 0; failed.mimeType = "text/x-c++"; failed.extractionStatus = metadata::ExtractionStatus::Failed; repo->add(skipped); repo->add(success); repo->add(failed); yams::vector::VectorDatabaseConfig cfg{}; cfg.database_path = (tmp / "vectors.db").string(); cfg.create_if_missing = true; cfg.embedding_dim = 384; auto vdb = std::make_unique(cfg); REQUIRE(vdb->initialize()); vdb.reset(); auto missing = getDocumentsMissingEmbeddings(repo, tmp); REQUIRE(missing); const auto& list = missing.value(); REQUIRE(list.size() == 3u); CHECK(list[0] == std::string("success-doc")); CHECK(list[1] == std::string("failed-doc")); CHECK(list[2] == std::string("skipped-doc")); std::filesystem::remove_all(tmp, ec); } TEST_CASE("RepairUtilScan returns empty list when all documents embedded", "[repair][embedding][catch2]") { if (std::getenv("YAMS_DISABLE_VECTORS") || std::getenv("YAMS_DISABLE_VECTOR_DB")) { SKIP("Vectors disabled via environment"); } auto tmp = std::filesystem::temp_directory_path() / ("yams_repair_all_embedded_" + std::to_string(std::rand())); std::error_code ec; std::filesystem::remove_all(tmp, ec); std::filesystem::create_directories(tmp); auto repo = std::make_shared(); metadata::DocumentInfo doc{}; doc.id = 1; doc.sha256Hash = "embedded-doc"; doc.fileName = "readme.md"; doc.mimeType = "text/markdown"; doc.contentExtracted = true; doc.extractionStatus = metadata::ExtractionStatus::Success; repo->add(doc); yams::vector::VectorDatabaseConfig cfg{}; cfg.database_path = (tmp / "vectors.db").string(); cfg.create_if_missing = true; cfg.embedding_dim = 384; auto vdb = std::make_unique(cfg); REQUIRE(vdb->initialize()); yams::vector::VectorRecord rec; rec.document_hash = "embedded-doc"; rec.chunk_id = "c-1"; rec.embedding = std::vector(384, 0.2f); rec.content = "content"; REQUIRE(vdb->insertVector(rec)); vdb.reset(); auto missing = getDocumentsMissingEmbeddings(repo, tmp); REQUIRE(missing); CHECK(missing.value().empty()); std::filesystem::remove_all(tmp, ec); } TEST_CASE("RepairUtilScan returns empty list for empty metadata", "[repair][embedding][catch2]") { if (std::getenv("YAMS_DISABLE_VECTORS") || std::getenv("YAMS_DISABLE_VECTOR_DB")) { SKIP("Vectors disabled via environment"); } auto tmp = std::filesystem::temp_directory_path() / ("yams_repair_empty_repo_" + std::to_string(std::rand())); std::error_code ec; std::filesystem::remove_all(tmp, ec); std::filesystem::create_directories(tmp); auto repo = std::make_shared(); yams::vector::VectorDatabaseConfig cfg{}; cfg.database_path = (tmp / "vectors.db").string(); cfg.create_if_missing = true; cfg.embedding_dim = 384; auto vdb = std::make_unique(cfg); REQUIRE(vdb->initialize()); vdb.reset(); auto missing = getDocumentsMissingEmbeddings(repo, tmp); REQUIRE(missing); CHECK(missing.value().empty()); std::filesystem::remove_all(tmp, ec); } } // namespace yams::repair::test