233 lines
8.7 KiB
MQL5
233 lines
8.7 KiB
MQL5
//+------------------------------------------------------------------+
|
|
//| TestBehaviorInjection.mq5 |
|
|
//| Synthetic Test Harness for MetaTrader 5 |
|
|
//| Institutional Architecture in MQL5 - Part 1 Series |
|
|
//+------------------------------------------------------------------+
|
|
#property copyright "Institutional Architecture in MQL5 Series"
|
|
#property version "1.00"
|
|
#property description "Synthetic test: Dynamic Behavior Injection for Chaos Engineering"
|
|
#property description "Validates function pointer delegates, stateful requotes, and on-the-fly fault switching."
|
|
#property script_show_inputs
|
|
#property strict
|
|
|
|
#include <SyntheticTestHarness\CSyntheticEngine.mqh>
|
|
#include <SyntheticTestHarness\BehaviorLibrary.mqh>
|
|
#include <SyntheticTestHarness\ErrorCodes.mqh>
|
|
#include <SyntheticTestHarness\CAssert.mqh>
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Helper: Build minimal BUY trade request |
|
|
//+------------------------------------------------------------------+
|
|
void BuildTestBuyRequest(MqlTradeRequest &request, const double volume = 0.10)
|
|
{
|
|
ZeroMemory(request);
|
|
request.action = TRADE_ACTION_DEAL;
|
|
request.type = ORDER_TYPE_BUY;
|
|
request.volume = volume;
|
|
request.price = 1.10000;
|
|
request.symbol = "EURUSD";
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Custom Behavior Mock Functions for Price & DOM Testing |
|
|
//+------------------------------------------------------------------+
|
|
double CustomSpreadSpikeBehavior(void)
|
|
{
|
|
return(250.0); // Custom 250 point spread injection
|
|
}
|
|
|
|
bool CustomMarketBookBehavior(const string symbol, MqlBookInfo &book[])
|
|
{
|
|
ArrayResize(book, 2);
|
|
book[0].type = BOOK_TYPE_BUY;
|
|
book[0].price = 1.10000;
|
|
book[0].volume = 100;
|
|
|
|
book[1].type = BOOK_TYPE_SELL;
|
|
book[1].price = 1.10025;
|
|
book[1].volume = 150;
|
|
return(true);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TEST 1: Default Behavior Without Injection or Ticks |
|
|
//| Pattern: Arrange -> Act -> Assert |
|
|
//+------------------------------------------------------------------+
|
|
void TestDefaultEnvironmentState(void)
|
|
{
|
|
Log("--- Test 1: Default Environment State ---");
|
|
|
|
// [Arrange]
|
|
CSyntheticEngine engine; // Standalone engine without tick provider
|
|
|
|
// [Act & Assert]
|
|
AssertDoubleEqual("Default Bid price is 1.10000", 1.10000, engine.GetBid());
|
|
AssertDoubleEqual("Default Ask price is 1.10020", 1.10020, engine.GetAsk());
|
|
AssertDoubleEqual("Default Spread is 20 points", 20.0, engine.GetSpread());
|
|
|
|
MqlBookInfo book[];
|
|
AssertFalse("Default MarketBookGet returns false", engine.MarketBookGet("EURUSD", book));
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TEST 2: Stateful Requote Behavior Cycle (Behavior_RequoteTwice) |
|
|
//| Pattern: Arrange -> Act -> Assert |
|
|
//| Verifies: Call 1 & 2 return REQUOTE, Call 3 returns DONE. |
|
|
//+------------------------------------------------------------------+
|
|
void TestStatefulRequoteBehavior(void)
|
|
{
|
|
Log("--- Test 2: Stateful Requote Twice Behavior ---");
|
|
|
|
// [Arrange]
|
|
CSyntheticEngine engine;
|
|
engine.MockOrderSend(Behavior_RequoteTwice); // Inject stateful behavior
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
BuildTestBuyRequest(request, 1.0);
|
|
|
|
// [Act 1: First Attempt]
|
|
bool success1 = engine.OrderSend(request, result);
|
|
|
|
// [Assert 1]
|
|
AssertFalse("Attempt 1 fails due to requote", success1);
|
|
AssertUintEqual("Attempt 1 retcode is TRADE_RETCODE_REQUOTE",
|
|
SYNTH_ERR_REQUOTE, result.retcode);
|
|
|
|
// [Act 2: Second Attempt]
|
|
bool success2 = engine.OrderSend(request, result);
|
|
|
|
// [Assert 2]
|
|
AssertFalse("Attempt 2 fails due to requote", success2);
|
|
AssertUintEqual("Attempt 2 retcode is TRADE_RETCODE_REQUOTE",
|
|
SYNTH_ERR_REQUOTE, result.retcode);
|
|
|
|
// [Act 3: Third Attempt]
|
|
bool success3 = engine.OrderSend(request, result);
|
|
|
|
// [Assert 3]
|
|
AssertTrue("Attempt 3 succeeds after retry", success3);
|
|
AssertUintEqual("Attempt 3 retcode is TRADE_RETCODE_DONE",
|
|
TRADE_RETCODE_DONE, result.retcode);
|
|
AssertDoubleEqual("Filled volume matches request", 1.0, result.volume);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TEST 3: On-the-fly Hot Swapping of Injected Behaviors |
|
|
//| Pattern: Arrange -> Act -> Assert |
|
|
//| Verifies: Switching from Requote to Fatal Disconnect mid-flight. |
|
|
//+------------------------------------------------------------------+
|
|
void TestHotSwappingBehavior(void)
|
|
{
|
|
Log("--- Test 3: Hot-Swapping Behavior to Fatal Disconnect ---");
|
|
|
|
// [Arrange]
|
|
CSyntheticEngine engine;
|
|
engine.MockOrderSend(Behavior_RequoteTwice);
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
BuildTestBuyRequest(request, 0.5);
|
|
|
|
// Consume one requote
|
|
engine.OrderSend(request, result);
|
|
AssertUintEqual("Initial behavior is REQUOTE", SYNTH_ERR_REQUOTE, result.retcode);
|
|
|
|
// [Act: Hot-Swap Behavior Dynamically]
|
|
engine.MockOrderSend(Behavior_FatalDisconnect);
|
|
|
|
bool success = engine.OrderSend(request, result);
|
|
|
|
// [Assert]
|
|
AssertFalse("Fatal disconnect causes immediate failure", success);
|
|
AssertUintEqual("Retcode reflects TRADE_RETCODE_CONNECTION",
|
|
TRADE_RETCODE_CONNECTION, result.retcode);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TEST 4: Behavior Resetting (Reversion to Default Engine State) |
|
|
//| Pattern: Arrange -> Act -> Assert |
|
|
//+------------------------------------------------------------------+
|
|
void TestBehaviorReset(void)
|
|
{
|
|
Log("--- Test 4: Resetting Injected Behaviors ---");
|
|
|
|
// [Arrange]
|
|
CSyntheticEngine engine;
|
|
engine.MockOrderSend(Behavior_FatalDisconnect);
|
|
|
|
MqlTradeRequest request;
|
|
MqlTradeResult result;
|
|
BuildTestBuyRequest(request, 0.1);
|
|
|
|
// Verify injected error is active
|
|
engine.OrderSend(request, result);
|
|
AssertUintEqual("Injected disconnect is active", TRADE_RETCODE_CONNECTION, result.retcode);
|
|
|
|
// [Act: Reset Behavior]
|
|
engine.ResetOrderSend();
|
|
|
|
// [Assert: Engine reverts to default tickless guard]
|
|
bool success = engine.OrderSend(request, result);
|
|
AssertFalse("Default without loaded tick returns SYNTH_ERR_PRICE_OFF", success);
|
|
AssertUintEqual("Retcode is SYNTH_ERR_PRICE_OFF", SYNTH_ERR_PRICE_OFF, result.retcode);
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| TEST 5: Custom Price & DOM Behavior Injection |
|
|
//| Pattern: Arrange -> Act -> Assert |
|
|
//+------------------------------------------------------------------+
|
|
void TestCustomPriceAndDOMInjection(void)
|
|
{
|
|
Log("--- Test 5: Custom Price & DOM Behavior Injection ---");
|
|
|
|
// [Arrange]
|
|
CSyntheticEngine engine;
|
|
engine.MockGetSpread(CustomSpreadSpikeBehavior);
|
|
engine.MockMarketBookGet(CustomMarketBookBehavior);
|
|
|
|
// [Act & Assert: Price Behavior]
|
|
AssertDoubleEqual("Custom injected spread is 250 points", 250.0, engine.GetSpread());
|
|
|
|
// [Act & Assert: DOM Behavior]
|
|
MqlBookInfo book[];
|
|
bool has_book = engine.MarketBookGet("EURUSD", book);
|
|
AssertTrue("Custom MarketBookGet succeeds", has_book);
|
|
AssertIntEqual("Custom book has 2 entries", 2, ArraySize(book));
|
|
AssertDoubleEqual("Book best bid is 1.10000", 1.10000, book[0].price);
|
|
AssertDoubleEqual("Book best ask is 1.10025", 1.10025, book[1].price);
|
|
|
|
// [Reset]
|
|
engine.ResetAllBehaviors();
|
|
AssertDoubleEqual("Reset restores default spread of 20 points", 20.0, engine.GetSpread());
|
|
}
|
|
|
|
//+------------------------------------------------------------------+
|
|
//| Script Program Start Function |
|
|
//+------------------------------------------------------------------+
|
|
void OnStart(void)
|
|
{
|
|
CAssert::Reset();
|
|
|
|
Log("==========================================================");
|
|
Log(" SYNTHETIC TEST HARNESS - Behavior Injection Runner ");
|
|
Log(" Institutional Architecture in MQL5 - Part 1 Upgrade ");
|
|
Log("==========================================================");
|
|
Log("");
|
|
|
|
// Execute test suite
|
|
TestDefaultEnvironmentState();
|
|
Log("");
|
|
TestStatefulRequoteBehavior();
|
|
Log("");
|
|
TestHotSwappingBehavior();
|
|
Log("");
|
|
TestBehaviorReset();
|
|
Log("");
|
|
TestCustomPriceAndDOMInjection();
|
|
Log("");
|
|
|
|
//--- Test Summary Output
|
|
CAssert::Summary("Behavior Injection Test Suite", true);
|
|
}
|
|
//+------------------------------------------------------------------+
|