Cloud-Account-Kontingent: App liest cloud_remaining, sobald der Server es liefert

BakaAuth bekommt verbleibend/merkeVerbleibend analog zu GastZugang.
YtDownloadService liest cloud_remaining bei Cloud-Zugriff (nicht bei
Gast-Zugriff — Felder bleiben getrennt). UI zeigt "Noch N von 100
heute" für angemeldete Cloud-Accounts. Reine App-Seite, wirkungslos
bis der Server (separates Vorhaben) cloud_remaining liefert.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_012A2pmbnVNPiHdyf2GW8eLP
This commit is contained in:
Hermes (Server)
2026-08-29 13:22:04 +02:00
co-authored by Claude Sonnet 5
parent 8611738124
commit d03a4e93a0
9 changed files with 204 additions and 0 deletions
+15
View File
@@ -253,6 +253,21 @@ void main() {
findsOneWidget);
});
testWidgets(
'Angemeldeter Cloud-Account sieht das Kontingent nach einem Download',
(tester) async {
final auth = await _angemeldet();
await tester.pumpWidget(_wrap(auth, db: db, categories: categories));
await tester.pumpAndSettle();
expect(find.textContaining('von 100 heute'), findsNothing);
auth.merkeVerbleibend(63);
await tester.pump();
expect(find.text('Noch 63 von 100 heute'), findsOneWidget);
});
testWidgets(
'Server-User meldet sich im Hintergrund mit denselben Zugangsdaten '
'beim Baka-Konto an', (tester) async {
@@ -176,6 +176,26 @@ void main() {
await db.close();
});
testWidgets('Angemeldeter Cloud-Account sieht das Kontingent nach Download',
(tester) async {
final db = MeloDb(NativeDatabase.memory());
final auth = await _angemeldeteAuth();
final search = YtSearchService(auth: auth);
final download = YtDownloadService(auth: auth);
await tester.pumpWidget(_wrap(
db: db, auth: auth, search: search, download: download));
await tester.pumpAndSettle();
expect(find.textContaining('von 100 heute'), findsNothing);
auth.merkeVerbleibend(98);
await tester.pump();
expect(find.text('Noch 98 von 100 heute'), findsOneWidget);
await db.close();
});
// Kein Widget-Test für den Download-Knopf selbst: YtDownloadService.
// herunterladen() kombiniert Future.any mit .timeout() — dessen Timer
// bleibt unter Flutters fake_async-Testbindung immer als "pending" stehen
+14
View File
@@ -129,6 +129,20 @@ void main() {
expect(auth.istAngemeldet, isFalse);
});
test('merkeVerbleibend übernimmt den Kontingent-Stand und meldet Listener',
() async {
final auth = BakaAuth(
client: MockClient((_) async => http.Response('', 500)),
speicher: _MemorySpeicher());
var benachrichtigt = 0;
auth.addListener(() => benachrichtigt++);
auth.merkeVerbleibend(37);
expect(auth.verbleibend, 37);
expect(benachrichtigt, 1);
});
test('Abmelden löscht Token und Benutzer aus dem Speicher', () async {
final speicher = _MemorySpeicher();
final auth = BakaAuth(
+72
View File
@@ -380,4 +380,76 @@ void main() {
expect(tokenAufrufe, 1);
});
});
group('Cloud-Kontingent', () {
test('cloud_remaining aus der Erfolgsantwort landet in auth.verbleibend',
() async {
final auth = await _angemeldeteAuth();
final dienst = YtDownloadService(
auth: auth,
client: MockClient((req) async {
if (req.url.path == '/api/yt-dl') {
return http.Response(
jsonEncode({
'titel': 'Cloud-Lied',
'dauer': 30,
'mp3_url': '/api/dl/c.mp3',
'cloud_remaining': 99,
}),
200);
}
return http.Response.bytes([1], 200);
}),
);
final ergebnis = await dienst.herunterladen('https://youtu.be/abc',
zielOrdner: ziel.path);
expect(ergebnis, isNotNull);
expect(auth.verbleibend, 99);
});
test(
'Bei Gast-Zugriff wird cloud_remaining nicht gelesen, selbst wenn es '
'im Body steht', () async {
final speicher = _MemorySpeicher();
speicher.werte['guest_token'] = 'gast-xyz';
final gast = GastZugang(
client: MockClient((_) async => http.Response('', 500)),
speicher: speicher,
);
await gast.laden();
final auth = BakaAuth(
client: MockClient((_) async => http.Response('', 500)),
speicher: _MemorySpeicher());
final dienst = YtDownloadService(
auth: auth,
gast: gast,
client: MockClient((req) async {
if (req.url.path == '/api/yt-dl') {
return http.Response(
jsonEncode({
'titel': 'Gast-Lied',
'dauer': 30,
'mp3_url': '/api/dl/g.mp3',
'guest_remaining': 4,
// Sollte serverseitig nie gleichzeitig vorkommen, aber
// falls doch: darf nicht in auth.verbleibend landen.
'cloud_remaining': 77,
}),
200);
}
return http.Response.bytes([1], 200);
}),
);
final ergebnis = await dienst.herunterladen('https://youtu.be/abc',
zielOrdner: ziel.path);
expect(ergebnis, isNotNull);
expect(gast.verbleibend, 4);
expect(auth.verbleibend, isNull);
});
});
}