diet-guard/app/test/e2e_github_header.dart
Krzysztof kuhy Rudnicki 617e1ec879
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
file-length / file length <= 250 lines (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (mypy, mypy, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (pylint, pylint, ) (push) Waiting to run
pre-commit / pre-commit (${{ matrix.group }}) (rest, , mypy,pylint) (push) Waiting to run
Tests / test (push) Waiting to run
release-apk / release (APK) (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
E2E-encrypt diet_guard sync and export keys
Every sync payload (food log, banks, budget, catering credential,
Body, export keyring) now lives on diet-guard-sync/devices-e2e, sealed
with the Argon2id key from the shared crdt-sync passphrase; record
ids, tombstones and clocks stay clear. Old plaintext nodes are copied
once and never written or deleted. Locked = no pull, no push.

- PC: _sync_e2e.open_e2e over crdt-sync 0.11.0, key and copy memo in
  ~/.local/share/diet_guard/e2e; undecryptable peers are skipped.
- Phone/desktop: sync_e2e.dart, crdt_sync_flutter 0.8.1, lock icon
  in Settings; pullExportKeys goes through the same open.
- wake-alarm weigh-ins read from wake-alarm-sync/devices-e2e (own
  header), on both sides.

Todo: fda71ed1

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-12 10:31:44 +01:00

36 lines
1.3 KiB
Dart

/// A GitHub stand-in that persists the end-to-end encryption header, which
/// the sync service writes and re-reads; every other request goes to [inner].
library;
import 'dart:convert';
import 'package:http/http.dart' as http;
import 'package:http/testing.dart';
/// Wraps [inner] so `…/e2e` PUTs are stored and served back on GET.
MockClient e2eMock(Future<http.Response> Function(http.Request) inner) {
String? header;
return MockClient((request) async {
if (request.url.path.endsWith('/e2e.json')) {
// Firebase REST: the value is a JSON-encoded string.
if (request.method == 'PUT') {
header = jsonDecode(request.body) as String;
return http.Response(request.body, 200);
}
return http.Response(jsonEncode(header), 200);
}
if (!request.url.path.endsWith('/e2e')) return inner(request);
if (request.method == 'PUT') {
final body = jsonDecode(request.body) as Map<String, dynamic>;
header = utf8.decode(base64.decode(body['content'] as String));
return http.Response(jsonEncode({'content': {}, 'sha': 'h'}), 200);
}
final text = header;
if (text == null) return http.Response('', 404);
return http.Response(
jsonEncode({'content': base64.encode(utf8.encode(text)), 'sha': 'h'}),
200,
);
});
}