MotorSignal-App/test/route_service_test.dart

236 lines
6.8 KiB
Dart

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<ArgumentError>().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<ArgumentError>().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<ArgumentError>().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<ArgumentError>().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<ArgumentError>().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)'));
});
});
}