@Tags(['integration', 'postgres']) library; import 'package:cyberhybridhub_server/question_dispatcher.dart'; import 'package:cyberhybridhub_server/questions_db.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( 'listPanelQuestions excludes market when market_history enabled at ratio 0', () async { if (testDb == null) { markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests'); return; } // The critical scenario: user sets spelling=100%, market stays enabled but // ratio=0. A stale market question is already in the DB. The dispatcher // must never serve it. const String uid = 'spelling-100-market-0-panel-uid'; await testDb!.seedUser(uid); await testDb!.questionSourcePreferencesDb.save( uid, >[ {'id': 'market_history', 'enabled': true, 'ratio': 0}, {'id': 'spelling', 'enabled': true, 'ratio': 100}, ], ); await testDb!.questionsDb.createQuestion( assignedUserId: uid, questionText: 'Stale market question', correctAnswer: 10, sourceTag: 'market_history:prospective', pipelineKey: 'trading', ); final QuestionDispatcher dispatcher = testDb!.questionDispatcher(); final List> panel = await dispatcher.listPanelQuestions(uid); expect(panel, isNotEmpty); for (final Map payload in panel) { expect(payload['sourceId'], isNot('market_history')); } expect(panel.first['sourceId'], 'spelling'); expect(panel.first['presentation'], 'spelling_word'); }); test('nextQuestionAfterAnswer never returns market when spelling is 100%', () async { if (testDb == null) { markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests'); return; } const String uid = 'spelling-100-next-answer-uid'; await testDb!.seedUser(uid); await testDb!.questionSourcePreferencesDb.save( uid, >[ {'id': 'market_history', 'enabled': true, 'ratio': 0}, {'id': 'spelling', 'enabled': true, 'ratio': 100}, ], ); // Seed a stale market question that should never surface. await testDb!.questionsDb.createQuestion( assignedUserId: uid, questionText: 'Old market question', correctAnswer: 5, sourceTag: 'market_history:prospective', pipelineKey: 'trading', ); final QuestionDispatcher dispatcher = testDb!.questionDispatcher(); // Simulate the context of "just answered a spelling question". final Map fakeAnswered = { 'sourceTag': 'spelling:word_quiz', 'pipelineKey': 'spelling', 'pipelineStep': 'spelling_word_quiz', }; final Map? next = await dispatcher.nextQuestionAfterAnswer( firebaseUid: uid, answeredQuestion: fakeAnswered, ); expect(next, isNotNull); expect(next!['sourceId'], isNot('market_history')); expect(next['sourceId'], 'spelling'); }); test('listPanelQuestions excludes market when market_history disabled', () async { if (testDb == null) { markTestSkipped('Set DATABASE_URL or TEST_DATABASE_URL for integration tests'); return; } const String uid = 'spelling-only-panel-uid'; await testDb!.seedUser(uid); await testDb!.questionSourcePreferencesDb.save( uid, >[ {'id': 'market_history', 'enabled': false, 'ratio': 0}, {'id': 'spelling', 'enabled': true, 'ratio': 100}, ], ); await testDb!.questionsDb.createQuestion( assignedUserId: uid, questionText: 'Stale market question', correctAnswer: 10, sourceTag: 'market_history:prospective', pipelineKey: 'trading', ); final QuestionDispatcher dispatcher = testDb!.questionDispatcher(); final List> panel = await dispatcher.listPanelQuestions(uid); expect(panel, isNotEmpty); for (final Map payload in panel) { expect(payload['sourceId'], isNot('market_history')); } expect(panel.first['sourceId'], 'spelling'); expect(panel.first['presentation'], 'spelling_word'); }); }