import 'dart:math'; import 'package:cyberhybridhub_server/question_source_preferences.dart'; import 'package:test/test.dart'; void main() { group('QuestionSourcePreferences.effectiveMix', () { test('matches client-style normalization for enabled sources', () { final List prefs = QuestionSourcePreferences.mergeStored(>[ {'id': 'market_history', 'enabled': true, 'ratio': 70}, {'id': 'spelling', 'enabled': true, 'ratio': 30}, ]); final Map mix = QuestionSourcePreferences.effectiveMix(prefs); expect(mix['market_history'], 70); expect(mix['spelling'], 30); }); test('excludes disabled sources', () { final List prefs = QuestionSourcePreferences.mergeStored(>[ {'id': 'market_history', 'enabled': true, 'ratio': 50}, {'id': 'spelling', 'enabled': false, 'ratio': 50}, ]); final Map mix = QuestionSourcePreferences.effectiveMix(prefs); expect(mix.keys, {'market_history'}); expect(mix['market_history'], 100); }); }); group('_sourceTryOrder (via dispatcher selection)', () { test('weighted random favors configured ratio over many rolls', () { final List prefs = QuestionSourcePreferences.mergeStored(>[ {'id': 'market_history', 'enabled': true, 'ratio': 90}, {'id': 'spelling', 'enabled': true, 'ratio': 10}, ]); final Random random = Random(42); var marketFirst = 0; for (var i = 0; i < 200; i++) { final List order = _sourceTryOrderForTest(prefs, random); if (order.first == 'market_history') { marketFirst++; } } expect(marketFirst, greaterThan(140)); }); test('only enabled source is always first', () { final List prefs = QuestionSourcePreferences.mergeStored(>[ {'id': 'market_history', 'enabled': false, 'ratio': 0}, {'id': 'spelling', 'enabled': true, 'ratio': 100}, ]); final List order = _sourceTryOrderForTest(prefs, Random(1)); expect(order, ['spelling']); }); }); } /// Mirrors [QuestionDispatcher._sourceTryOrder] for deterministic unit tests. List _sourceTryOrderForTest( List prefs, Random random, ) { final List enabled = prefs .where( (ResolvedQuestionSourcePreference s) => s.enabled && !s.comingSoon, ) .toList(growable: false); if (enabled.isEmpty) { return []; } final int totalWeight = enabled.fold( 0, (int sum, ResolvedQuestionSourcePreference s) => sum + s.ratio, ); if (totalWeight <= 0) { final List ids = enabled.map((ResolvedQuestionSourcePreference s) => s.id).toList(); ids.shuffle(random); return ids; } var roll = random.nextInt(totalWeight); String? primary; for (final ResolvedQuestionSourcePreference source in enabled) { roll -= source.ratio; if (roll < 0) { primary = source.id; break; } } primary ??= enabled.first.id; final List rest = enabled .where((ResolvedQuestionSourcePreference s) => s.id != primary) .toList() ..sort( ( ResolvedQuestionSourcePreference a, ResolvedQuestionSourcePreference b, ) => b.ratio.compareTo(a.ratio), ); return [ primary, ...rest.map((ResolvedQuestionSourcePreference s) => s.id), ]; }