Files
EVEngine/test/policyregistry.cpp
T

209 lines
9.8 KiB
C++

#include "zeroerr/assert.h"
#include "zeroerr/unittest.h"
#include "common/Module.h"
#include "policyregistry/PolicyRegistry.h"
#include <simplesquirrel/simplesquirrel.hpp>
#include <stdexcept>
#include <string>
#include <string_view>
#include <vector>
using namespace eve::policyregistry;
TEST_CASE("policyregistry.registersCanonicalDescriptors") {
PolicyRegistry registry;
REQUIRE(registry.insert("movement", "ground", 2, 10, true, "builtin", "move.schema", "{\"z\":2,\"a\":1}").ok());
auto policy = registry.resolve("movement", "ground");
REQUIRE(policy.ok());
const auto& descriptor = policy.value().get();
CHECK_EQ(descriptor.version.value(), uint64_t{2});
CHECK_EQ(descriptor.priority, 10);
CHECK(descriptor.enabled);
CHECK_EQ(implementationKindName(descriptor.kind), std::string("builtin"));
CHECK_EQ(descriptor.metadataJson, std::string("{\"a\":1,\"z\":2}"));
CHECK(!registry.insert("movement", "ground", 1, 0, true, "script", "", "{}").ok());
CHECK(!registry.insert("", "bad", 1, 0, true, "script", "", "{}").ok());
CHECK(!registry.insert("movement", "bad", 0, 0, true, "script", "", "{}").ok());
CHECK(!registry.insert("movement", "bad", 1, 0, true, "remote", "", "{}").ok());
CHECK(!registry.insert("movement", "bad", 1, 0, true, "script", "", "[]").ok());
}
TEST_CASE("policyregistry.selectsEnabledPriorityThenName") {
PolicyRegistry registry;
REQUIRE(registry.insert("targeting", "zeta", 1, 20, true, "script", "", "{}").ok());
REQUIRE(registry.insert("targeting", "alpha", 1, 20, true, "batch", "", "{}").ok());
REQUIRE(registry.insert("targeting", "disabled", 1, 99, false, "builtin", "", "{}").ok());
REQUIRE(registry.select("targeting") != nullptr);
CHECK_EQ(registry.select("targeting")->name, std::string("alpha"));
REQUIRE(registry.enable("targeting", "disabled", true).ok());
CHECK_EQ(registry.select("targeting")->name, std::string("disabled"));
REQUIRE(registry.enable("targeting", "disabled", false).ok());
CHECK_EQ(registry.select("targeting")->name, std::string("alpha"));
CHECK(registry.select("missing") == nullptr);
}
TEST_CASE("policyregistry.generationsInvalidateStaleHandles") {
PolicyRegistry registry;
REQUIRE(registry.insert("economy", "tax", 1, 0, true, "script", "tax.v1", "{}").ok());
auto oldResult = registry.handle("economy", "tax");
REQUIRE(oldResult.ok());
const auto old = std::move(oldResult).takeValue();
REQUIRE(registry.resolveHandle(old).ok());
REQUIRE(registry.replace("economy", "tax", 2, 1, true, "batch", "tax.v2", "{}").ok());
CHECK(!registry.resolveHandle(old).ok());
auto current = registry.resolve("economy", "tax");
REQUIRE(current.ok());
CHECK_EQ(current.value().get().generation.value(), uint64_t{2});
REQUIRE(registry.remove("economy", "tax").ok());
REQUIRE(registry.insert("economy", "tax", 3, 2, true, "script", "tax.v3", "{}").ok());
current = registry.resolve("economy", "tax");
REQUIRE(current.ok());
CHECK_EQ(current.value().get().generation.value(), uint64_t{4});
}
TEST_CASE("policyregistry.registryParityKeepsTombstones") {
PolicyRegistry registry;
auto inserted = registry.insert("movement", "ground", 1, 2, true, "builtin", "", "{}");
REQUIRE(inserted.ok());
const auto first = std::move(inserted).takeValue();
auto disabled = registry.enable("movement", "ground", false);
REQUIRE(disabled.ok());
const auto second = std::move(disabled).takeValue();
CHECK_EQ(second.generation.value(), uint64_t{2});
CHECK(registry.isStale(first));
auto current = registry.resolve("movement", "ground");
REQUIRE(current.ok());
CHECK(!current.value().get().enabled);
CHECK_EQ(registry.eventAt(1)->name, std::string("policy_enabled"));
auto removed = registry.remove("movement", "ground");
REQUIRE(removed.ok());
const auto tombstone = std::move(removed).takeValue();
CHECK_EQ(tombstone.generation.value(), uint64_t{3});
CHECK(registry.isTombstone("movement", "ground"));
auto stale = registry.resolveHandle(second);
CHECK(!stale.ok());
CHECK_EQ(stale.error()->code(), eve::DiagnosticCode::StaleHandle);
auto revived = registry.insert("movement", "ground", 2, 3, true, "script", "", "{}");
REQUIRE(revived.ok());
CHECK_EQ(std::move(revived).takeValue().generation.value(), uint64_t{4});
}
TEST_CASE("policyregistry.subscriptionAndSnapshotEnvelopeAreCompatible") {
PolicyRegistry registry;
std::vector<std::string> names;
auto subscription = registry.subscribe([&](const PolicyEvent& event) { names.push_back(event.name); });
REQUIRE(registry.insert("orders", "safe", 1, 1, true, "script", "", "{}").ok());
REQUIRE(registry.enable("orders", "safe", false).ok());
CHECK_EQ(names, std::vector<std::string>({"policy_registered", "policy_enabled"}));
subscription.dispose();
const auto hash = [](std::string_view) -> eve::Result<eve::ContentId> {
eve::ContentId::Bytes bytes{};
bytes[0] = 0x42;
return eve::Result<eve::ContentId>::success(eve::ContentId(bytes));
};
auto envelope = registry.snapshotEnvelopeJson(hash);
REQUIRE(envelope.ok());
PolicyRegistry copy;
auto restored = copy.restoreSnapshotJson(std::move(envelope).takeValue(), hash);
REQUIRE(restored.ok());
auto safe = copy.resolve("orders", "safe");
REQUIRE(safe.ok());
CHECK(!safe.value().get().enabled);
}
TEST_CASE("policyregistry.mutationPreservesObserverWarning") {
PolicyRegistry registry;
auto subscription =
registry.subscribe([](const PolicyEvent&) { throw std::runtime_error("injected policy observer failure"); });
auto result = registry.insert("orders", "safe", 1, 1, true, "script", "", "{}");
REQUIRE(result.ok());
CHECK_EQ(result.code(), eve::StatusCode::Applied);
REQUIRE_EQ(result.diagnostics().size(), std::size_t{1});
CHECK_EQ(result.diagnostics().front().code(), eve::DiagnosticCode::CallbackFailure);
CHECK(registry.resolve("orders", "safe").ok());
}
TEST_CASE("policyregistry.enumeratesDeterministically") {
PolicyRegistry registry;
REQUIRE(registry.insert("z", "last", 1, 0, true, "script", "", "{}").ok());
REQUIRE(registry.insert("a", "two", 1, 0, true, "script", "", "{}").ok());
REQUIRE(registry.insert("a", "one", 1, 0, true, "script", "", "{}").ok());
CHECK_EQ(registry.size(), 3);
CHECK_EQ(registry.countDomain("a"), 2);
CHECK_EQ(registry.at(0)->domain, std::string("a"));
CHECK_EQ(registry.atDomain("a", 0)->name, std::string("one"));
CHECK_EQ(registry.atDomain("a", 1)->name, std::string("two"));
CHECK_EQ(registry.at(2)->domain, std::string("z"));
}
TEST_CASE("policyregistry.eventsAreOrderedAndDescriptive") {
PolicyRegistry registry;
REQUIRE(registry.insert("ai", "default", 1, 0, true, "script", "", "{}").ok());
REQUIRE(registry.enable("ai", "default", false).ok());
REQUIRE(registry.replace("ai", "default", 2, 5, true, "batch", "", "{}").ok());
REQUIRE(registry.remove("ai", "default").ok());
CHECK_EQ(registry.eventCount(), 4);
CHECK_EQ(registry.eventAt(0)->name, std::string("policy_registered"));
CHECK_EQ(registry.eventAt(1)->name, std::string("policy_enabled"));
CHECK(!registry.eventAt(1)->enabled);
CHECK_EQ(registry.eventAt(3)->name, std::string("policy_removed"));
CHECK_EQ(registry.eventAt(3)->sequence.value(), uint64_t{4});
registry.clearEvents();
REQUIRE(registry.insert("ai", "default", 3, 0, true, "script", "", "{}").ok());
CHECK_EQ(registry.eventAt(0)->sequence.value(), uint64_t{5});
}
TEST_CASE("policyregistry.snapshotRoundTripsTransactionally") {
PolicyRegistry original;
REQUIRE(original.insert("orders", "safe", 1, 5, true, "script", "orders.policy", "{\"label\":\"Safe\"}").ok());
REQUIRE(original.insert("orders", "old", 1, 0, true, "builtin", "", "{}").ok());
REQUIRE(original.remove("orders", "old").ok());
const auto snapshot = original.snapshotJson();
PolicyRegistry restored;
REQUIRE(restored.restoreJson(snapshot).ok());
CHECK_EQ(restored.snapshotJson(), snapshot);
CHECK_EQ(restored.select("orders")->name, std::string("safe"));
REQUIRE(restored.insert("orders", "old", 2, 0, true, "batch", "", "{}").ok());
auto oldPolicy = restored.resolve("orders", "old");
REQUIRE(oldPolicy.ok());
CHECK_EQ(oldPolicy.value().get().generation.value(), uint64_t{3});
const auto before = restored.snapshotJson();
CHECK(!restored.restoreJson("{\"version\":1}").ok());
CHECK_EQ(restored.snapshotJson(), before);
}
TEST_CASE("policyregistry.scriptDiscoversButDoesNotStoreCallbacks") {
ssq::VM vm(1024, ssq::Libs::ALL);
eve::ModuleManager::expose(vm);
vm.run(vm.compileSource(R"(
result <- "fail";
local module = eve.PolicyRegistryModule();
local registry = module.newRegistry();
local callback = function(subject) { return subject + ":handled"; };
local inserted = registry.insert("administration", "governor", 1, 8, true, "script",
"governor.policy", "{\"owner\":\"game-script\"}");
if (inserted.ok) {
local generation = inserted.value.generation;
local resolved = registry.resolve("administration", "governor");
local snapshot = registry.snapshotJson();
local copy = module.newRegistry();
local restored = copy.restoreJson(snapshot);
local resolvedCopy = copy.resolveHandle("administration", "governor", generation);
if (resolved.ok && restored.ok && copy.select("administration").getName() == "governor" &&
resolvedCopy.ok && resolvedCopy.value.kind == "script")
result = callback("general-7");
}
)"));
CHECK_EQ(vm.find("result").toString(), std::string("general-7:handled"));
}