99cde9a1e2
Erste Testsuite des Projekts, gezielt für die zwei heiligsten Codepfade: guardedSoftLogoutRefresh (wirft nie -> kein SDK-logout()+clear(), Uta-Bug) und updatePowerLevelsSafely (Selbst-Aussperr-Schutz, Server-Stand bleibt erhalten). 10 Tests, alle gruen auf dem Pi (flutter test laeuft headless). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
171 lines
5.0 KiB
Dart
171 lines
5.0 KiB
Dart
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:matrix/matrix.dart';
|
|
import 'package:pyramid/features/spaces/space_admin_dialog.dart';
|
|
|
|
/// Fake-Client für `updatePowerLevelsSafely`: liefert einen vorgegebenen
|
|
/// Server-Stand des power_levels-Events und zeichnet auf, was (wenn
|
|
/// überhaupt) zurückgeschrieben wird. Alles andere → NoSuchMethodError.
|
|
class _FakeClient implements Client {
|
|
_FakeClient({required this.serverState, this.getError});
|
|
|
|
Map<String, Object?> serverState;
|
|
MatrixException? getError;
|
|
Map<String, Object?>? written;
|
|
|
|
@override
|
|
String? get userID => '@me:pyramid.example';
|
|
|
|
@override
|
|
Future<Map<String, Object?>> getRoomStateWithKey(
|
|
String roomId,
|
|
String eventType,
|
|
String stateKey, {
|
|
Format? format,
|
|
}) async {
|
|
final err = getError;
|
|
if (err != null) throw err;
|
|
return serverState;
|
|
}
|
|
|
|
@override
|
|
Future<String> setRoomStateWithKey(
|
|
String roomId,
|
|
String eventType,
|
|
String stateKey,
|
|
Map<String, Object?> body,
|
|
) async {
|
|
written = body;
|
|
return r'$neuesEvent';
|
|
}
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
}
|
|
|
|
class _FakeRoom implements Room {
|
|
_FakeRoom(this.client);
|
|
|
|
@override
|
|
final Client client;
|
|
|
|
@override
|
|
String get id => '!raum:pyramid.example';
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) => super.noSuchMethod(invocation);
|
|
}
|
|
|
|
void main() {
|
|
test('Server-Stand bleibt erhalten: fremde Einträge werden nie überschrieben '
|
|
'(genau der Bug, der schon einmal Admins ausgesperrt hat)', () async {
|
|
final client = _FakeClient(serverState: {
|
|
'users': {'@me:pyramid.example': 100, '@uta:pyramid.example': 50},
|
|
'users_default': 0,
|
|
'state_default': 50,
|
|
});
|
|
final room = _FakeRoom(client);
|
|
|
|
await updatePowerLevelsSafely(room, (content) {
|
|
(content['users'] as Map<String, dynamic>)['@neu:pyramid.example'] = 50;
|
|
});
|
|
|
|
final users = client.written!['users'] as Map;
|
|
expect(users['@uta:pyramid.example'], 50,
|
|
reason: 'bestehender Server-Eintrag muss überleben');
|
|
expect(users['@me:pyramid.example'], 100);
|
|
expect(users['@neu:pyramid.example'], 50);
|
|
});
|
|
|
|
test('Selbst-Degradierung unter das nötige Level wird abgebrochen, '
|
|
'nichts wird geschrieben', () async {
|
|
final client = _FakeClient(serverState: {
|
|
'users': {'@me:pyramid.example': 100},
|
|
'state_default': 50,
|
|
});
|
|
final room = _FakeRoom(client);
|
|
|
|
await expectLater(
|
|
updatePowerLevelsSafely(room, (content) {
|
|
(content['users'] as Map<String, dynamic>)['@me:pyramid.example'] = 0;
|
|
}),
|
|
throwsException,
|
|
);
|
|
expect(client.written, isNull);
|
|
});
|
|
|
|
test('eigener Eintrag entfernt → Fallback auf users_default greift und '
|
|
'schützt ebenfalls', () async {
|
|
final client = _FakeClient(serverState: {
|
|
'users': {'@me:pyramid.example': 100},
|
|
'users_default': 0,
|
|
});
|
|
final room = _FakeRoom(client);
|
|
|
|
await expectLater(
|
|
updatePowerLevelsSafely(room, (content) {
|
|
(content['users'] as Map<String, dynamic>)
|
|
.remove('@me:pyramid.example');
|
|
}),
|
|
throwsException,
|
|
);
|
|
expect(client.written, isNull);
|
|
});
|
|
|
|
test('explizites events[m.room.power_levels]-Erfordernis wird respektiert',
|
|
() async {
|
|
final client = _FakeClient(serverState: {
|
|
'users': {'@me:pyramid.example': 60},
|
|
'events': {'m.room.power_levels': 75},
|
|
'state_default': 50,
|
|
});
|
|
final room = _FakeRoom(client);
|
|
|
|
// 60 < 75 → muss abbrechen, obwohl state_default (50) erfüllt wäre.
|
|
await expectLater(
|
|
updatePowerLevelsSafely(room, (_) {}),
|
|
throwsException,
|
|
);
|
|
expect(client.written, isNull);
|
|
});
|
|
|
|
test('fehlendes power_levels-Event (M_NOT_FOUND): Start mit leerem Event, '
|
|
'Schreiben nur wenn man sich selbst hoch genug setzt', () async {
|
|
final client = _FakeClient(
|
|
serverState: {},
|
|
getError: MatrixException.fromJson(
|
|
{'errcode': 'M_NOT_FOUND', 'error': 'Event not found.'}),
|
|
);
|
|
final room = _FakeRoom(client);
|
|
|
|
// Ohne eigenen Eintrag: users_default 0 < state_default-Fallback 50 → Abbruch.
|
|
await expectLater(
|
|
updatePowerLevelsSafely(room, (_) {}),
|
|
throwsException,
|
|
);
|
|
expect(client.written, isNull);
|
|
|
|
// Setzt man sich selbst auf 100, darf geschrieben werden.
|
|
await updatePowerLevelsSafely(room, (content) {
|
|
(content['users'] as Map<String, dynamic>)['@me:pyramid.example'] = 100;
|
|
});
|
|
final users = client.written!['users'] as Map;
|
|
expect(users['@me:pyramid.example'], 100);
|
|
});
|
|
|
|
test('andere Serverfehler (z. B. M_FORBIDDEN) werden durchgereicht, '
|
|
'nichts wird geschrieben', () async {
|
|
final client = _FakeClient(
|
|
serverState: {},
|
|
getError: MatrixException.fromJson(
|
|
{'errcode': 'M_FORBIDDEN', 'error': 'Nope.'}),
|
|
);
|
|
final room = _FakeRoom(client);
|
|
|
|
await expectLater(
|
|
updatePowerLevelsSafely(room, (_) {}),
|
|
throwsA(isA<MatrixException>()),
|
|
);
|
|
expect(client.written, isNull);
|
|
});
|
|
}
|