import 'package:cyberhybridhub_server/trading/market_history_query.dart'; import 'package:cyberhybridhub_server/trading/rule_engine.dart'; import 'package:cyberhybridhub_server/trading/trading_config.dart'; import 'package:test/test.dart'; void main() { late TradingRuleConfig guessRule; late DateTime now; setUp(() { guessRule = TradingRuleConfig.fromJson({ 'id': 'guess_weekly_move', 'type': 'guess_weekly_move', 'symbol': '*', 'question_template': '{{token}} was {{ref_price}} {{ref_days_ago}} days ago. +10 up, -10 down.', }); now = DateTime.utc(2026, 5, 26, 12); }); WeeklyMover mover({ required num openClose, required num currentClose, int days = 5, }) { return WeeklyMover( symbol: 'SPY', openClose: openClose, currentClose: currentClose, days: days, ); } group('RuleEngine.evaluateGuessWeeklyMove', () { test('up move produces ASSET_A token and correct_answer 10', () { final RuleEngine engine = RuleEngine(); final RuleEvaluation result = engine.evaluateGuessWeeklyMove( rule: guessRule, mover: mover(openClose: 500, currentClose: 510), symbolToken: 'ASSET_A', now: now, ); expect(result.fired, isTrue); expect(result.symbolToken, 'ASSET_A'); expect(result.guessSymbol, 'SPY'); expect(result.correctAnswer, 10); expect(result.refPrice, 500); expect(result.observedPrice, 510); expect(result.refDaysAgo, 5); expect( result.questionText, 'ASSET_A was 500.00 5 days ago. +10 up, -10 down.', ); expect(result.questionText, isNot(contains('SPY'))); expect(result.questionText, isNot(contains('{{symbol}}'))); }); test('down move produces correct_answer -10', () { final RuleEngine engine = RuleEngine(); final RuleEvaluation result = engine.evaluateGuessWeeklyMove( rule: guessRule, mover: mover(openClose: 510, currentClose: 500), symbolToken: 'ASSET_A', now: now, ); expect(result.fired, isTrue); expect(result.correctAnswer, -10); }); test('insufficient bars (no mover) does not fire', () { final RuleEngine engine = RuleEngine(); final RuleEvaluation result = engine.evaluateGuessWeeklyMove( rule: guessRule, mover: null, symbolToken: null, now: now, ); expect(result.fired, isFalse); expect(result.skipReason, RuleSkipReason.insufficientBars); }); test('guessSymbol is exposed for pipeline metadata', () { final RuleEngine engine = RuleEngine(); final RuleEvaluation result = engine.evaluateGuessWeeklyMove( rule: guessRule, mover: mover(openClose: 500, currentClose: 510), symbolToken: 'ASSET_A', now: now, ); expect(result.guessSymbol, 'SPY'); }); }); }