Tests / test (push) Canceled after 0s
Build Android (FCM) / build-android-fcm (push) Canceled after 0s
Build Android / build-android (push) Canceled after 0s
Build iOS / build-ios (push) Canceled after 0s
Build Linux / build-linux (push) Canceled after 0s
Build macOS / build-macos (push) Canceled after 0s
Build Windows / build-windows (push) Canceled after 0s
Native crypto / native-crypto (push) Canceled after 0s
Release (main) / android (oneme) (push) Canceled after 0s
Release (main) / android (qlyra) (push) Canceled after 0s
Release (main) / windows (push) Canceled after 0s
Release (main) / linux (push) Canceled after 0s
Release (main) / macos (push) Canceled after 0s
Release (main) / ios (push) Canceled after 0s
Release (main) / release (push) Canceled after 0s
Adapt video editing, decrypted message actions, custom themes and gestures from PR 166. Track visible server presence per session and preserve delivery for invisible clients.
115 lines
3.8 KiB
Dart
115 lines
3.8 KiB
Dart
import 'dart:async';
|
|
import 'dart:convert';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:http/http.dart' as http;
|
|
import 'package:http/testing.dart';
|
|
import 'package:ikar/backend/api.dart';
|
|
import 'package:ikar/backend/ikar_transport.dart';
|
|
import 'package:ikar/core/config/qlyra_settings.dart';
|
|
import 'package:ikar/core/protocol/opcode_map.dart';
|
|
import 'package:ikar/core/utils/app_foreground.dart';
|
|
import 'package:shared_preferences/shared_preferences.dart';
|
|
|
|
class _Transport extends IkarTransport {
|
|
_Transport(
|
|
super.uri,
|
|
List<Map> requests, {
|
|
Future<void> Function(Map)? onRequest,
|
|
}) : super(
|
|
client: MockClient((request) async {
|
|
final payload = jsonDecode(request.body)['payload'] as Map;
|
|
requests.add(payload);
|
|
await onRequest?.call(payload);
|
|
return http.Response(jsonEncode({'cmd': 1, 'payload': {}}), 200);
|
|
}),
|
|
);
|
|
bool connected = false;
|
|
final eventActivity = <bool>[];
|
|
@override
|
|
bool get eventsConnected => connected;
|
|
@override
|
|
Future<void> connectEvents() async {
|
|
eventActivity.add(interactive);
|
|
connected = true;
|
|
}
|
|
}
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
setUp(() {
|
|
SharedPreferences.setMockInitialValues({});
|
|
QlyraSettings.ghostMode.value = false;
|
|
AppForeground.update(foreground: true);
|
|
});
|
|
test('explicit inactive ping reaches the server', () async {
|
|
final requests = <Map>[];
|
|
final api = Api(transportFactory: (uri) => _Transport(uri, requests));
|
|
addTearDown(api.dispose);
|
|
await api.connect();
|
|
api.sendPing(interactive: false);
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
expect(requests.last['interactive'], false);
|
|
});
|
|
test('ghost mode survives login and event reconnection', () async {
|
|
QlyraSettings.ghostMode.value = true;
|
|
final requests = <Map>[];
|
|
late _Transport transport;
|
|
final api = Api(
|
|
transportFactory: (uri) => transport = _Transport(uri, requests),
|
|
);
|
|
addTearDown(api.dispose);
|
|
await api.connect();
|
|
await api.sendRequest(Opcode.login, {
|
|
'token': 'synthetic',
|
|
'interactive': true,
|
|
});
|
|
expect(requests.last['interactive'], false);
|
|
expect(transport.eventActivity, [false]);
|
|
transport.connected = false;
|
|
api.wakeUp();
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
expect(transport.eventActivity, [false, false]);
|
|
expect(requests.last['interactive'], false);
|
|
QlyraSettings.ghostMode.value = false;
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
expect(requests.last['interactive'], true);
|
|
AppForeground.update(foreground: false);
|
|
await Future<void>.delayed(const Duration(milliseconds: 20));
|
|
expect(requests.last['interactive'], false);
|
|
});
|
|
test('a slow active ping cannot override newly enabled ghost mode', () async {
|
|
final requests = <Map>[];
|
|
final started = Completer<void>();
|
|
final release = Completer<void>();
|
|
final hidden = Completer<void>();
|
|
final api = Api(
|
|
transportFactory: (uri) => _Transport(
|
|
uri,
|
|
requests,
|
|
onRequest: (payload) async {
|
|
if (payload['interactive'] == true && !started.isCompleted) {
|
|
started.complete();
|
|
await release.future;
|
|
}
|
|
if (payload['interactive'] == false && !hidden.isCompleted) {
|
|
hidden.complete();
|
|
}
|
|
},
|
|
),
|
|
);
|
|
addTearDown(api.dispose);
|
|
await api.connect();
|
|
api.sendPing(interactive: true);
|
|
await started.future;
|
|
QlyraSettings.ghostMode.value = true;
|
|
await Future<void>.delayed(Duration.zero);
|
|
expect(
|
|
requests.where((payload) => payload.containsKey('interactive')),
|
|
hasLength(1),
|
|
);
|
|
release.complete();
|
|
await hidden.future.timeout(const Duration(seconds: 2));
|
|
expect(requests.last['interactive'], false);
|
|
});
|
|
}
|