mirror of
https://github.com/logos-co/logos-liblogos.git
synced 2026-08-27 04:41:12 +00:00
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>
This commit is contained in:
co-authored by
Claude Opus 4.8
parent
66256b54ef
commit
050f2d3628
@@ -58,9 +58,9 @@ sections:
|
||||
> tested. With no pin it falls back to latest `master`.
|
||||
steps:
|
||||
- title: "Build the CLI with the liblogos override"
|
||||
run: "nix build 'github:logos-co/logos-logoscore-cli' --override-input logos-liblogos 'github:logos-co/logos-liblogos{release}' --out-link ./logos"
|
||||
run: "nix build 'github:logos-co/logos-logoscore-cli/b92ade06cdbd3cdf48c8de5b8375cbcc3a6088cf' --override-input logos-liblogos 'github:logos-co/logos-liblogos{release}' --out-link ./logos"
|
||||
code_block: |
|
||||
nix build 'github:logos-co/logos-logoscore-cli' \
|
||||
nix build 'github:logos-co/logos-logoscore-cli/b92ade06cdbd3cdf48c8de5b8375cbcc3a6088cf' \
|
||||
--override-input logos-liblogos 'github:logos-co/logos-liblogos' \
|
||||
--out-link ./logos
|
||||
check_file: "logos/bin/logoscore"
|
||||
|
||||
Generated
+187
-5305
File diff suppressed because it is too large
Load Diff
@@ -5,13 +5,18 @@
|
||||
logos-nix.url = "github:logos-co/logos-nix";
|
||||
nixpkgs.follows = "logos-nix/nixpkgs";
|
||||
logos-cpp-sdk.url = "github:logos-co/logos-cpp-sdk";
|
||||
logos-cpp-sdk.inputs.logos-protocol.follows = "logos-protocol";
|
||||
logos-protocol.url = "github:logos-co/logos-protocol";
|
||||
logos-qt-sdk.url = "github:logos-co/logos-qt-sdk";
|
||||
logos-qt-sdk.inputs.logos-protocol.follows = "logos-protocol";
|
||||
logos-qt-sdk.inputs.logos-cpp-sdk.follows = "logos-cpp-sdk";
|
||||
logos-capability-module.url = "github:logos-co/logos-capability-module";
|
||||
logos-module.url = "github:logos-co/logos-module";
|
||||
process-stats.url = "github:logos-co/process-stats";
|
||||
logos-package-manager.url = "github:logos-co/logos-package-manager";
|
||||
};
|
||||
|
||||
outputs = { self, nixpkgs, logos-nix, logos-cpp-sdk, logos-capability-module, logos-module, logos-package-manager, process-stats }:
|
||||
outputs = { self, nixpkgs, logos-nix, logos-cpp-sdk, logos-protocol, logos-qt-sdk, logos-capability-module, logos-module, logos-package-manager, process-stats }:
|
||||
|
||||
let
|
||||
systems = [ "aarch64-darwin" "x86_64-darwin" "aarch64-linux" "x86_64-linux" ];
|
||||
@@ -19,6 +24,8 @@
|
||||
inherit system;
|
||||
pkgs = import nixpkgs { inherit system; };
|
||||
logosSdk = logos-cpp-sdk.packages.${system}.default;
|
||||
logosProtocolPkg = logos-protocol.packages.${system}.default;
|
||||
logosQtSdk = logos-qt-sdk.packages.${system}.default;
|
||||
capabilityModule = logos-capability-module.packages.${system}.default;
|
||||
logosModule = logos-module.packages.${system}.default;
|
||||
processStats = process-stats.packages.${system}.default;
|
||||
@@ -27,12 +34,12 @@
|
||||
});
|
||||
in
|
||||
{
|
||||
packages = forAllSystems ({ pkgs, system, logosSdk, capabilityModule, logosModule, processStats, logosPackageManager, logosPackageManagerPortable }:
|
||||
packages = forAllSystems ({ pkgs, system, logosSdk, logosProtocolPkg, logosQtSdk, capabilityModule, logosModule, processStats, logosPackageManager, logosPackageManagerPortable }:
|
||||
let
|
||||
# Common configuration (dev, default)
|
||||
common = import ./nix/default.nix { inherit pkgs logosSdk logosModule processStats logosPackageManager; };
|
||||
common = import ./nix/default.nix { inherit pkgs logosSdk logosProtocolPkg logosQtSdk logosModule processStats logosPackageManager; };
|
||||
# Common configuration (portable)
|
||||
commonPortable = import ./nix/default.nix { inherit pkgs logosSdk logosModule processStats; logosPackageManager = logosPackageManagerPortable; portableBuild = true; };
|
||||
commonPortable = import ./nix/default.nix { inherit pkgs logosSdk logosProtocolPkg logosQtSdk logosModule processStats; logosPackageManager = logosPackageManagerPortable; portableBuild = true; };
|
||||
src = ./.;
|
||||
|
||||
# Shared build that compiles everything (dev)
|
||||
@@ -46,13 +53,13 @@
|
||||
modules = import ./nix/modules.nix { inherit pkgs common capabilityModule; };
|
||||
modulesPortable = import ./nix/modules.nix { inherit pkgs capabilityModule; common = commonPortable; portableBuild = true; };
|
||||
bin = import ./nix/bin.nix { inherit pkgs common build lib modules; };
|
||||
include = import ./nix/include.nix { inherit pkgs common src logosSdk; };
|
||||
include = import ./nix/include.nix { inherit pkgs common src logosSdk; inherit logosProtocolPkg logosQtSdk; };
|
||||
tests = import ./nix/tests.nix { inherit pkgs common build; };
|
||||
|
||||
# Portable package components
|
||||
libPortable = import ./nix/lib.nix { inherit pkgs; common = commonPortable; build = buildPortable; };
|
||||
binPortable = import ./nix/bin.nix { inherit pkgs; common = commonPortable; build = buildPortable; lib = libPortable; modules = modulesPortable; };
|
||||
includePortable = import ./nix/include.nix { inherit pkgs src logosSdk; common = commonPortable; };
|
||||
includePortable = import ./nix/include.nix { inherit pkgs src logosSdk; inherit logosProtocolPkg logosQtSdk; common = commonPortable; };
|
||||
|
||||
# Combined package (dev)
|
||||
liblogos = pkgs.symlinkJoin {
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
# Common build configuration shared across all packages
|
||||
{ pkgs, logosSdk, logosModule, processStats, logosPackageManager, portableBuild ? false }:
|
||||
{ pkgs, logosSdk, logosProtocolPkg, logosQtSdk, logosModule, processStats, logosPackageManager, portableBuild ? false }:
|
||||
|
||||
{
|
||||
pname = "logos-liblogos";
|
||||
@@ -28,6 +28,8 @@
|
||||
pkgs.qt6.qtbase
|
||||
pkgs.qt6.qtremoteobjects
|
||||
logosSdk
|
||||
logosProtocolPkg
|
||||
logosQtSdk
|
||||
pkgs.zstd
|
||||
pkgs.gtest
|
||||
pkgs.cli11
|
||||
@@ -41,6 +43,8 @@
|
||||
cmakeFlags = [
|
||||
"-GNinja"
|
||||
"-DLOGOS_CPP_SDK_ROOT=${logosSdk}"
|
||||
"-DLOGOS_PROTOCOL_ROOT=${logosProtocolPkg}"
|
||||
"-DLOGOS_QT_SDK_ROOT=${logosQtSdk}"
|
||||
"-DLOGOS_MODULE_ROOT=${logosModule}"
|
||||
"-DPROCESS_STATS_ROOT=${processStats}"
|
||||
"-DLOGOS_PACKAGE_MANAGER_ROOT=${logosPackageManager}"
|
||||
@@ -51,6 +55,8 @@
|
||||
# Environment variables
|
||||
env = {
|
||||
LOGOS_CPP_SDK_ROOT = "${logosSdk}";
|
||||
LOGOS_PROTOCOL_ROOT = "${logosProtocolPkg}";
|
||||
LOGOS_QT_SDK_ROOT = "${logosQtSdk}";
|
||||
LOGOS_MODULE_ROOT = "${logosModule}";
|
||||
PROCESS_STATS_ROOT = "${processStats}";
|
||||
LOGOS_PACKAGE_MANAGER_ROOT = "${logosPackageManager}";
|
||||
|
||||
+7
-1
@@ -1,5 +1,5 @@
|
||||
# Installs the logos-liblogos headers
|
||||
{ pkgs, common, src, logosSdk ? null }:
|
||||
{ pkgs, common, src, logosSdk ? null, logosProtocolPkg ? null, logosQtSdk ? null }:
|
||||
|
||||
pkgs.stdenv.mkDerivation {
|
||||
pname = "${common.pname}-headers";
|
||||
@@ -27,6 +27,12 @@ pkgs.stdenv.mkDerivation {
|
||||
if [ -n "${toString logosSdk}" ] && [ -d "${toString logosSdk}/include" ]; then
|
||||
cp -r ${toString logosSdk}/include/* $out/include/ 2>/dev/null || true
|
||||
fi
|
||||
if [ -n "${toString logosProtocolPkg}" ] && [ -d "${toString logosProtocolPkg}/include" ]; then
|
||||
cp -r ${toString logosProtocolPkg}/include/* $out/include/ 2>/dev/null || true
|
||||
fi
|
||||
if [ -n "${toString logosQtSdk}" ] && [ -d "${toString logosQtSdk}/include" ]; then
|
||||
cp -r ${toString logosQtSdk}/include/* $out/include/ 2>/dev/null || true
|
||||
fi
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
@@ -28,6 +28,8 @@ pkgs.stdenv.mkDerivation {
|
||||
cmake -B build -S ${build.src} \
|
||||
-GNinja \
|
||||
-DLOGOS_CPP_SDK_ROOT=${common.env.LOGOS_CPP_SDK_ROOT} \
|
||||
-DLOGOS_PROTOCOL_ROOT=${common.env.LOGOS_PROTOCOL_ROOT} \
|
||||
-DLOGOS_QT_SDK_ROOT=${common.env.LOGOS_QT_SDK_ROOT} \
|
||||
-DLOGOS_MODULE_ROOT=${common.env.LOGOS_MODULE_ROOT} \
|
||||
-DPROCESS_STATS_ROOT=${common.env.PROCESS_STATS_ROOT} \
|
||||
-DLOGOS_PACKAGE_MANAGER_ROOT=${common.env.LOGOS_PACKAGE_MANAGER_ROOT} \
|
||||
|
||||
+35
-20
@@ -7,30 +7,45 @@ endif()
|
||||
|
||||
get_filename_component(LOGOS_CPP_SDK_ROOT "${LOGOS_CPP_SDK_ROOT}" ABSOLUTE)
|
||||
|
||||
# Check if we have a built SDK package (with lib and include directories)
|
||||
if(EXISTS "${LOGOS_CPP_SDK_ROOT}/lib" AND EXISTS "${LOGOS_CPP_SDK_ROOT}/include")
|
||||
# Use the built SDK package via its CMake Config file. The SDK ships
|
||||
# logos-cpp-sdkConfig.cmake under lib/cmake/logos-cpp-sdk/ whose
|
||||
# imported target (logos-cpp-sdk::logos_sdk) carries OpenSSL / Boost
|
||||
# / nlohmann_json / Qt on its INTERFACE_LINK_LIBRARIES — no manual
|
||||
# wiring required here.
|
||||
message(STATUS "Using built logos-cpp-sdk at ${LOGOS_CPP_SDK_ROOT}")
|
||||
find_package(logos-cpp-sdk REQUIRED
|
||||
PATHS "${LOGOS_CPP_SDK_ROOT}" NO_DEFAULT_PATH)
|
||||
# Provide a `logos_sdk` alias so the rest of this CMakeLists keeps
|
||||
# using the unqualified name (matches the source-layout behaviour
|
||||
# where `logos_sdk` is the target name from add_library).
|
||||
# The SDK stack since the Qt split: logos-protocol (transports + lp_* C
|
||||
# ABI), logos-qt-sdk (LogosAPI + provider glue — what this runtime's Qt
|
||||
# side uses), logos-cpp-sdk (Qt-free std headers). The `logos_sdk` alias
|
||||
# is kept so the rest of this file (and the host targets) stay unchanged;
|
||||
# it now points at logos-qt-sdk, whose link interface chains
|
||||
# logos-protocol and its OpenSSL/Boost/nlohmann deps.
|
||||
if(NOT DEFINED LOGOS_PROTOCOL_ROOT AND DEFINED ENV{LOGOS_PROTOCOL_ROOT})
|
||||
set(LOGOS_PROTOCOL_ROOT "$ENV{LOGOS_PROTOCOL_ROOT}")
|
||||
endif()
|
||||
if(NOT DEFINED LOGOS_QT_SDK_ROOT AND DEFINED ENV{LOGOS_QT_SDK_ROOT})
|
||||
set(LOGOS_QT_SDK_ROOT "$ENV{LOGOS_QT_SDK_ROOT}")
|
||||
endif()
|
||||
|
||||
if(EXISTS "${LOGOS_PROTOCOL_ROOT}/lib/cmake/logos-protocol"
|
||||
AND EXISTS "${LOGOS_QT_SDK_ROOT}/lib/cmake/logos-qt-sdk")
|
||||
message(STATUS "Using logos-protocol at ${LOGOS_PROTOCOL_ROOT}")
|
||||
message(STATUS "Using logos-qt-sdk at ${LOGOS_QT_SDK_ROOT}")
|
||||
find_package(logos-protocol REQUIRED
|
||||
PATHS "${LOGOS_PROTOCOL_ROOT}/lib/cmake/logos-protocol" NO_DEFAULT_PATH)
|
||||
find_package(logos-qt-sdk REQUIRED
|
||||
PATHS "${LOGOS_QT_SDK_ROOT}/lib/cmake/logos-qt-sdk" NO_DEFAULT_PATH)
|
||||
if(NOT TARGET logos_sdk)
|
||||
add_library(logos_sdk INTERFACE IMPORTED)
|
||||
target_link_libraries(logos_sdk INTERFACE logos-cpp-sdk::logos_sdk)
|
||||
target_link_libraries(logos_sdk INTERFACE logos-qt-sdk::logos_qt_sdk)
|
||||
endif()
|
||||
|
||||
elseif(EXISTS "${LOGOS_CPP_SDK_ROOT}/cpp/CMakeLists.txt")
|
||||
# Use the source SDK (original behavior)
|
||||
message(STATUS "Using logos-cpp-sdk source at ${LOGOS_CPP_SDK_ROOT}")
|
||||
add_subdirectory(${LOGOS_CPP_SDK_ROOT}/cpp ${CMAKE_BINARY_DIR}/sdk)
|
||||
else()
|
||||
message(FATAL_ERROR "logos-cpp-sdk not found at ${LOGOS_CPP_SDK_ROOT}")
|
||||
message(FATAL_ERROR "logos-protocol / logos-qt-sdk not found. Set "
|
||||
"LOGOS_PROTOCOL_ROOT and LOGOS_QT_SDK_ROOT to built packages.")
|
||||
endif()
|
||||
|
||||
# Qt-free base SDK headers (logos_module_context.h etc.) — header-only.
|
||||
if(EXISTS "${LOGOS_CPP_SDK_ROOT}/lib/cmake/logos-cpp-sdk")
|
||||
find_package(logos-cpp-sdk REQUIRED
|
||||
PATHS "${LOGOS_CPP_SDK_ROOT}/lib/cmake/logos-cpp-sdk" NO_DEFAULT_PATH)
|
||||
if(NOT TARGET logos_headers)
|
||||
add_library(logos_headers INTERFACE IMPORTED)
|
||||
target_link_libraries(logos_headers INTERFACE logos-cpp-sdk::logos_headers)
|
||||
endif()
|
||||
target_link_libraries(logos_sdk INTERFACE logos-cpp-sdk::logos_headers)
|
||||
endif()
|
||||
|
||||
# Find logos-module library
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
#include <boost/uuid/uuid_io.hpp>
|
||||
#include "logos_api.h"
|
||||
#include "logos_api_client.h"
|
||||
#include "logos_module.h"
|
||||
#include "logos_protocol.h"
|
||||
#include "protocol_gate.h"
|
||||
#include "logos_transport_config_json.h"
|
||||
#include "token_manager.h"
|
||||
#include "instance_persistence.h"
|
||||
@@ -261,6 +264,46 @@ namespace {
|
||||
desc.transportSetJson = it->second;
|
||||
}
|
||||
|
||||
// ── Protocol-version load gate ─────────────────────────────────
|
||||
// Read the module's embedded metadata without loading it and apply
|
||||
// the one compatibility rule: equal logos-protocol MAJOR loads,
|
||||
// different MAJOR is refused, a missing stamp (pre-protocol module)
|
||||
// loads permissively with a warning.
|
||||
std::string moduleProtocolVersion;
|
||||
if (auto meta = ModuleLib::LogosModule::extractMetadata(modPath)) {
|
||||
// While we have it, hand the full metadata to the runtime.
|
||||
desc.rawMetadata = nlohmann::json::parse(
|
||||
meta->rawMetadataJson, nullptr, /*allow_exceptions=*/false);
|
||||
if (desc.rawMetadata.is_discarded())
|
||||
desc.rawMetadata = nlohmann::json::object();
|
||||
if (auto it = desc.rawMetadata.find("logos_protocol_version");
|
||||
it != desc.rawMetadata.end() && it->is_string())
|
||||
moduleProtocolVersion = it->get<std::string>();
|
||||
}
|
||||
const auto gate = LogosCore::evaluateProtocolGate(
|
||||
moduleProtocolVersion, LOGOS_PROTOCOL_VERSION_MAJOR);
|
||||
switch (gate.decision) {
|
||||
case LogosCore::ProtocolGateDecision::Refuse:
|
||||
spdlog::error(
|
||||
"Refusing to load module {}: built against logos-protocol {} "
|
||||
"(major {}), this host speaks major {} ({}) — incompatible "
|
||||
"protocol majors",
|
||||
name, moduleProtocolVersion, gate.moduleMajor,
|
||||
LOGOS_PROTOCOL_VERSION_MAJOR, LOGOS_PROTOCOL_VERSION_STRING);
|
||||
return false;
|
||||
case LogosCore::ProtocolGateDecision::AllowLegacy:
|
||||
spdlog::warn(
|
||||
"Module {} carries no usable logos_protocol_version "
|
||||
"(pre-protocol build) — loading permissively",
|
||||
name);
|
||||
break;
|
||||
case LogosCore::ProtocolGateDecision::Allow:
|
||||
spdlog::debug("Module {} protocol version {} compatible with host {}",
|
||||
name, moduleProtocolVersion,
|
||||
LOGOS_PROTOCOL_VERSION_STRING);
|
||||
break;
|
||||
}
|
||||
|
||||
auto rt = runtimeRegistry().select(desc);
|
||||
if (!rt) {
|
||||
spdlog::warn("No runtime available to load module: {}", name);
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
#ifndef LOGOS_PROTOCOL_GATE_H
|
||||
#define LOGOS_PROTOCOL_GATE_H
|
||||
|
||||
#include <cstdlib>
|
||||
#include <string>
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Protocol-version load gate (pure decision logic, std-only).
|
||||
//
|
||||
// Every module built by a current logos-module-builder carries the
|
||||
// logos-protocol semver it was compiled against in its embedded metadata
|
||||
// (`logos_protocol_version`). One rule governs Logos load/call
|
||||
// compatibility: two participants interoperate iff they share the same
|
||||
// protocol MAJOR. The host (this library) refuses to load a module whose
|
||||
// major differs from its own; modules with no stamp predate the scheme and
|
||||
// load permissively ("legacy") so the existing fleet never hard-fails.
|
||||
//
|
||||
// The caller (ModuleManager::loadModuleInternal) extracts the stamp from the
|
||||
// plugin metadata pre-load and logs according to the decision.
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
namespace LogosCore {
|
||||
|
||||
enum class ProtocolGateDecision {
|
||||
Allow, // same major — compatible
|
||||
AllowLegacy, // no/unparseable stamp — pre-protocol module, load + warn
|
||||
Refuse, // different major — incompatible, do not load
|
||||
};
|
||||
|
||||
struct ProtocolGateResult {
|
||||
ProtocolGateDecision decision;
|
||||
int moduleMajor = -1; // -1 when absent/unparseable
|
||||
};
|
||||
|
||||
// Parse the MAJOR component of "MAJOR.MINOR.PATCH". Returns -1 when the
|
||||
// string does not start with a non-negative integer.
|
||||
inline int protocolVersionMajor(const std::string& version)
|
||||
{
|
||||
if (version.empty()) return -1;
|
||||
char* end = nullptr;
|
||||
const long major = std::strtol(version.c_str(), &end, 10);
|
||||
if (end == version.c_str() || major < 0) return -1;
|
||||
return static_cast<int>(major);
|
||||
}
|
||||
|
||||
inline ProtocolGateResult evaluateProtocolGate(const std::string& moduleVersion,
|
||||
int hostMajor)
|
||||
{
|
||||
const int moduleMajor = protocolVersionMajor(moduleVersion);
|
||||
if (moduleMajor < 0)
|
||||
return {ProtocolGateDecision::AllowLegacy, -1};
|
||||
if (moduleMajor == hostMajor)
|
||||
return {ProtocolGateDecision::Allow, moduleMajor};
|
||||
return {ProtocolGateDecision::Refuse, moduleMajor};
|
||||
}
|
||||
|
||||
} // namespace LogosCore
|
||||
|
||||
#endif // LOGOS_PROTOCOL_GATE_H
|
||||
@@ -75,6 +75,14 @@ LogosAPI* initializeLogosAPI(const std::string& moduleName, QObject* module,
|
||||
fs::path(instancePersistencePath).filename().string());
|
||||
}
|
||||
|
||||
// Surface the token as a QObject property BEFORE registerObject:
|
||||
// registration runs the provider object's init(), where cdylib-authored
|
||||
// modules read this property (a cross-image-safe dynamic lookup, like
|
||||
// modulePath above) and forward it across the module-impl C ABI via
|
||||
// logos_module_accept_token — their statically-linked protocol stack has
|
||||
// its own TokenManager copy the host's saveToken calls below never reach.
|
||||
logos_api->setProperty("authToken", QString::fromStdString(authToken));
|
||||
|
||||
bool success = logos_api->getProvider()->registerObject(basePlugin->name(), module);
|
||||
if (success) {
|
||||
logos_api->getTokenManager()->saveToken(std::string("core"), authToken);
|
||||
|
||||
+17
-3
@@ -21,6 +21,7 @@ add_executable(logos_core_tests
|
||||
test_module_name_validation.cpp
|
||||
test_runtime_registry.cpp
|
||||
test_module_runtime_abstraction.cpp
|
||||
test_protocol_gate.cpp
|
||||
# qt_test_adapter.h calls SubprocessTokenReceiver::receive; compile the
|
||||
# implementation unit directly into the test binary (it lives in logos_host_qt,
|
||||
# which is an executable, not a linkable library).
|
||||
@@ -54,15 +55,28 @@ if(DEFINED PROCESS_STATS_ROOT AND EXISTS "${PROCESS_STATS_ROOT}/include")
|
||||
target_include_directories(logos_core_tests PRIVATE ${PROCESS_STATS_ROOT}/include)
|
||||
endif()
|
||||
|
||||
# Add SDK include directory (same logic as src/CMakeLists.txt)
|
||||
if(EXISTS "${LOGOS_CPP_SDK_ROOT}/lib" AND EXISTS "${LOGOS_CPP_SDK_ROOT}/include")
|
||||
# Add SDK include directories (same layering as src/CMakeLists.txt:
|
||||
# Qt-free base headers + qt-sdk developer layer + protocol layer)
|
||||
if(EXISTS "${LOGOS_CPP_SDK_ROOT}/include")
|
||||
target_include_directories(logos_core_tests PRIVATE
|
||||
${LOGOS_CPP_SDK_ROOT}/include
|
||||
${LOGOS_CPP_SDK_ROOT}/include/cpp
|
||||
${LOGOS_CPP_SDK_ROOT}/include/core
|
||||
)
|
||||
else()
|
||||
target_include_directories(logos_core_tests PRIVATE ${LOGOS_CPP_SDK_ROOT}/cpp)
|
||||
endif()
|
||||
if(DEFINED LOGOS_QT_SDK_ROOT AND EXISTS "${LOGOS_QT_SDK_ROOT}/include")
|
||||
target_include_directories(logos_core_tests PRIVATE
|
||||
${LOGOS_QT_SDK_ROOT}/include
|
||||
${LOGOS_QT_SDK_ROOT}/include/cpp
|
||||
${LOGOS_QT_SDK_ROOT}/include/core
|
||||
)
|
||||
endif()
|
||||
if(DEFINED LOGOS_PROTOCOL_ROOT AND EXISTS "${LOGOS_PROTOCOL_ROOT}/include")
|
||||
target_include_directories(logos_core_tests PRIVATE
|
||||
${LOGOS_PROTOCOL_ROOT}/include
|
||||
${LOGOS_PROTOCOL_ROOT}/include/cpp
|
||||
)
|
||||
endif()
|
||||
|
||||
gtest_discover_tests(logos_core_tests)
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
#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);
|
||||
}
|
||||
Reference in New Issue
Block a user