/** * @file test_metadata.cpp * @brief Module records: installation, removal, and listing without loading * * @copyright Copyright (c) 2026 SILO GROUP LLC * @author Chris Punches * * Part of the Dark Horse Linux Package Manager (DPM) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Affero General Public License as * published by the Free Software Foundation, either version 3 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see . */ #include "harness.hpp" #include #include namespace fs = std::filesystem; int main(void) { const std::string scratch = std::string(TEST_SCRATCH_ROOT) + "/metadata"; /* ---- listing reads records and marks what has none ---- */ { dpm_open_overrides overrides = {TEST_FIXTURE_CONF, TEST_FIXTURE_MODULES, nullptr, -1, TEST_FIXTURE_METADATA}; dpm_ctx* ctx = dpm_open(&overrides); CHECK(ctx != nullptr); if (!ctx) { std::fprintf(stderr, "cannot continue without a context\n"); return 1; } dpm_cursor* cur = dpm_list_modules(ctx); CHECK(cur != nullptr); if (cur) { int count = 0; int uninstalled = 0; bool saw_good = false; dpm_module_info info; while (dpm_cursor_next(cur, &info) == 0) { count++; CHECK(info.name != nullptr); CHECK(info.version != nullptr); CHECK(info.description != nullptr); if (std::strcmp(info.name, "good") == 0) { saw_good = true; CHECK(std::strcmp(info.version, "1.2.3") == 0); CHECK(*info.description); } if (std::strcmp(info.version, "") == 0) { uninstalled++; } } /* every .so present is reported, recorded or not */ CHECK(count == 3); CHECK(saw_good); CHECK(uninstalled == 2); dpm_cursor_free(cur); } /* the cursor reports nothing further once exhausted */ cur = dpm_list_modules(ctx); CHECK(cur != nullptr); if (cur) { dpm_module_info info; while (dpm_cursor_next(cur, &info) == 0) { } CHECK(dpm_cursor_next(cur, &info) != 0); dpm_cursor_free(cur); } CHECK(dpm_cursor_next(nullptr, nullptr) != 0); dpm_cursor_free(nullptr); /* an unreadable module path refuses with a reason */ dpm_open_overrides bad_path = {TEST_FIXTURE_CONF, "/does/not/exist", nullptr, -1, TEST_FIXTURE_METADATA}; dpm_ctx* bad = dpm_open(&bad_path); CHECK(bad != nullptr); if (bad) { CHECK(dpm_list_modules(bad) == nullptr); CHECK(error_contains(bad, "module path")); dpm_close(bad); } CHECK(dpm_list_modules(nullptr) == nullptr); dpm_close(ctx); } /* ---- installation writes a record a later context reads back ---- */ { fs::remove_all(scratch); dpm_open_overrides overrides = {TEST_FIXTURE_CONF, TEST_FIXTURE_MODULES, nullptr, -1, scratch.c_str()}; dpm_ctx* ctx = dpm_open(&overrides); CHECK(ctx != nullptr); if (!ctx) { std::fprintf(stderr, "cannot continue without a context\n"); return 1; } /* a module that cannot load cannot be installed */ CHECK(dpm_install_module(ctx, "bad_version") != 0); CHECK(error_contains(ctx, "malformed version")); CHECK(dpm_install_module(ctx, "nonexistent") != 0); CHECK(dpm_install_module(ctx, nullptr) != 0); CHECK(dpm_install_module(nullptr, "good") != 0); CHECK(dpm_install_module(ctx, "good") == 0); CHECK(fs::exists(scratch + "/good.meta")); /* the record answers a fresh context without opening the module */ dpm_ctx* reader = dpm_open(&overrides); CHECK(reader != nullptr); if (reader) { dpm_cursor* cur = dpm_list_modules(reader); CHECK(cur != nullptr); if (cur) { bool found = false; dpm_module_info info; while (dpm_cursor_next(cur, &info) == 0) { if (std::strcmp(info.name, "good") == 0) { found = true; CHECK(std::strcmp(info.version, "1.2.3") == 0); CHECK(std::strcmp(info.description, "Known-good stub module.") == 0); } } CHECK(found); dpm_cursor_free(cur); } dpm_close(reader); } /* installing again over an existing record succeeds */ CHECK(dpm_install_module(ctx, "good") == 0); /* removal takes the record and leaves the module file alone */ CHECK(dpm_uninstall_module(ctx, "good") == 0); CHECK(!fs::exists(scratch + "/good.meta")); CHECK(dpm_uninstall_module(ctx, "good") != 0); CHECK(dpm_uninstall_module(ctx, nullptr) != 0); CHECK(dpm_uninstall_module(nullptr, "good") != 0); /* the module still loads by its own name once uninstalled */ dpm_ctx* after = dpm_open(&overrides); CHECK(after != nullptr); if (after) { CHECK(dpm_require(after, "good") != nullptr); dpm_close(after); } dpm_close(ctx); } /* ---- an unwritable metadata directory fails without throwing ---- */ { dpm_open_overrides readonly = {TEST_FIXTURE_CONF, TEST_FIXTURE_MODULES, nullptr, -1, "/proc/dpm-metadata"}; dpm_ctx* ctx = dpm_open(&readonly); CHECK(ctx != nullptr); if (ctx) { CHECK(dpm_install_module(ctx, "good") != 0); /* listing and loading carry on with no records available */ dpm_cursor* cur = dpm_list_modules(ctx); CHECK(cur != nullptr); if (cur) { dpm_cursor_free(cur); } CHECK(dpm_require(ctx, "good") != nullptr); dpm_close(ctx); } } return harness_report("metadata"); }