135 lines
4.5 KiB
Dart
135 lines
4.5 KiB
Dart
import 'dart:convert';
|
||
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:marianum_mobile/session/nextcloud_credentials.dart';
|
||
import 'package:marianum_mobile/session/session.dart';
|
||
import 'package:marianum_mobile/session/session_codec.dart';
|
||
|
||
String _basic(String user, String secret) =>
|
||
'Basic ${base64Encode(utf8.encode('$user:$secret'))}';
|
||
|
||
void main() {
|
||
group('decodeSession – installs from before guardian accounts', () {
|
||
test('username + password without a kind is a credential session', () {
|
||
final session = decodeSession({
|
||
'username': 'max',
|
||
'password': 'pw',
|
||
'nextcloud_app_password': 'app',
|
||
'nextcloud_app_password_talk': 'talk',
|
||
});
|
||
expect(session, isA<CredentialSession>());
|
||
final credential = session! as CredentialSession;
|
||
expect(credential.username, 'max');
|
||
expect(credential.password, 'pw');
|
||
expect(credential.isDemo, isFalse);
|
||
expect(credential.nextcloud.appPassword, 'app');
|
||
expect(credential.nextcloud.appPasswordTalk, 'talk');
|
||
expect(credential.nextcloud.usesLoginFlow, isFalse);
|
||
});
|
||
|
||
test('login-flow and demo flags are carried over', () {
|
||
final session =
|
||
decodeSession({
|
||
'username': 'demo@x',
|
||
'password': 'demo',
|
||
'is_demo': 'true',
|
||
'nextcloud_login_flow': 'true',
|
||
})!
|
||
as CredentialSession;
|
||
expect(session.isDemo, isTrue);
|
||
expect(session.nextcloud.usesLoginFlow, isTrue);
|
||
});
|
||
|
||
test('missing password means signed out', () {
|
||
expect(decodeSession({'username': 'max'}), isNull);
|
||
expect(decodeSession({}), isNull);
|
||
});
|
||
});
|
||
|
||
group('decodeSession – guardian', () {
|
||
test('guardian kind with e-mail', () {
|
||
final session = decodeSession({
|
||
'session_kind': 'guardian',
|
||
'guardian_email': 'eltern@example.org',
|
||
});
|
||
expect(session, isA<GuardianSession>());
|
||
expect(session!.nextcloud, isNull);
|
||
expect((session as GuardianSession).email, 'eltern@example.org');
|
||
});
|
||
|
||
test('guardian kind without e-mail is invalid', () {
|
||
expect(decodeSession({'session_kind': 'guardian'}), isNull);
|
||
});
|
||
|
||
test('an unknown kind from a newer app version reads as signed out', () {
|
||
expect(
|
||
decodeSession({
|
||
'session_kind': 'something-new',
|
||
'username': 'max',
|
||
'password': 'pw',
|
||
}),
|
||
isNull,
|
||
);
|
||
});
|
||
});
|
||
|
||
group('encodeSessionFields', () {
|
||
test('round-trips a credential session', () {
|
||
final original = CredentialSession(
|
||
username: 'max',
|
||
password: 'pw',
|
||
usesLoginFlow: true,
|
||
);
|
||
final decoded =
|
||
decodeSession(encodeSessionFields(original))! as CredentialSession;
|
||
expect(decoded.username, 'max');
|
||
expect(decoded.password, 'pw');
|
||
expect(decoded.nextcloud.usesLoginFlow, isTrue);
|
||
});
|
||
|
||
test('guardian clears credential fields', () {
|
||
final fields = encodeSessionFields(
|
||
const GuardianSession(email: 'e@x.de'),
|
||
);
|
||
expect(fields['username'], isNull);
|
||
expect(fields['password'], isNull);
|
||
expect(fields.containsKey('username'), isTrue);
|
||
expect(decodeSession(fields), isA<GuardianSession>());
|
||
});
|
||
});
|
||
|
||
group('NextcloudCredentials', () {
|
||
const base = NextcloudCredentials(username: 'max', password: 'pw');
|
||
|
||
test('prefers the app password once available', () {
|
||
expect(base.basicAuthHeader, _basic('max', 'pw'));
|
||
final withApp = base.copyWith(appPassword: () => 'app');
|
||
expect(withApp.basicAuthHeader, _basic('max', 'app'));
|
||
expect(withApp.secret, 'app');
|
||
expect(withApp.realPasswordBasicAuthHeader, _basic('max', 'pw'));
|
||
});
|
||
|
||
test('talk header needs its own app password', () {
|
||
expect(() => base.talkBasicAuthHeader, throwsStateError);
|
||
final withTalk = base.copyWith(appPasswordTalk: () => 't');
|
||
expect(withTalk.talkBasicAuthHeader, _basic('max', 't'));
|
||
});
|
||
|
||
test('login-flow accounts share the flow password for talk', () {
|
||
final flow = base.copyWith(
|
||
appPassword: () => 'flow',
|
||
usesLoginFlow: true,
|
||
);
|
||
expect(flow.talkBasicAuthHeader, _basic('max', 'flow'));
|
||
});
|
||
|
||
test('copyWith can clear an app password', () {
|
||
final cleared = base
|
||
.copyWith(appPassword: () => 'app')
|
||
.copyWith(appPassword: () => null);
|
||
expect(cleared.hasAppPassword, isFalse);
|
||
expect(cleared.secret, 'pw');
|
||
});
|
||
});
|
||
}
|