Files
doormile_milderapp/test/rail_counts_the_route_test.dart
2026-08-28 15:07:30 +05:30

134 lines
5.3 KiB
Dart

import 'package:flutter_test/flutter_test.dart';
import 'package:miler/Models/stop_status.dart';
import 'package:miler/data/service_profile.dart';
import 'package:miler/views/Dashboard/home/trip.dart';
import 'package:miler/views/Dashboard/pickups/pickups.dart';
/// ─────────────────────────────────────────────────────────────────────────
/// THE RAIL COUNTS THE ROUTE, NOT THE RECORD
///
/// ── What the rider saw ──
///
/// He accepted one order out of five, picked it up, and the progress rail at
/// the top of Deliveries drew **five nodes**. One of them was his.
///
/// ── Where the other four came from ──
///
/// The rail reads the completed store, and that store is not a list of
/// finished deliveries. It is where every order that leaves the rider's hands
/// is filed, whatever ended it — and Home files a **declined** stop there too,
/// with `terminalStatus: 'rejected'`, so the decision survives on Activity.
/// That was harmless for as long as nothing read the store as a route. The rail
/// reads it as a route.
///
/// The count was the visible half. The colours were worse, and this is what
/// these tests really pin:
///
/// `rejected` → [StopProgress.pending] — four stops he had **refused**,
/// drawn on the rail ahead of him as work still to come.
/// `cancelled` → [StopStatusX.isWorkComplete] → [StopProgress.done] —
/// drawn **green**, claiming a delivery that never happened.
///
/// A skip is the case that must survive the fix: it is a door he went to and
/// could not finish, the rail has an amber node for precisely that, and losing
/// it would hide work he still owes.
/// ─────────────────────────────────────────────────────────────────────────
void main() {
tearDown(() => ServiceProfile.setActive(ServiceProfile.parcel));
/// A row as the completed store hands it back — `orderstatus` stamped with
/// whatever ended it. See `addCompletedBookings`.
Map<String, dynamic> filed(String id, String terminalStatus) => {
'orderid': id,
'orderstatus': terminalStatus,
'completedday': '2026-08-27',
'completedat': '2026-08-27T10:00:00',
};
group('what belongs on a progress rail', () {
test('a declined stop does not', () {
// The four that made the rail read five. He said no to these.
expect(wasRiddenTo(filed('r', 'rejected')), isFalse);
});
test('a cancelled stop does not', () {
// The office called it off. It was never a door he stood at, and on a
// milk run it would have drawn green.
expect(wasRiddenTo(filed('c', 'cancelled')), isFalse);
});
test('a skipped stop does', () {
// Unfinished business, and the one state the rail has an amber node for.
// Dropping it with the others would hide a door he still owes a visit.
expect(wasRiddenTo(filed('s', 'skipped')), isTrue);
});
test('a delivered stop does', () {
expect(wasRiddenTo(filed('d', 'delivered')), isTrue);
});
test('a picked stop does', () {
expect(wasRiddenTo(filed('p', 'picked')), isTrue);
});
});
group('the states the excluded rows would have been drawn in', () {
test('a declined stop reads as work still ahead', () {
// Not a subtle wrong colour — [StopProgress.pending] is the same node a
// stop he has yet to ride to gets. This is why exclusion is the fix and
// recolouring is not: it is not on his route at all.
expect(
stopProgressFor(stopStatusFromRaw('rejected')),
StopProgress.pending,
);
});
test('a cancelled stop reads as done', () {
ServiceProfile.setActive(ServiceProfile.milkMan);
expect(stopStatusFromRaw('cancelled').isWorkComplete, isTrue);
expect(
stopProgressFor(stopStatusFromRaw('cancelled')),
StopProgress.done,
);
});
test('a skipped stop reads as skipped, on either line', () {
for (final profile in [ServiceProfile.parcel, ServiceProfile.milkMan]) {
ServiceProfile.setActive(profile);
expect(
stopProgressFor(stopStatusFromRaw('skipped')),
StopProgress.skipped,
reason: 'the amber node is the whole reason a skip stays',
);
}
});
});
group("the rider's own case: one of five", () {
test('four declined and one picked leaves a rail of one', () {
// The report, as arithmetic. The store holds all five because Home files
// a decline; only one of them is a place he rode to.
final store = [
filed('1', 'picked'),
filed('2', 'rejected'),
filed('3', 'rejected'),
filed('4', 'rejected'),
filed('5', 'rejected'),
];
expect(store.length, 5);
expect(store.where(wasRiddenTo).length, 1);
});
test('a day genuinely worked is not trimmed', () {
// The fix must not cost a rider his progress. Four real completions and
// one in hand is still a five-node rail, four of them green.
final store = [
for (final id in ['1', '2', '3', '4']) filed(id, 'delivered'),
];
expect(store.where(wasRiddenTo).length, 4);
});
});
}