import 'package:cyberhybridhub/admin/models/backfill_sync_item.dart'; import 'package:cyberhybridhub/admin/models/sync_run_event.dart'; import 'package:cyberhybridhub/admin/repositories/sync_run_log_repository.dart'; import 'package:cyberhybridhub/admin/widgets/sync_run_expansion_tile.dart'; import 'package:cyberhybridhub/theme/app_theme.dart'; import 'package:flutter/material.dart'; import 'package:flutter_test/flutter_test.dart'; SyncRunEvent _event({ required int id, required String kind, required DateTime startedAt, String? error, SyncRunSeverity severity = SyncRunSeverity.ok, SyncRunStatus status = SyncRunStatus.success, }) { return SyncRunEvent( id: id, kind: kind, startedAt: startedAt, finishedAt: startedAt.add(const Duration(minutes: 1)), rowsWritten: kind == 'cleanup' ? 0 : 100, rowsRemoved: kind == 'cleanup' ? 4200 : 0, error: error, severity: severity, status: status, durationMs: 60000, summary: error ?? '100 bar rows written', ); } void main() { testWidgets('shows severity chip and full error text when expanded', ( WidgetTester tester, ) async { const String errorText = 'AlpacaMarketDataException: getBarsRange rate limited: 429'; final SyncRunEvent event = _event( id: 7, kind: 'backfill', startedAt: DateTime.utc(2026, 5, 27, 10), error: errorText, severity: SyncRunSeverity.rateLimit, status: SyncRunStatus.failed, ); await tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: Scaffold( body: SyncRunExpansionTile( event: event, now: DateTime.utc(2026, 5, 27, 12), ), ), ), ); expect(find.text('Partial success'), findsNothing); expect(find.textContaining('Failed'), findsWidgets); expect(find.textContaining('Rate limited'), findsNothing); await tester.tap(find.byType(ExpansionTile)); await tester.pumpAndSettle(); expect(find.byKey(const Key('sync-run-error-7')), findsOneWidget); expect(find.text(errorText), findsWidgets); expect(find.textContaining('raw'), findsNothing); expect(find.text('HTTP status'), findsOneWidget); expect(find.text('429'), findsOneWidget); }); testWidgets('renders very long error text without raw field labels', ( WidgetTester tester, ) async { tester.view.physicalSize = const Size(800, 1600); tester.view.devicePixelRatio = 1.0; addTearDown(tester.view.reset); final String longError = 'E' * 500; final SyncRunEvent event = _event( id: 8, kind: 'backfill', startedAt: DateTime.utc(2026, 5, 27, 10), error: longError, severity: SyncRunSeverity.error, status: SyncRunStatus.failed, ); await tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: Scaffold( body: SyncRunExpansionTile(event: event), ), ), ); await tester.tap(find.byType(ExpansionTile)); await tester.pumpAndSettle(); expect(find.text(longError), findsWidgets); expect(find.textContaining('"raw"'), findsNothing); }); testWidgets('backfill items show Alpaca start wire string for DB spot checks', ( WidgetTester tester, ) async { const String slotWire = '2026-05-26T08:00:00Z'; final SyncRunEvent event = SyncRunEvent( id: 9, kind: 'backfill', startedAt: DateTime.utc(2026, 5, 27, 10), finishedAt: DateTime.utc(2026, 5, 27, 10, 1), rowsWritten: 0, rowsRemoved: 0, severity: SyncRunSeverity.warning, status: SyncRunStatus.partial, durationMs: 60000, summary: '0 bar rows written', error: 'partial sync saved (0 rows)', backfillItems: [ BackfillSyncItem( slotStart: DateTime.utc(2026, 5, 26, 8), slotStartWire: slotWire, symbols: ['A', 'AA'], ), ], ); await tester.pumpWidget( MaterialApp( theme: buildAppTheme(), home: Scaffold( body: SyncRunExpansionTile(event: event), ), ), ); await tester.tap(find.byType(ExpansionTile)); await tester.pumpAndSettle(); expect(find.text(slotWire), findsOneWidget); expect(find.text('2 assets'), findsOneWidget); expect(find.textContaining('A, AA'), findsNothing); expect( find.textContaining('Backfill fetches (Alpaca start / raw.slot_start)'), findsOneWidget, ); }); }