import 'package:flutter_test/flutter_test.dart'; import 'package:motorcycle_dash/services/routes/navigation_handoff.dart'; import 'package:motorcycle_dash/services/routes/route_models.dart'; import 'package:motorcycle_dash/services/routes/route_provider.dart'; import 'package:motorcycle_dash/services/routes/route_service.dart'; void main() { group('RouteService validation tests', () { final service = RouteService(firestore: null); test('validates route title length (minimum 3 characters)', () async { expect( () => service.saveRoute( title: 'Hi', ownerUid: 'alice', stops: [], ), throwsA(isA().having( (e) => e.message, 'message', contains('between 3 and 80 characters'), )), ); }); test('validates route title length (maximum 80 characters)', () async { final longTitle = 'A' * 81; expect( () => service.saveRoute( title: longTitle, ownerUid: 'alice', stops: [], ), throwsA(isA().having( (e) => e.message, 'message', contains('between 3 and 80 characters'), )), ); }); test('validates maximum stops technical cap (25 stops)', () async { final stops = List.generate( 26, (i) => RouteStop( id: 'stop-$i', latitude: 37.0 + i * 0.01, longitude: -122.0, displayName: 'Stop $i', stopType: RouteStopType.waypoint, orderIndex: i, ), ); expect( () => service.saveRoute( title: 'Too Many Stops Route', ownerUid: 'alice', stops: stops, ), throwsA(isA().having( (e) => e.message, 'message', contains('cannot exceed 25 stops'), )), ); }); test('validates stop coordinates bounds', () async { final invalidLatStop = [ const RouteStop( id: 'stop-bad-lat', latitude: 95.0, longitude: -122.0, displayName: 'Bad Lat', stopType: RouteStopType.origin, orderIndex: 0, ), ]; expect( () => service.saveRoute( title: 'Invalid Coordinates Route', ownerUid: 'alice', stops: invalidLatStop, ), throwsA(isA().having( (e) => e.message, 'message', contains('invalid coordinates'), )), ); final invalidLngStop = [ const RouteStop( id: 'stop-bad-lng', latitude: 37.0, longitude: -185.0, displayName: 'Bad Lng', stopType: RouteStopType.origin, orderIndex: 0, ), ]; expect( () => service.saveRoute( title: 'Invalid Coordinates Route', ownerUid: 'alice', stops: invalidLngStop, ), throwsA(isA().having( (e) => e.message, 'message', contains('invalid coordinates'), )), ); }); test('offline fallback: saveRoute generates valid route object and reindexes stops', () async { final inputStops = [ const RouteStop( id: 'stop-b', latitude: 37.4, longitude: -122.2, displayName: 'Midpoint', stopType: RouteStopType.waypoint, orderIndex: 99, ), const RouteStop( id: 'stop-a', latitude: 37.7, longitude: -122.4, displayName: 'Start', stopType: RouteStopType.origin, orderIndex: 0, ), ]; final saved = await service.saveRoute( title: 'Skyline Ridge', description: 'Twisty Sunday Run', ownerUid: 'alice', visibility: RouteVisibility.groupOnly, stops: inputStops, ); expect(saved.id, startsWith('route_')); expect(saved.title, equals('Skyline Ridge')); expect(saved.ownerUid, equals('alice')); expect(saved.visibility, equals(RouteVisibility.groupOnly)); expect(saved.stops.length, equals(2)); expect(saved.stops[0].orderIndex, equals(0)); expect(saved.stops[1].orderIndex, equals(1)); }); }); group('StraightLineRouteProvider tests', () { const provider = StraightLineRouteProvider(); test('builds straight-line polyline and calculates distance', () async { const stops = [ RouteStop( id: 's1', latitude: 37.7749, longitude: -122.4194, displayName: 'SF', stopType: RouteStopType.origin, orderIndex: 0, ), RouteStop( id: 's2', latitude: 37.3382, longitude: -121.8863, displayName: 'San Jose', stopType: RouteStopType.destination, orderIndex: 1, ), ]; final result = await provider.calculateRoute(stops); expect(result.points.length, equals(2)); expect(result.points[0].latitude, equals(37.7749)); expect(result.points[1].latitude, equals(37.3382)); expect(result.totalDistanceMeters, greaterThan(65000)); expect(result.isHonestStraightLine, isTrue); }); test('empty stops produces zero points and zero distance', () async { final result = await provider.calculateRoute(const []); expect(result.points.isEmpty, isTrue); expect(result.totalDistanceMeters, equals(0.0)); }); }); group('NavigationHandoffService URI tests', () { test('builds valid Google Maps navigation URI', () { final uri = NavigationHandoffService.buildGoogleMapsUri( latitude: 37.7749, longitude: -122.4194, displayName: 'San Francisco Hub', ); expect(uri.scheme, equals('https')); expect(uri.host, equals('www.google.com')); expect(uri.path, equals('/maps/dir/')); expect(uri.queryParameters['api'], equals('1')); expect(uri.queryParameters['destination'], equals('37.774900,-122.419400')); }); test('builds valid Waze navigation URI', () { final uri = NavigationHandoffService.buildWazeUri( latitude: 37.7749, longitude: -122.4194, ); expect(uri.scheme, equals('https')); expect(uri.host, equals('waze.com')); expect(uri.path, equals('/ul')); expect(uri.queryParameters['ll'], equals('37.774900,-122.419400')); expect(uri.queryParameters['navigate'], equals('yes')); }); test('builds valid system geo URI', () { final uri = NavigationHandoffService.buildSystemGeoUri( latitude: 37.7749, longitude: -122.4194, displayName: 'SF Landmark', ); expect(uri.scheme, equals('geo')); expect(uri.path, equals('37.774900,-122.419400')); expect(uri.queryParameters['q'], equals('37.774900,-122.419400(SF Landmark)')); }); }); }