147 lines
6.0 KiB
C++
147 lines
6.0 KiB
C++
#include "attributes/AttributeSetResourceAccount.h"
|
|
#include "dialogue/ConversationAuthoring.h"
|
|
#include "dialogue/DialogueFlow.h"
|
|
#include "dialogue/DialoguePayment.h"
|
|
#include "economy/EconomyLedgerResourceAccount.h"
|
|
#include "statepatch/StateAccessAdapter.h"
|
|
#include "statepatch/StatePatch.h"
|
|
#include "zeroerr/unittest.h"
|
|
|
|
#include <array>
|
|
#include <cstdint>
|
|
#include <span>
|
|
#include <utility>
|
|
|
|
#include <simplesquirrel/simplesquirrel.hpp>
|
|
|
|
namespace {
|
|
|
|
using eve::StateMutation;
|
|
using eve::Value;
|
|
using eve::dialogue::DialogueAccountBindings;
|
|
using eve::dialogue::DialoguePaymentAdapter;
|
|
using eve::dialogue::PaymentSpec;
|
|
|
|
DialoguePaymentAdapter makeAdapter(eve::economy::EconomyLedgerResourceAccount& money,
|
|
eve::attributes::AttributeSetResourceAccount& reputation) {
|
|
DialogueAccountBindings bindings;
|
|
bindings.money = &money;
|
|
bindings.reputation = &reputation;
|
|
return DialoguePaymentAdapter(std::move(bindings));
|
|
}
|
|
|
|
} // namespace
|
|
|
|
TEST_CASE("dialoguePayment.composesMoneyReputationAndStatePatch") {
|
|
eve::economy::EconomyLedger ledger;
|
|
CHECK(ledger.credit("gold", 20) == 20);
|
|
eve::attributes::AttributeSet attributes("actor");
|
|
attributes.setBase("reputation", 10.0);
|
|
eve::economy::EconomyLedgerResourceAccount money(ledger);
|
|
eve::attributes::AttributeSetResourceAccount reputation(attributes);
|
|
DialoguePaymentAdapter adapter = makeAdapter(money, reputation);
|
|
|
|
eve::statepatch::Store store;
|
|
eve::statepatch::StatePatchStateAdapter state(store);
|
|
PaymentSpec payment{5, 2};
|
|
const std::array<StateMutation, 1> mutations{{
|
|
StateMutation{"actor", "accepted", Value(true), eve::MutationKind::Set, true},
|
|
}};
|
|
|
|
auto result = adapter.executeStateMutation("dialogue-payment-success", payment, state, mutations);
|
|
REQUIRE(result.ok());
|
|
(void)std::move(result).takeValue();
|
|
CHECK(ledger.get("gold") == 15);
|
|
CHECK(attributes.getBase("reputation") == 8.0);
|
|
CHECK(store.has("actor", "accepted"));
|
|
CHECK(store.get("actor", "accepted") == "true");
|
|
}
|
|
|
|
TEST_CASE("dialoguePayment.failureDoesNotDebitOrPublishState") {
|
|
eve::economy::EconomyLedger ledger;
|
|
CHECK(ledger.credit("gold", 20) == 20);
|
|
eve::attributes::AttributeSet attributes("actor");
|
|
attributes.setBase("reputation", 10.0);
|
|
eve::economy::EconomyLedgerResourceAccount money(ledger);
|
|
eve::attributes::AttributeSetResourceAccount reputation(attributes);
|
|
DialoguePaymentAdapter adapter = makeAdapter(money, reputation);
|
|
|
|
eve::statepatch::Store store;
|
|
eve::statepatch::StatePatchStateAdapter state(store);
|
|
const std::array<StateMutation, 2> invalidMutations{{
|
|
StateMutation{"actor", "must_not_publish", Value(true), eve::MutationKind::Set, true},
|
|
StateMutation{"actor", "not_a_number", Value("bad"), eve::MutationKind::AddNumber, true},
|
|
}};
|
|
auto result = adapter.executeStateMutation("dialogue-payment-failure", PaymentSpec{5, 2}, state, invalidMutations);
|
|
REQUIRE(!result.ok());
|
|
(void)result.status();
|
|
CHECK(ledger.get("gold") == 20);
|
|
CHECK(attributes.getBase("reputation") == 10.0);
|
|
CHECK(!store.has("actor", "must_not_publish"));
|
|
|
|
auto insufficient = adapter.executeStateMutation("dialogue-payment-insufficient", PaymentSpec{100, 1}, state,
|
|
std::span<const StateMutation>{});
|
|
REQUIRE(!insufficient.ok());
|
|
(void)insufficient.status();
|
|
CHECK(ledger.get("gold") == 20);
|
|
CHECK(attributes.getBase("reputation") == 10.0);
|
|
}
|
|
|
|
TEST_CASE("dialoguePayment.specRejectsUnknownAndNonPositiveFields") {
|
|
const auto unknown = PaymentSpec::fromValue(Value::Object{{"money", Value(1)}, {"tokens", Value(2)}});
|
|
REQUIRE(!unknown.ok());
|
|
(void)unknown.status();
|
|
|
|
const auto negative = PaymentSpec::fromValue(Value::Object{{"reputation", Value(0)}});
|
|
REQUIRE(!negative.ok());
|
|
(void)negative.status();
|
|
|
|
PaymentSpec payment{3, 4};
|
|
const Value serialized = payment.toValue();
|
|
auto roundTrip = PaymentSpec::fromValue(serialized);
|
|
REQUIRE(roundTrip.ok());
|
|
const PaymentSpec restored = std::move(roundTrip).takeValue();
|
|
CHECK(restored.money == 3);
|
|
CHECK(restored.reputation == 4);
|
|
}
|
|
|
|
TEST_CASE("dialoguePayment.choiceUsesTheSameAtomicFacade") {
|
|
eve::economy::EconomyLedger ledger;
|
|
CHECK(ledger.credit("gold", 20) == 20);
|
|
eve::attributes::AttributeSet attributes("actor");
|
|
attributes.setBase("reputation", 10.0);
|
|
eve::economy::EconomyLedgerResourceAccount money(ledger);
|
|
eve::attributes::AttributeSetResourceAccount reputation(attributes);
|
|
eve::statepatch::Store store;
|
|
eve::statepatch::StatePatchStateAdapter state(store);
|
|
|
|
eve::dialogue::ConversationAsset asset;
|
|
asset.id = "dialogue.payment-choice";
|
|
asset.entry = "choice";
|
|
eve::dialogue::ConversationAsset::Node choice;
|
|
choice.id = "choice";
|
|
choice.kind = eve::dialogue::ConversationAsset::Node::Kind::Choice;
|
|
choice.routes.emplace_back("buy", "end");
|
|
choice.routes.front().payment = PaymentSpec{5, 2};
|
|
choice.routes.front().stateMutations.push_back(
|
|
StateMutation{"actor", "accepted", Value(true), eve::MutationKind::Set, true});
|
|
eve::dialogue::ConversationAsset::Node end;
|
|
end.id = "end";
|
|
end.kind = eve::dialogue::ConversationAsset::Node::Kind::End;
|
|
asset.nodes = {choice, end};
|
|
eve::dialogue::ConversationDocument document(asset);
|
|
|
|
eve::dialogue::DialogueFlow flow;
|
|
eve::dialogue::DialogueFlow::IntegrationConfig config;
|
|
config.stateMutation = &state;
|
|
config.accounts.money = &money;
|
|
config.accounts.reputation = &reputation;
|
|
flow.configureIntegration(std::move(config));
|
|
REQUIRE(flow.applyDocument(&document));
|
|
CHECK(flow.start(asset.id, ssq::Object{}));
|
|
CHECK(flow.select("buy").ok());
|
|
CHECK(ledger.get("gold") == 15);
|
|
CHECK(attributes.getBase("reputation") == 8.0);
|
|
CHECK(store.has("actor", "accepted"));
|
|
}
|