cyberhybridhub/server/test/integration/question_dispatcher_mix_test.dart
nathanAndrinoid a7c370b743
Some checks failed
Admin portal tests / test (push) Has been cancelled
Fix database connection pooling and UI question filtering
- Replaced single Postgres Connection with Pool across the server to prevent runTx locking
- Added _filterForEnabledSources to client's _fetchUnansweredForPanel to strictly enforce local configuration

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-05 22:04:06 +00:00

170 lines
4.9 KiB
Dart

@Tags(<String>['integration', 'postgres'])
library;
import 'package:cyberhybridhub_server/pipeline/question_pipeline.dart';
import 'package:cyberhybridhub_server/question_dispatcher.dart';
import 'package:cyberhybridhub_server/questions_db.dart';
import 'package:postgres/postgres.dart';
import 'package:test/test.dart';
import '../helpers/test_db.dart';
void main() {
TestDb? testDb;
setUpAll(() async {
testDb = await TestDb.open();
});
tearDown(() async {
if (testDb != null) {
await testDb!.truncateTradingTables();
}
});
tearDownAll(() async {
await testDb?.close();
});
test('disabled source is never dispatched', () async {
if (testDb == null) {
markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests');
return;
}
const String uid = 'dispatcher-spelling-only';
await testDb!.seedUser(uid);
await testDb!.questionSourcePreferencesDb.save(
uid,
<Map<String, dynamic>>[
<String, dynamic>{'id': 'market_history', 'enabled': false, 'ratio': 0},
<String, dynamic>{'id': 'spelling', 'enabled': true, 'ratio': 100},
],
);
final QuestionDispatcher dispatcher = testDb!.questionDispatcher();
final Map<String, dynamic>? payload =
await dispatcher.dispatchNextQuestion(uid);
expect(payload, isNotNull);
expect(payload!['sourceId'], 'spelling');
expect(payload['presentation'], 'spelling_word');
});
test('spelling 100% with market enabled at 0 does not enqueue market fallback',
() async {
if (testDb == null) {
markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests');
return;
}
const String uid = 'dispatcher-spelling-100-no-market-fallback';
await testDb!.seedUser(uid);
await testDb!.questionSourcePreferencesDb.save(
uid,
<Map<String, dynamic>>[
<String, dynamic>{'id': 'market_history', 'enabled': true, 'ratio': 0},
<String, dynamic>{'id': 'spelling', 'enabled': true, 'ratio': 100},
],
);
final QuestionDispatcher dispatcher = testDb!.questionDispatcher();
await dispatcher.topUpQueue(uid);
final Result rows = await testDb!.connection.execute(
Sql.named(
'''
SELECT source_tag, pipeline_key
FROM questions
WHERE assigned_user_id = @uid AND user_response IS NULL
''',
),
parameters: <String, dynamic>{'uid': uid},
);
expect(rows, isNotEmpty);
for (final ResultRow row in rows) {
expect(
QuestionsDb.resolveSourceId(<String, dynamic>{
'sourceTag': row[0],
'pipelineKey': row[1],
}),
'spelling',
);
}
});
test('70/30 mix yields spelling and respects fall-through over cycles', () async {
if (testDb == null) {
markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests');
return;
}
const String uid = 'dispatcher-mix-uid';
await testDb!.seedUser(uid);
await testDb!.questionSourcePreferencesDb.save(
uid,
<Map<String, dynamic>>[
<String, dynamic>{'id': 'market_history', 'enabled': true, 'ratio': 70},
<String, dynamic>{'id': 'spelling', 'enabled': true, 'ratio': 30},
],
);
final QuestionDispatcher dispatcher = testDb!.questionDispatcher(
testMode: true,
);
final Set<String> seenSources = <String>{};
for (var cycle = 0; cycle < 16 && seenSources.length < 2; cycle++) {
final Map<String, dynamic>? payload =
await dispatcher.dispatchNextQuestion(uid);
if (payload == null) {
continue;
}
seenSources.add(payload['sourceId'] as String);
final Result rows = await testDb!.connection.execute(
Sql.named(
'''
SELECT id, pipeline_key
FROM questions
WHERE assigned_user_id = @uid AND user_response IS NULL
ORDER BY created_at
LIMIT 1
''',
),
parameters: <String, dynamic>{'uid': uid},
);
if (rows.isEmpty) {
break;
}
final String questionId = rows.first[0]!.toString();
final String? pipelineKey = rows.first[1] as String?;
await testDb!.questionsDb.submitAnswer(
questionId: questionId,
assignedUserId: uid,
userResponse: 0,
);
if (pipelineKey == PipelineKeys.spelling) {
await testDb!.connection.execute(
Sql.named(
'''
UPDATE user_pipeline_state
SET step = @idle
WHERE assigned_user_id = @uid AND pipeline_key = @key
''',
),
parameters: <String, dynamic>{
'uid': uid,
'key': PipelineKeys.spelling,
'idle': PipelineSteps.idle,
},
);
}
}
expect(seenSources, contains('spelling'));
expect(seenSources.length, greaterThanOrEqualTo(1));
});
}