From d1c2f9664f375543c10c1beac1e447e056943235 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Pablo=20I=C3=B1igo=20Blasco?= Date: Wed, 29 Apr 2026 13:23:57 +0200 Subject: [PATCH] fix(marketplace): hide registry entries with no artifact for current platform MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The registry view listed every extension regardless of whether the declared `platforms` map contained an artifact for the host OS/arch. ExtensionManager::doInstall already rejects those installs with "No artifact available for platform ...", so advertising them in the catalog only led users to a guaranteed failure. Encapsulate the filter in RegistryManager::compatibleExtensions(platform) and call it from MarketplaceWindow with PlatformUtils::currentPlatform(). The platform key is injected by the caller so RegistryManager stays independent of host detection — tests can exercise the filter with any key (covered by the new CompatibleExtensionsFiltersByRequestedPlatform test) without depending on the OS the suite runs on. --- .../pj_marketplace/registry_manager.hpp | 7 ++++ pj_marketplace/src/core/RegistryManager.cpp | 11 +++++ pj_marketplace/src/ui/marketplace_window.cpp | 2 +- .../tests/registry_manager_test.cpp | 42 +++++++++++++++++++ 4 files changed, 61 insertions(+), 1 deletion(-) diff --git a/pj_marketplace/include/pj_marketplace/registry_manager.hpp b/pj_marketplace/include/pj_marketplace/registry_manager.hpp index 316c59c..7871b5e 100644 --- a/pj_marketplace/include/pj_marketplace/registry_manager.hpp +++ b/pj_marketplace/include/pj_marketplace/registry_manager.hpp @@ -34,6 +34,13 @@ class RegistryManager : public QObject { // Returns the parsed extensions after a successful fetch; empty list otherwise. QList extensions() const; + // Same as extensions(), but drops entries whose `platforms` map has no + // artifact for `platform` (e.g. "linux-x86_64"). Use this for catalog + // views — installation would fail anyway for the dropped entries (see + // ExtensionManager::doInstall). The platform key is injected by the + // caller so RegistryManager stays independent of host detection. + QList compatibleExtensions(const QString& platform) const; + // Returns the first extension whose id matches, or a default-constructed Extension // (id is empty) when not found. Extension findById(const QString& id) const; diff --git a/pj_marketplace/src/core/RegistryManager.cpp b/pj_marketplace/src/core/RegistryManager.cpp index c3dded6..e46015c 100644 --- a/pj_marketplace/src/core/RegistryManager.cpp +++ b/pj_marketplace/src/core/RegistryManager.cpp @@ -48,6 +48,17 @@ QList RegistryManager::extensions() const { return extensions_; } +QList RegistryManager::compatibleExtensions(const QString& platform) const { + QList result; + result.reserve(extensions_.size()); + for (const Extension& ext : extensions_) { + if (ext.platforms.contains(platform)) { + result.append(ext); + } + } + return result; +} + Extension RegistryManager::findById(const QString& id) const { for (const Extension& ext : extensions_) { if (ext.id == id) { diff --git a/pj_marketplace/src/ui/marketplace_window.cpp b/pj_marketplace/src/ui/marketplace_window.cpp index 8e2c635..16a5cdd 100644 --- a/pj_marketplace/src/ui/marketplace_window.cpp +++ b/pj_marketplace/src/ui/marketplace_window.cpp @@ -148,7 +148,7 @@ void MarketplaceWindow::setupSignals() { // A successful refresh is a strong "things are working" signal; let it // override any old sticky error so progress messages aren't suppressed. clearStickyStatus(); - extensions_ = registry_mgr_->extensions(); + extensions_ = registry_mgr_->compatibleExtensions(PlatformUtils::currentPlatform()); applyFilters(); setStatus("Ready — " + QString::number(extensions_.size()) + " extensions loaded"); }); diff --git a/pj_marketplace/tests/registry_manager_test.cpp b/pj_marketplace/tests/registry_manager_test.cpp index d760f61..1d03650 100644 --- a/pj_marketplace/tests/registry_manager_test.cpp +++ b/pj_marketplace/tests/registry_manager_test.cpp @@ -263,6 +263,48 @@ TEST_F(RegistryManagerTest, ParsesPlatformArtifacts) { EXPECT_EQ(platforms["windows-x86_64"].url, "https://example.com/csv-loader-win.dll"); } +// compatibleExtensions(platform) returns only entries whose `platforms` map +// contains the requested key, while extensions() stays untouched. +TEST_F(RegistryManagerTest, CompatibleExtensionsFiltersByRequestedPlatform) { + static const QByteArray kMixedPlatformsJson = R"({ + "extensions": [ + { "id": "linux-only", "name": "Linux Only", "version": "1.0.0", + "platforms": { "linux-x86_64": { "url": "u1", "checksum": "sha256:1" } } }, + { "id": "windows-only", "name": "Windows Only", "version": "1.0.0", + "platforms": { "windows-x86_64": { "url": "u2", "checksum": "sha256:2" } } }, + { "id": "cross", "name": "Cross", "version": "1.0.0", + "platforms": { + "linux-x86_64": { "url": "u3", "checksum": "sha256:3" }, + "windows-x86_64": { "url": "u4", "checksum": "sha256:4" } + } + } + ] + })"; + + RegistryManager mgr; + QSignalSpy spy_finished(&mgr, &RegistryManager::fetchFinished); + + server_->setResponseBody(kMixedPlatformsJson); + mgr.fetchRegistry(server_->url()); + ASSERT_TRUE(spy_finished.wait(3000)); + + // extensions() returns the parsed list verbatim, regardless of platform. + EXPECT_EQ(mgr.extensions().size(), 3); + + const QList linux_compat = mgr.compatibleExtensions("linux-x86_64"); + ASSERT_EQ(linux_compat.size(), 2); + EXPECT_EQ(linux_compat.at(0).id, "linux-only"); + EXPECT_EQ(linux_compat.at(1).id, "cross"); + + const QList win_compat = mgr.compatibleExtensions("windows-x86_64"); + ASSERT_EQ(win_compat.size(), 2); + EXPECT_EQ(win_compat.at(0).id, "windows-only"); + EXPECT_EQ(win_compat.at(1).id, "cross"); + + // Unknown platform key drops everything. + EXPECT_TRUE(mgr.compatibleExtensions("imaginary-os-arch").isEmpty()); +} + // [2] Changelog map (version -> description) TEST_F(RegistryManagerTest, ParsesChangelog) { RegistryManager mgr;