cyberhybridhub/server/test/question_dispatcher_test.dart
nathanAndrinoid 4459852f83
Some checks failed
Admin portal tests / test (push) Has been cancelled
spelling
2026-06-05 03:22:35 +00:00

122 lines
3.8 KiB
Dart

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