import 'package:flutter_test/flutter_test.dart'; import 'package:miler/data/stop_compliance.dart'; /// The two verdicts the hub pays on. Both were already being measured — the /// bonus rule is "before the ETA" and the distance is measured to bill rider /// charges — and neither reached the rider until now, so the arithmetic behind /// them is worth pinning. void main() { group('one stop', () { test('on time and within the route tolerance is clean', () { const c = StopCompliance(onTime: true, actualKm: 5.4, plannedKm: 5.0); expect(c.onRoute, isTrue); expect(c.isClean, isTrue); expect(c.extraKm, isNull); }); test('a wander past the tolerance is off route, and says by how much', () { const c = StopCompliance(onTime: true, actualKm: 12.0, plannedKm: 5.0); expect(c.onRoute, isFalse); expect(c.extraKm, 7.0); expect(c.isClean, isFalse, reason: 'both halves have to hold'); }); // Traffic, one-ways and diversions are the rider's normal, not a detour. test('ordinary slack does not count as leaving the route', () { const near = StopCompliance(actualKm: 6.2, plannedKm: 5.0); // +24% expect(near.onRoute, isTrue); const grace = StopCompliance(actualKm: 1.4, plannedKm: 1.0); // short hop expect( grace.onRoute, isTrue, reason: 'a fixed grace keeps short stops from failing on rounding', ); }); test('late is not clean, however good the route', () { const c = StopCompliance( onTime: false, lateBy: Duration(minutes: 9), actualKm: 4.0, plannedKm: 5.0, ); expect(c.onRoute, isTrue); expect(c.isClean, isFalse); }); // A hub that set no ETA, a GPS that never resolved: not the rider's fault // and never shown as a miss. test('missing inputs are unknown, not failures', () { const nothing = StopCompliance(); expect(nothing.isUnknown, isTrue); expect(nothing.onRoute, isNull); expect(nothing.isClean, isFalse); const zeroPlan = StopCompliance(actualKm: 3.0, plannedKm: 0); expect( zeroPlan.onRoute, isNull, reason: 'a planned distance of zero is missing, not perfect', ); }); }); group('reading a stored stop', () { test('prefers the stamp written at completion', () { final c = StopCompliance.of({ 'compliance': { 'ontime': true, 'latebyseconds': 0, 'actualkm': 4.0, 'plannedkm': 4.0, }, 'riderkms': '99', 'kms': '1', }); expect(c.onTime, isTrue); expect(c.actualKm, 4.0); expect(c.onRoute, isTrue); }); test('falls back to the backend row when there is no stamp', () { final c = StopCompliance.of({'riderkms': '9.0', 'kms': '4'}); expect(c.onTime, isNull); expect(c.onRoute, isFalse, reason: '9km against a 4km plan is a wander'); }); }); group('the day', () { test('scores only what could be measured', () { final summary = ComplianceSummary.from([ { 'compliance': {'ontime': true, 'actualkm': 4.0, 'plannedkm': 4.0}, }, { 'compliance': {'ontime': false, 'actualkm': 4.0, 'plannedkm': 4.0}, }, { 'compliance': {'ontime': true, 'actualkm': 20.0, 'plannedkm': 4.0}, }, {'orderid': 'no-data'}, ]); expect(summary.measured, 3); expect(summary.unmeasured, 1, reason: 'and it is not in the denominator'); expect(summary.onTime, 2); expect(summary.onRoute, 2); expect(summary.clean, 1); expect(summary.points, 1, reason: 'a point per stop that cleared both'); expect(summary.onTimePercent, 67); }); test('an empty day scores nothing rather than dividing by zero', () { final summary = ComplianceSummary.from(const []); expect(summary.points, 0); expect(summary.onTimePercent, 0); expect(summary.cleanPercent, 0); }); }); }