diet-guard/app/lib/services/kuchnia_errors.dart
Krzysztof kuhy Rudnicki 3d82ddefd4 feat: let the phone fetch and bank the catering delivery itself
The phone now walks the panel on its own -- login, active order, delivery,
menu -- parses the dishes and banks them, so the feature works with the PC
switched off. Read-only: nothing here logs a meal.

`package:http` has no cookie jar, so the `SESSION` cookie is read off the login
response and replayed by hand. `Response.headers` folds duplicate `set-cookie`
values into one comma-joined string, so the parser splits on both `,` and `;`
and looks for the named cookie rather than assuming the header holds only it --
tested against both orderings and against a panel that sets no session cookie
at all.

Android-only, as decided: the panel sends no CORS headers, so a browser cannot
call it. `refreshDelivery` checks `kIsWeb` first and returns the unsupported
reason without touching the network. No `dart:io` anywhere -- `package:http`
compiles for web, it is the panel that refuses, not the toolchain, so this
needs no `_io`/`_web` seam.

The idempotency guard is ported with its teeth: `addManualEntry` restamps
`editedAt` unconditionally, so re-banking an unchanged dish republishes the
whole curated bank to every peer. `bankDishes` compares the nutritional fields
first and returns a write *count*, which is what the tests assert on -- "the
entry exists" passes while this misbehaves. The comparison is numeric, not
string, so `270 == 270.0` is a match exactly as in Python's `_matches`.

Every failure becomes a reason string, never a throw, so a catering outage
cannot break the caller: a rejected login, a delivery whose dishes all fail
the unit check, and missing credentials each have a test.

One test bug worth noting: the assertion that nothing is logged was missing an
`await` and so was comparing a Future against `isEmpty` -- the single most
important guarantee in this file was passing vacuously until the split forced
it to run.

Test files split three ways for the 250-line cap; block counts checked against
the pre-split file (16 either side), not just a green suite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QQpCiPaSbyRpwsLkH3dgsX
2026-08-23 08:30:21 +02:00

20 lines
657 B
Dart

/// The catering import's single error type.
///
/// Mirrors `diet_guard/_kuchnia_errors.py`. Every failure the panel can hand
/// back becomes one of these, so the fail-closed boundary in
/// `kuchnia_import.dart` has exactly one exception to translate into a reason
/// string -- a catering outage must never break the caller.
library;
/// Raised when the catering panel cannot be read.
class KuchniaError implements Exception {
/// Creates an error carrying a user-facing [message].
const KuchniaError(this.message);
/// Why the panel could not be read, phrased for display.
final String message;
@override
String toString() => message;
}