feat(watch): clôture lot #91 - fréquence cardiaque live, notifications de séance et finitions montre

Consolide le lot applicatif watch companion validé :
- télémétrie fréquence cardiaque live remontée montre -> téléphone
  (collecteur watch, adapter Wear Data Layer, persistance Drift,
  propagation aux écrans historique/programme/profil/exécution)
- notifications de séance en arrière-plan côté téléphone (service
  foreground de statut + passerelle applicative)
- finitions montre : chrono d'étape, score d'étape, retrait du bouton
  "lancer une séance", thème, icônes et polices watch_app

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
2026-07-28 05:56:12 +02:00
parent c65a5a76a9
commit 65d43b9768
80 changed files with 12292 additions and 892 deletions

View File

@ -62,6 +62,24 @@ void main() {
expect(response.statusCode, 409);
});
test('register endpoint returns 400 for malformed json payload', () async {
final handler = buildApiHandler(authApi: _authApi());
final response = await handler(
Request(
'POST',
Uri.parse('http://localhost/auth/register'),
body: jsonEncode(['not-an-object']),
),
);
expect(response.statusCode, 400);
expect(
jsonDecode(await response.readAsString()),
{'error': 'Request body must be a JSON object.'},
);
});
test('login endpoint returns a token and expiration', () async {
final users = _FakeUserRepository();
await users.insert(
@ -121,6 +139,24 @@ void main() {
expect(response.statusCode, 401);
});
test('login endpoint returns 400 when email is blank', () async {
final handler = buildApiHandler(authApi: _authApi());
final response = await handler(
Request(
'POST',
Uri.parse('http://localhost/auth/login'),
body: jsonEncode({'email': ' ', 'password': 'password123'}),
),
);
expect(response.statusCode, 400);
expect(
jsonDecode(await response.readAsString()),
{'error': 'email must be a non-empty string.'},
);
});
test('logout endpoint revokes the bearer session', () async {
final users = _FakeUserRepository();
await users.insert(
@ -157,6 +193,20 @@ void main() {
expect(response.statusCode, 204);
expect(sessions.revokedSessionIds, ['session-1']);
});
test('logout endpoint returns 401 without bearer token', () async {
final handler = buildApiHandler(authApi: _authApi());
final response = await handler(
Request('POST', Uri.parse('http://localhost/auth/logout')),
);
expect(response.statusCode, 401);
expect(
jsonDecode(await response.readAsString()),
{'error': 'Missing bearer token.'},
);
});
}
AuthApi _authApi({