Files
logos-liblogos/tests/test_protocol_gate.cpp
Dario LipicarandClaude Opus 4.8 050f2d3628 Qt-split retarget + logos_protocol_version load gate (#142)
* Qt-split retarget + protocol-version load gate

- Link the split SDK stack: logos-qt-sdk (LogosAPI/provider glue; the
  logos_sdk alias now points at logos-qt-sdk::logos_qt_sdk, chaining
  logos-protocol) + Qt-free logos-cpp-sdk headers.
- Protocol-version load gate (the first real consumer of module
  metadata pre-load): ModuleManager reads the module's embedded
  logos_protocol_version before runtime.load() and applies the one
  compatibility rule — equal protocol MAJOR loads, different MAJOR is
  refused with a diagnostic naming both versions, missing/unparseable
  stamp (pre-protocol modules) loads permissively with a warning. The
  decision logic is std-only (logos_core/protocol_gate.h) and unit
  tested (refuse bumped major / warn-load legacy / silent minor skew).
- ModuleDescriptor.rawMetadata is now actually populated for runtimes.

* lock: pin extraction-chain branch revs for standalone CI

Temporary — drop when the chain PRs merge (re-lock against masters).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* doctest: pin logoscore-cli to its qt-split branch head

The doc-test builds logoscore-cli at latest master with only liblogos
overridden to the commit under test; master logoscore-cli cannot build
against qt-split liblogos. Pin the runtime to the chain branch
(logos-co/logos-logoscore-cli#43) so the doc-test exercises the
coherent stack. Temporary — revert to the unpinned URL when the chain
merges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* host: surface the spawn auth token as a LogosAPI property

cdylib-authored modules run their own statically-linked protocol stack
whose TokenManager is a separate copy of the singleton; the generated Qt
glue reads this property (cross-image-safe, like modulePath) and seeds
the cdylib's stack via logos_module_accept_token so the module's
outbound calls authenticate.

* host: set the authToken property before registerObject

registerObject runs the provider object's init() — where the cdylib glue
reads the property. Setting it afterwards meant cdylib modules always saw
an empty token.

* lock: protocol+cpp-sdk merged to master — pins advance (protocol 9de4165, cpp-sdk f0fe8cb, qt-sdk 722e590)

* lock: qt-sdk#1 merged — pin advances to qt-sdk master

* gate: drop QJson from the Qt-free core — parse rawMetadataJson with nlohmann

The protocol-version load gate had pulled QJsonDocument/QJsonObject into
src/logos_core (Qt-free territory). logos-module now exposes the embedded
metadata as a compact JSON string, so the gate reads it via nlohmann and
the std::string extractMetadata overload.

* lock: logos-module b42805d (result-lm untracked)

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 21:45:49 -03:00

70 lines
2.4 KiB
C++

#include <gtest/gtest.h>
#include "logos_core/protocol_gate.h"
#include "logos_protocol.h"
// Gate tests (land with the gate, per the versioning design):
// (a) a module stamped with a different MAJOR is refused,
// (b) an unstamped (legacy) module is allowed permissively,
// (c) equal major / different minor loads silently.
// The stamp values are exercised against both a fixed major and the real
// LOGOS_PROTOCOL_VERSION_MAJOR the host links.
using LogosCore::evaluateProtocolGate;
using LogosCore::protocolVersionMajor;
using LogosCore::ProtocolGateDecision;
TEST(ProtocolGate, RefusesDifferentMajor)
{
const auto r = evaluateProtocolGate("2.0.0", 1);
EXPECT_EQ(r.decision, ProtocolGateDecision::Refuse);
EXPECT_EQ(r.moduleMajor, 2);
// Against the real host major: a deliberately bumped stamp is refused.
const int hostMajor = LOGOS_PROTOCOL_VERSION_MAJOR;
const std::string bumped = std::to_string(hostMajor + 1) + ".0.0";
EXPECT_EQ(evaluateProtocolGate(bumped, hostMajor).decision,
ProtocolGateDecision::Refuse);
}
TEST(ProtocolGate, MissingStampLoadsPermissively)
{
const auto r = evaluateProtocolGate("", LOGOS_PROTOCOL_VERSION_MAJOR);
EXPECT_EQ(r.decision, ProtocolGateDecision::AllowLegacy);
EXPECT_EQ(r.moduleMajor, -1);
}
TEST(ProtocolGate, UnparseableStampLoadsPermissively)
{
EXPECT_EQ(evaluateProtocolGate("garbage", 1).decision,
ProtocolGateDecision::AllowLegacy);
EXPECT_EQ(evaluateProtocolGate("-1.0.0", 1).decision,
ProtocolGateDecision::AllowLegacy);
}
TEST(ProtocolGate, EqualMajorDifferentMinorAllowsSilently)
{
const int hostMajor = LOGOS_PROTOCOL_VERSION_MAJOR;
const std::string newerMinor =
std::to_string(hostMajor) + ".99.7";
const auto r = evaluateProtocolGate(newerMinor, hostMajor);
EXPECT_EQ(r.decision, ProtocolGateDecision::Allow);
EXPECT_EQ(r.moduleMajor, hostMajor);
}
TEST(ProtocolGate, HostOwnVersionAllows)
{
EXPECT_EQ(evaluateProtocolGate(LOGOS_PROTOCOL_VERSION_STRING,
LOGOS_PROTOCOL_VERSION_MAJOR).decision,
ProtocolGateDecision::Allow);
}
TEST(ProtocolGate, MajorParser)
{
EXPECT_EQ(protocolVersionMajor("0.1.0"), 0);
EXPECT_EQ(protocolVersionMajor("12.3.4"), 12);
EXPECT_EQ(protocolVersionMajor("1"), 1);
EXPECT_EQ(protocolVersionMajor(""), -1);
EXPECT_EQ(protocolVersionMajor("x.y.z"), -1);
}