203 lines
6.4 KiB
Dart
203 lines
6.4 KiB
Dart
import 'package:dio/dio.dart';
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:marianum_mobile/api/errors/ticker_content_unavailable_exception.dart';
|
|
import 'package:marianum_mobile/api/marianumconnect/queries/get_ticker/get_ticker_response.dart';
|
|
import 'package:marianum_mobile/api/marianumconnect/queries/get_ticker_nav/get_ticker_nav_response.dart';
|
|
import 'package:marianum_mobile/api/marianumconnect/queries/get_ticker_page/get_ticker_page.dart';
|
|
import 'package:marianum_mobile/api/marianumconnect/queries/get_ticker_page/get_ticker_page_response.dart';
|
|
|
|
void main() {
|
|
group('TickerResponse.fromJson', () {
|
|
test('available post carries the nested content object', () {
|
|
final response = TickerResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'available': true,
|
|
'hash': 'R123:uuid@2026-07-08T10:00:00',
|
|
'publishedAt': '2026-07-08T10:00:00',
|
|
'webUrl': '/ticker',
|
|
'content': {
|
|
'type': 'doc',
|
|
'content': [
|
|
{
|
|
'type': 'paragraph',
|
|
'content': [
|
|
{'type': 'text', 'text': 'Hallo'},
|
|
],
|
|
},
|
|
],
|
|
},
|
|
});
|
|
|
|
expect(response.available, isTrue);
|
|
expect(response.schemaVersion, 1);
|
|
expect(response.webUrl, '/ticker');
|
|
expect(response.content, isA<Map<String, dynamic>>());
|
|
expect(response.content!['type'], 'doc');
|
|
});
|
|
|
|
test('unavailable post keeps content null and defaults safely', () {
|
|
final response = TickerResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'available': false,
|
|
'webUrl': '/ticker',
|
|
'content': null,
|
|
});
|
|
|
|
expect(response.available, isFalse);
|
|
expect(response.content, isNull);
|
|
expect(response.hash, isNull);
|
|
});
|
|
});
|
|
|
|
group('TickerNavResponse.fromJson', () {
|
|
test('parses sections with per-kind fields', () {
|
|
final nav = TickerNavResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'navHash': 'nav-abc',
|
|
'sections': [
|
|
{
|
|
'title': 'Informationen',
|
|
'pages': [
|
|
{'title': 'Über uns', 'slug': 'ueber-uns', 'kind': 'CONTENT'},
|
|
{
|
|
'title': 'Webseite',
|
|
'slug': 'web',
|
|
'kind': 'REDIRECT',
|
|
'externalUrl': 'https://marianum-fulda.de',
|
|
'externalUrlNewTab': true,
|
|
},
|
|
{
|
|
'title': 'Flyer',
|
|
'slug': 'flyer',
|
|
'kind': 'PROXIED_FILE',
|
|
'fileUrl': 'ticker/pages/flyer/file',
|
|
},
|
|
],
|
|
},
|
|
],
|
|
});
|
|
|
|
expect(nav.navHash, 'nav-abc');
|
|
expect(nav.sections, hasLength(1));
|
|
final pages = nav.sections.single.pages;
|
|
expect(pages.map((p) => p.kind), ['CONTENT', 'REDIRECT', 'PROXIED_FILE']);
|
|
expect(pages[1].externalUrl, 'https://marianum-fulda.de');
|
|
expect(pages[1].externalUrlNewTab, isTrue);
|
|
expect(pages[2].fileUrl, 'ticker/pages/flyer/file');
|
|
});
|
|
|
|
test('missing sections default to empty', () {
|
|
final nav = TickerNavResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'navHash': '',
|
|
});
|
|
expect(nav.sections, isEmpty);
|
|
});
|
|
});
|
|
|
|
group('TickerPageResponse.fromJson', () {
|
|
test('CONTENT page exposes the nested document', () {
|
|
final page = TickerPageResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'slug': 'ueber-uns',
|
|
'title': 'Über uns',
|
|
'kind': 'CONTENT',
|
|
'content': {'type': 'doc', 'content': []},
|
|
});
|
|
expect(page.kind, TickerPageKind.content);
|
|
expect(page.content, isNotNull);
|
|
});
|
|
|
|
test('REDIRECT page exposes the external URL', () {
|
|
final page = TickerPageResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'kind': 'REDIRECT',
|
|
'externalUrl': 'https://marianum-fulda.de',
|
|
});
|
|
expect(page.kind, TickerPageKind.redirect);
|
|
expect(page.externalUrl, 'https://marianum-fulda.de');
|
|
});
|
|
|
|
test('PROXIED_FILE page exposes file metadata', () {
|
|
final page = TickerPageResponse.fromJson({
|
|
'schemaVersion': 1,
|
|
'kind': 'PROXIED_FILE',
|
|
'fileUrl': 'ticker/pages/flyer/file',
|
|
'contentType': 'application/pdf',
|
|
'filename': 'flyer.pdf',
|
|
});
|
|
expect(page.kind, TickerPageKind.proxiedFile);
|
|
expect(page.contentType, 'application/pdf');
|
|
expect(page.filename, 'flyer.pdf');
|
|
});
|
|
});
|
|
|
|
group('GetTickerPage error mapping', () {
|
|
test('404 CONTENT_UNAVAILABLE maps to a typed exception with webUrl', () async {
|
|
final requestOptions = RequestOptions(path: 'ticker/pages/foo');
|
|
final dio = _ThrowingDio(
|
|
DioException(
|
|
requestOptions: requestOptions,
|
|
type: DioExceptionType.badResponse,
|
|
response: Response<dynamic>(
|
|
requestOptions: requestOptions,
|
|
statusCode: 404,
|
|
data: {'error': 'CONTENT_UNAVAILABLE', 'webUrl': '/ticker/p/foo'},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
() => GetTickerPage('foo', dio: dio).run(),
|
|
throwsA(
|
|
isA<TickerContentUnavailableException>()
|
|
.having((e) => e.webUrl, 'webUrl', '/ticker/p/foo')
|
|
.having((e) => e.allowRetry, 'allowRetry', isFalse),
|
|
),
|
|
);
|
|
});
|
|
|
|
test('other 404 (NOT_FOUND) does not become CONTENT_UNAVAILABLE', () async {
|
|
final requestOptions = RequestOptions(path: 'ticker/pages/foo');
|
|
final dio = _ThrowingDio(
|
|
DioException(
|
|
requestOptions: requestOptions,
|
|
type: DioExceptionType.badResponse,
|
|
response: Response<dynamic>(
|
|
requestOptions: requestOptions,
|
|
statusCode: 404,
|
|
data: {'error': 'NOT_FOUND', 'webUrl': '/ticker/p/foo'},
|
|
),
|
|
),
|
|
);
|
|
|
|
expect(
|
|
() => GetTickerPage('foo', dio: dio).run(),
|
|
throwsA(isNot(isA<TickerContentUnavailableException>())),
|
|
);
|
|
});
|
|
});
|
|
}
|
|
|
|
/// Minimal fake Dio whose `get` always fails with the given exception; every
|
|
/// other member is unused.
|
|
class _ThrowingDio implements Dio {
|
|
final DioException error;
|
|
|
|
_ThrowingDio(this.error);
|
|
|
|
@override
|
|
Future<Response<T>> get<T>(
|
|
String path, {
|
|
Object? data,
|
|
Map<String, dynamic>? queryParameters,
|
|
Options? options,
|
|
CancelToken? cancelToken,
|
|
ProgressCallback? onReceiveProgress,
|
|
}) => Future<Response<T>>.error(error);
|
|
|
|
@override
|
|
dynamic noSuchMethod(Invocation invocation) =>
|
|
super.noSuchMethod(invocation);
|
|
}
|