mirror of
https://github.com/logos-co/logos-app.git
synced 2026-08-30 19:01:09 +00:00
PluginLoader::loadCoreDependencies read each `dependencies[]` entry as
QString depName = dep.toString();
if (depName.isEmpty()) continue;
QVariant::toString() on a QVariantMap returns a NULL QString — no
diagnostic, no exception. So the moment the module ABI is widened to send
the object form {"name": …, "version": "^2.0.0", "signer": "did:…"} that
the LGX spec already allows and lgpm already parses, every CONSTRAINED
core dependency is silently skipped and the ui plugin mounts on top of an
unloaded dependency.
This commit changes no behaviour. It gives that decision a name
(logos::readDependencyEntry) and a test, so the next commit's fix is
visible as a diff in outcomes rather than a diff in expressions.
Red, as expected — 5 of 14 cases fail against the extracted behaviour:
FAIL! : object_entry_yields_its_name() kind 1, want 0
FAIL! : object_entry_without_constraints_yields_its_name() kind 1, want 0
FAIL! : json_object_entry_yields_its_name() kind 1, want 0
FAIL! : hash_object_entry_yields_its_name() kind 1, want 0
FAIL! : a_number_is_unrecognised() kind 0, want 1
(1 = Unrecognised, i.e. skipped. The last one is the mirror image: a
non-string scalar is currently stringified and loaded as a module name.)
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
150 lines
5.2 KiB
C++
150 lines
5.2 KiB
C++
#include <QtTest/QtTest>
|
|
|
|
#include <QJsonObject>
|
|
#include <QVariantHash>
|
|
#include <QVariantMap>
|
|
|
|
#include "utils/DependencyEntry.h"
|
|
|
|
using logos::DependencyEntryKind;
|
|
using logos::readDependencyEntry;
|
|
|
|
// A manifest `dependencies[]` entry is either a bare name ("wallet_module")
|
|
// or an object carrying that name alongside the constraints an installer
|
|
// resolves it by: {"name": …, "version": "^2.0.0", "signer": "did:jwk:…"}.
|
|
// Both forms declare THE SAME EDGE, so both must yield the same name here.
|
|
//
|
|
// The trap: QVariant::toString() on a QVariantMap returns a NULL QString, not
|
|
// a diagnostic. A reader written as
|
|
//
|
|
// QString depName = dep.toString();
|
|
// if (depName.isEmpty()) continue;
|
|
//
|
|
// therefore drops every object-form entry in total silence — no error, no log
|
|
// line — and the plugin mounts with an unloaded dependency.
|
|
class DependencyEntryTest : public QObject {
|
|
Q_OBJECT
|
|
|
|
private slots:
|
|
// ── The Qt semantics this whole file exists for ─────────────────────
|
|
void toString_on_an_object_entry_is_null_not_a_diagnostic()
|
|
{
|
|
QVariantMap constrained;
|
|
constrained.insert("name", "wallet_module");
|
|
constrained.insert("version", "^2.0.0");
|
|
constrained.insert("signer", "did:jwk:eyJrdHkiOiJPS1AifQ");
|
|
|
|
// This is why a bare toString() reader cannot be trusted: the call
|
|
// succeeds, returns nothing, and reports nothing.
|
|
QVERIFY(QVariant(constrained).toString().isNull());
|
|
}
|
|
|
|
// ── Bare-name entries: the shape the ABI sends today ────────────────
|
|
void bare_name_yields_that_name()
|
|
{
|
|
const auto e = readDependencyEntry(QVariant(QStringLiteral("wallet_module")));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Name);
|
|
QCOMPARE(e.name, QStringLiteral("wallet_module"));
|
|
}
|
|
|
|
// ── Object entries: the shape the ABI will send once widened ────────
|
|
void object_entry_yields_its_name()
|
|
{
|
|
QVariantMap constrained;
|
|
constrained.insert("name", "wallet_module");
|
|
constrained.insert("version", "^2.0.0");
|
|
constrained.insert("signer", "did:jwk:eyJrdHkiOiJPS1AifQ");
|
|
|
|
const auto e = readDependencyEntry(QVariant(constrained));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Name);
|
|
QCOMPARE(e.name, QStringLiteral("wallet_module"));
|
|
}
|
|
|
|
void object_entry_without_constraints_yields_its_name()
|
|
{
|
|
QVariantMap bare;
|
|
bare.insert("name", "libp2p_module");
|
|
|
|
const auto e = readDependencyEntry(QVariant(bare));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Name);
|
|
QCOMPARE(e.name, QStringLiteral("libp2p_module"));
|
|
}
|
|
|
|
// The IPC layer hands us QVariantMap, but a caller that went through
|
|
// QJsonDocument directly holds a QJsonObject. Same entry, same answer.
|
|
void json_object_entry_yields_its_name()
|
|
{
|
|
QJsonObject o;
|
|
o.insert("name", "chat_module");
|
|
o.insert("version", ">=1.2.0 <2.0.0");
|
|
|
|
const auto e = readDependencyEntry(QVariant(o));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Name);
|
|
QCOMPARE(e.name, QStringLiteral("chat_module"));
|
|
}
|
|
|
|
void hash_object_entry_yields_its_name()
|
|
{
|
|
QVariantHash h;
|
|
h.insert("name", "waku_module");
|
|
|
|
const auto e = readDependencyEntry(QVariant(h));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Name);
|
|
QCOMPARE(e.name, QStringLiteral("waku_module"));
|
|
}
|
|
|
|
// ── Everything we cannot name is Unrecognised, never a quiet skip ───
|
|
void object_without_a_name_is_unrecognised()
|
|
{
|
|
QVariantMap noName;
|
|
noName.insert("version", "^2.0.0");
|
|
|
|
const auto e = readDependencyEntry(QVariant(noName));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Unrecognised);
|
|
QVERIFY(e.name.isEmpty());
|
|
}
|
|
|
|
void object_with_an_empty_name_is_unrecognised()
|
|
{
|
|
QVariantMap emptyName;
|
|
emptyName.insert("name", "");
|
|
emptyName.insert("version", "^2.0.0");
|
|
|
|
const auto e = readDependencyEntry(QVariant(emptyName));
|
|
QCOMPARE(e.kind, DependencyEntryKind::Unrecognised);
|
|
}
|
|
|
|
void empty_string_is_unrecognised()
|
|
{
|
|
QCOMPARE(readDependencyEntry(QVariant(QString())).kind,
|
|
DependencyEntryKind::Unrecognised);
|
|
QCOMPARE(readDependencyEntry(QVariant(QStringLiteral(""))).kind,
|
|
DependencyEntryKind::Unrecognised);
|
|
}
|
|
|
|
void a_nested_list_is_unrecognised()
|
|
{
|
|
const QVariantList nested{QStringLiteral("wallet_module")};
|
|
QCOMPARE(readDependencyEntry(QVariant(nested)).kind,
|
|
DependencyEntryKind::Unrecognised);
|
|
}
|
|
|
|
void a_number_is_unrecognised()
|
|
{
|
|
// Not "42": a dependencies[] entry is a name or an object, and a
|
|
// number is neither. Reporting it beats silently loading a module
|
|
// called "42" and beats silently loading nothing.
|
|
QCOMPARE(readDependencyEntry(QVariant(42)).kind,
|
|
DependencyEntryKind::Unrecognised);
|
|
}
|
|
|
|
void an_invalid_variant_is_unrecognised()
|
|
{
|
|
QCOMPARE(readDependencyEntry(QVariant()).kind,
|
|
DependencyEntryKind::Unrecognised);
|
|
}
|
|
};
|
|
|
|
QTEST_MAIN(DependencyEntryTest)
|
|
#include "dependency_entry_test.moc"
|