From 55759cd5119b6229df14428696dbf0f78a2e2880 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 12:42:46 +0200 Subject: [PATCH 1/6] test(nextcloud): refactor test file structure Signed-off-by: Nikolas Rimikis --- .../{ => api/cookbook}/cookbook_test.dart | 0 .../test/{ => api/core}/core_test.dart | 0 .../{ => api/dashboard}/dashboard_test.dart | 0 .../test/{ => api/news}/news_test.dart | 0 .../test/{ => api/notes}/notes_test.dart | 0 .../notifications}/notifications_test.dart | 0 .../provisioning_api_test.dart | 0 .../{ => api/settings}/settings_test.dart | 0 .../test/{ => api/spreed}/spreed_test.dart | 0 .../test/{ => api/tables}/tables_test.dart | 0 .../test/{ => api/uppush}/uppush_test.dart | 0 .../user_status}/user_status_test.dart | 0 .../weather_status}/weather_status_test.dart | 0 .../test/api/webdav/csrf_client_test.dart | 20 +++++++++++ .../test/{ => api/webdav}/webdav_test.dart | 0 .../test/client_conformance_test.dart | 33 ------------------- .../test/{ => models}/version_check_test.dart | 0 ...t_test.dart => nextcloud_client_test.dart} | 15 +++++++++ .../test/utils/cookie_jar_client_test.dart | 22 +++++++++++++ .../date_time_test.dart} | 0 .../http_date_parser_test.dart} | 0 21 files changed, 57 insertions(+), 33 deletions(-) rename packages/nextcloud/test/{ => api/cookbook}/cookbook_test.dart (100%) rename packages/nextcloud/test/{ => api/core}/core_test.dart (100%) rename packages/nextcloud/test/{ => api/dashboard}/dashboard_test.dart (100%) rename packages/nextcloud/test/{ => api/news}/news_test.dart (100%) rename packages/nextcloud/test/{ => api/notes}/notes_test.dart (100%) rename packages/nextcloud/test/{ => api/notifications}/notifications_test.dart (100%) rename packages/nextcloud/test/{ => api/provisioning_api}/provisioning_api_test.dart (100%) rename packages/nextcloud/test/{ => api/settings}/settings_test.dart (100%) rename packages/nextcloud/test/{ => api/spreed}/spreed_test.dart (100%) rename packages/nextcloud/test/{ => api/tables}/tables_test.dart (100%) rename packages/nextcloud/test/{ => api/uppush}/uppush_test.dart (100%) rename packages/nextcloud/test/{ => api/user_status}/user_status_test.dart (100%) rename packages/nextcloud/test/{ => api/weather_status}/weather_status_test.dart (100%) create mode 100644 packages/nextcloud/test/api/webdav/csrf_client_test.dart rename packages/nextcloud/test/{ => api/webdav}/webdav_test.dart (100%) delete mode 100644 packages/nextcloud/test/client_conformance_test.dart rename packages/nextcloud/test/{ => models}/version_check_test.dart (100%) rename packages/nextcloud/test/{client_test.dart => nextcloud_client_test.dart} (89%) create mode 100644 packages/nextcloud/test/utils/cookie_jar_client_test.dart rename packages/nextcloud/test/{utils_test.dart => utils/date_time_test.dart} (100%) rename packages/nextcloud/test/{http_date_test.dart => utils/http_date_parser_test.dart} (100%) diff --git a/packages/nextcloud/test/cookbook_test.dart b/packages/nextcloud/test/api/cookbook/cookbook_test.dart similarity index 100% rename from packages/nextcloud/test/cookbook_test.dart rename to packages/nextcloud/test/api/cookbook/cookbook_test.dart diff --git a/packages/nextcloud/test/core_test.dart b/packages/nextcloud/test/api/core/core_test.dart similarity index 100% rename from packages/nextcloud/test/core_test.dart rename to packages/nextcloud/test/api/core/core_test.dart diff --git a/packages/nextcloud/test/dashboard_test.dart b/packages/nextcloud/test/api/dashboard/dashboard_test.dart similarity index 100% rename from packages/nextcloud/test/dashboard_test.dart rename to packages/nextcloud/test/api/dashboard/dashboard_test.dart diff --git a/packages/nextcloud/test/news_test.dart b/packages/nextcloud/test/api/news/news_test.dart similarity index 100% rename from packages/nextcloud/test/news_test.dart rename to packages/nextcloud/test/api/news/news_test.dart diff --git a/packages/nextcloud/test/notes_test.dart b/packages/nextcloud/test/api/notes/notes_test.dart similarity index 100% rename from packages/nextcloud/test/notes_test.dart rename to packages/nextcloud/test/api/notes/notes_test.dart diff --git a/packages/nextcloud/test/notifications_test.dart b/packages/nextcloud/test/api/notifications/notifications_test.dart similarity index 100% rename from packages/nextcloud/test/notifications_test.dart rename to packages/nextcloud/test/api/notifications/notifications_test.dart diff --git a/packages/nextcloud/test/provisioning_api_test.dart b/packages/nextcloud/test/api/provisioning_api/provisioning_api_test.dart similarity index 100% rename from packages/nextcloud/test/provisioning_api_test.dart rename to packages/nextcloud/test/api/provisioning_api/provisioning_api_test.dart diff --git a/packages/nextcloud/test/settings_test.dart b/packages/nextcloud/test/api/settings/settings_test.dart similarity index 100% rename from packages/nextcloud/test/settings_test.dart rename to packages/nextcloud/test/api/settings/settings_test.dart diff --git a/packages/nextcloud/test/spreed_test.dart b/packages/nextcloud/test/api/spreed/spreed_test.dart similarity index 100% rename from packages/nextcloud/test/spreed_test.dart rename to packages/nextcloud/test/api/spreed/spreed_test.dart diff --git a/packages/nextcloud/test/tables_test.dart b/packages/nextcloud/test/api/tables/tables_test.dart similarity index 100% rename from packages/nextcloud/test/tables_test.dart rename to packages/nextcloud/test/api/tables/tables_test.dart diff --git a/packages/nextcloud/test/uppush_test.dart b/packages/nextcloud/test/api/uppush/uppush_test.dart similarity index 100% rename from packages/nextcloud/test/uppush_test.dart rename to packages/nextcloud/test/api/uppush/uppush_test.dart diff --git a/packages/nextcloud/test/user_status_test.dart b/packages/nextcloud/test/api/user_status/user_status_test.dart similarity index 100% rename from packages/nextcloud/test/user_status_test.dart rename to packages/nextcloud/test/api/user_status/user_status_test.dart diff --git a/packages/nextcloud/test/weather_status_test.dart b/packages/nextcloud/test/api/weather_status/weather_status_test.dart similarity index 100% rename from packages/nextcloud/test/weather_status_test.dart rename to packages/nextcloud/test/api/weather_status/weather_status_test.dart diff --git a/packages/nextcloud/test/api/webdav/csrf_client_test.dart b/packages/nextcloud/test/api/webdav/csrf_client_test.dart new file mode 100644 index 00000000000..ba5471f7b16 --- /dev/null +++ b/packages/nextcloud/test/api/webdav/csrf_client_test.dart @@ -0,0 +1,20 @@ +import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; +import 'package:nextcloud/nextcloud.dart'; +import 'package:nextcloud/src/api/webdav/webdav.dart'; +import 'package:test/test.dart'; + +void main() { + group( + 'Client conformance tests', + () { + testAll( + () => WebDavCSRFClient(NextcloudClient(Uri())), + canReceiveSetCookieHeaders: true, + canSendCookieHeaders: true, + ); + }, + onPlatform: const { + 'browser': [Skip()], + }, + ); +} diff --git a/packages/nextcloud/test/webdav_test.dart b/packages/nextcloud/test/api/webdav/webdav_test.dart similarity index 100% rename from packages/nextcloud/test/webdav_test.dart rename to packages/nextcloud/test/api/webdav/webdav_test.dart diff --git a/packages/nextcloud/test/client_conformance_test.dart b/packages/nextcloud/test/client_conformance_test.dart deleted file mode 100644 index 16b90f20721..00000000000 --- a/packages/nextcloud/test/client_conformance_test.dart +++ /dev/null @@ -1,33 +0,0 @@ -@TestOn('vm') -library; - -import 'package:cookie_jar/cookie_jar.dart'; -import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; -import 'package:nextcloud/nextcloud.dart'; -import 'package:nextcloud/src/api/webdav/webdav.dart'; -import 'package:nextcloud/src/utils/cookie_jar_client.dart'; -import 'package:test/test.dart'; - -void main() { - group('Client conformance tests', () { - testAll( - () => NextcloudClient(Uri()), - canReceiveSetCookieHeaders: true, - canSendCookieHeaders: true, - ); - - testAll( - () => CookieJarClient( - cookieJar: CookieJar(), - ), - canReceiveSetCookieHeaders: true, - canSendCookieHeaders: true, - ); - - testAll( - () => WebDavCSRFClient(NextcloudClient(Uri())), - canReceiveSetCookieHeaders: true, - canSendCookieHeaders: true, - ); - }); -} diff --git a/packages/nextcloud/test/version_check_test.dart b/packages/nextcloud/test/models/version_check_test.dart similarity index 100% rename from packages/nextcloud/test/version_check_test.dart rename to packages/nextcloud/test/models/version_check_test.dart diff --git a/packages/nextcloud/test/client_test.dart b/packages/nextcloud/test/nextcloud_client_test.dart similarity index 89% rename from packages/nextcloud/test/client_test.dart rename to packages/nextcloud/test/nextcloud_client_test.dart index 0b7126f80f4..c37d849c8a3 100644 --- a/packages/nextcloud/test/client_test.dart +++ b/packages/nextcloud/test/nextcloud_client_test.dart @@ -1,12 +1,27 @@ import 'package:cookie_jar/cookie_jar.dart'; import 'package:http/http.dart'; import 'package:http/testing.dart'; +import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; import 'package:nextcloud/nextcloud.dart'; import 'package:test/test.dart'; void main() { final uri = Uri.parse('http://example.com'); group(NextcloudClient, () { + group( + 'Client conformance tests', + () { + testAll( + () => NextcloudClient(Uri()), + canReceiveSetCookieHeaders: true, + canSendCookieHeaders: true, + ); + }, + onPlatform: const { + 'browser': [Skip()], + }, + ); + group('Cookies', () { late CookieJar cookieJar; setUp(() { diff --git a/packages/nextcloud/test/utils/cookie_jar_client_test.dart b/packages/nextcloud/test/utils/cookie_jar_client_test.dart new file mode 100644 index 00000000000..edf983c5dfc --- /dev/null +++ b/packages/nextcloud/test/utils/cookie_jar_client_test.dart @@ -0,0 +1,22 @@ +import 'package:cookie_jar/cookie_jar.dart'; +import 'package:http_client_conformance_tests/http_client_conformance_tests.dart'; +import 'package:nextcloud/src/utils/cookie_jar_client.dart'; +import 'package:test/test.dart'; + +void main() { + group( + 'Client conformance tests', + () { + testAll( + () => CookieJarClient( + cookieJar: CookieJar(), + ), + canReceiveSetCookieHeaders: true, + canSendCookieHeaders: true, + ); + }, + onPlatform: const { + 'browser': [Skip()], + }, + ); +} diff --git a/packages/nextcloud/test/utils_test.dart b/packages/nextcloud/test/utils/date_time_test.dart similarity index 100% rename from packages/nextcloud/test/utils_test.dart rename to packages/nextcloud/test/utils/date_time_test.dart diff --git a/packages/nextcloud/test/http_date_test.dart b/packages/nextcloud/test/utils/http_date_parser_test.dart similarity index 100% rename from packages/nextcloud/test/http_date_test.dart rename to packages/nextcloud/test/utils/http_date_parser_test.dart From 7c00aa3d33fc18f8ec5fd6faccdd03dfe9abbed5 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 17:51:49 +0200 Subject: [PATCH 2/6] test(nextcloud): unit test notifications helpers Signed-off-by: Nikolas Rimikis --- .../notifications/notifications_helpers.dart | 29 ++-- .../notifications_helpers.g.dart | 1 - .../notifications_helpers_test.dart | 146 ++++++++++++++++++ 3 files changed, 162 insertions(+), 14 deletions(-) create mode 100644 packages/nextcloud/test/api/notifications/notifications_helpers_test.dart diff --git a/packages/nextcloud/lib/src/api/notifications/notifications_helpers.dart b/packages/nextcloud/lib/src/api/notifications/notifications_helpers.dart index c7f0bd96a6b..8b9fcb19076 100644 --- a/packages/nextcloud/lib/src/api/notifications/notifications_helpers.dart +++ b/packages/nextcloud/lib/src/api/notifications/notifications_helpers.dart @@ -10,17 +10,18 @@ import 'package:crypton/crypton.dart'; part 'notifications_helpers.g.dart'; -/// Generates the push token hash which is just sha512 +/// Generates the push token hash which is just sha512. String generatePushTokenHash(String pushToken) => sha512.convert(utf8.encode(pushToken)).toString(); +/// {@template decrypted_subject} /// Decrypted version of the encrypted push notification received from the server. +/// {@endtemplate} abstract class DecryptedSubject implements Built { - // ignore: public_member_api_docs + /// {@macro decrypted_subject} factory DecryptedSubject([void Function(DecryptedSubjectBuilder)? updates]) = _$DecryptedSubject; - const DecryptedSubject._(); - /// Decrypts the subject of a push notification + /// Decrypts the subject of a push notification. factory DecryptedSubject.fromEncrypted(RSAPrivateKey privateKey, String subject) => DecryptedSubject.fromJson( json.decode(privateKey.decrypt(subject)) as Map, ); @@ -34,28 +35,30 @@ abstract class DecryptedSubject implements Built get serializer => _$decryptedSubjectSerializer; - /// ID if the notification + /// ID if the notification. int? get nid; - /// App that sent the notification + /// App that sent the notification. String? get app; - /// Subject of the notification + /// Subject of the notification. String? get subject; - /// Type of the notification + /// Type of the notification. String? get type; - /// ID of the notification + /// ID of the notification. String? get id; - /// Delete the notification + /// Delete the notification. bool? get delete; - /// Delete all notifications + /// Delete all notifications. @BuiltValueField(wireName: 'delete-all') bool? get deleteAll; } -@SerializersFor([DecryptedSubject]) -final Serializers _serializers = (_$_serializers.toBuilder()..addPlugin(StandardJsonPlugin())).build(); +final Serializers _serializers = (Serializers().toBuilder() + ..add(DecryptedSubject.serializer) + ..addPlugin(StandardJsonPlugin())) + .build(); diff --git a/packages/nextcloud/lib/src/api/notifications/notifications_helpers.g.dart b/packages/nextcloud/lib/src/api/notifications/notifications_helpers.g.dart index 4823263f49c..372dcf1d3b3 100644 --- a/packages/nextcloud/lib/src/api/notifications/notifications_helpers.g.dart +++ b/packages/nextcloud/lib/src/api/notifications/notifications_helpers.g.dart @@ -6,7 +6,6 @@ part of 'notifications_helpers.dart'; // BuiltValueGenerator // ************************************************************************** -Serializers _$_serializers = (Serializers().toBuilder()..add(DecryptedSubject.serializer)).build(); Serializer _$decryptedSubjectSerializer = _$DecryptedSubjectSerializer(); class _$DecryptedSubjectSerializer implements StructuredSerializer { diff --git a/packages/nextcloud/test/api/notifications/notifications_helpers_test.dart b/packages/nextcloud/test/api/notifications/notifications_helpers_test.dart new file mode 100644 index 00000000000..6d6f6a2b77e --- /dev/null +++ b/packages/nextcloud/test/api/notifications/notifications_helpers_test.dart @@ -0,0 +1,146 @@ +import 'package:built_value_test/matcher.dart'; +import 'package:nextcloud/notifications.dart'; +import 'package:test/test.dart'; + +void main() { + test('generatePushTokenHash', () { + expect( + generatePushTokenHash('789'), + equals( + 'ca9879bd727ba3bd815f05fe6b9e4640c774b61cc8f141295542cefc1b7b8fc6e3daf3f656555cdec355894e7af48964e88994d960f41ba8f61f7a05d5ddbd07', + ), + ); + }); + + group('DecryptedSubject', () { + DecryptedSubject createDecryptedSubject({ + int? nid = 1, + String? app = 'app', + String? subject = 'subject', + String? type = 'type', + String? id = 'id', + bool? delete = false, + bool? deleteAll = false, + }) { + return DecryptedSubject((b) { + b + ..nid = nid + ..app = app + ..subject = subject + ..type = type + ..id = id + ..delete = delete + ..deleteAll = deleteAll; + }); + } + + group('constructor', () { + test('works correctly', () { + expect( + createDecryptedSubject, + returnsNormally, + ); + }); + }); + + test('supports value equality', () { + expect( + createDecryptedSubject(), + equalsBuilt(createDecryptedSubject()), + ); + + expect( + createDecryptedSubject().hashCode, + equals(createDecryptedSubject().hashCode), + ); + + expect( + createDecryptedSubject(), + isNot(equalsBuilt(createDecryptedSubject(deleteAll: true))), + ); + }); + + group('rebuild', () { + test('returns the same object if not attributes are changed', () { + expect( + createDecryptedSubject().rebuild((_) {}), + equalsBuilt(createDecryptedSubject()), + ); + }); + + test('replaces every attribute', () { + expect( + createDecryptedSubject().rebuild((b) { + b + ..nid = 2 + ..app = 'new-app' + ..subject = 'new-subject' + ..type = 'new-type' + ..id = 'new-id' + ..delete = true + ..deleteAll = true; + }), + equalsBuilt( + createDecryptedSubject( + nid: 2, + app: 'new-app', + subject: 'new-subject', + type: 'new-type', + id: 'new-id', + delete: true, + deleteAll: true, + ), + ), + ); + }); + }); + + group('serialization', () { + test('fromJson works correctly', () { + expect( + DecryptedSubject.fromJson({ + 'nid': 1, + 'app': 'app', + 'subject': 'subject', + 'type': 'type', + 'id': 'id', + 'delete': false, + 'delete-all': false, + }), + equalsBuilt(createDecryptedSubject()), + ); + }); + + test('toJson works correctly', () { + expect( + createDecryptedSubject().toJson(), + equals({ + 'nid': 1, + 'app': 'app', + 'subject': 'subject', + 'type': 'type', + 'id': 'id', + 'delete': false, + 'delete-all': false, + }), + ); + }); + }); + + test('to string', () { + expect( + createDecryptedSubject().toString(), + equals(''' +DecryptedSubject { + nid=1, + app=app, + subject=subject, + type=type, + id=id, + delete=false, + deleteAll=false, +}'''), + ); + }); + }); +} From c7fb28cb2fd570bf1a18858f476c9b7fab1d0930 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 17:54:51 +0200 Subject: [PATCH 3/6] test(nextcloud): explicitly unit test spreed helpers Signed-off-by: Nikolas Rimikis --- .../lib/src/api/spreed/spreed_helpers.dart | 4 +- .../test/api/spreed/spreed_helpers_test.dart | 101 ++++++++++++++++++ 2 files changed, 104 insertions(+), 1 deletion(-) create mode 100644 packages/nextcloud/test/api/spreed/spreed_helpers_test.dart diff --git a/packages/nextcloud/lib/src/api/spreed/spreed_helpers.dart b/packages/nextcloud/lib/src/api/spreed/spreed_helpers.dart index ca7129b934c..35cf6a741b5 100644 --- a/packages/nextcloud/lib/src/api/spreed/spreed_helpers.dart +++ b/packages/nextcloud/lib/src/api/spreed/spreed_helpers.dart @@ -154,6 +154,8 @@ enum ParticipantInCallFlag { extension EnumByBinary on List { /// Converts the binary representation of an enum into enum values. Set byBinary(int value) { + RangeError.checkNotNegative(value, 'value'); + final result = {}; var v = value; @@ -187,5 +189,5 @@ extension EnumCollectionBinary on Set { /// /// See [EnumBinary.binary] for getting the binary representation of a single enum value. /// See [EnumByBinary.byBinary] for converting the binary representation into enum values. - int get binary => map((p) => p.binary).reduce((a, b) => a | b); + int get binary => map((p) => p.binary).fold(0, (a, b) => a | b); } diff --git a/packages/nextcloud/test/api/spreed/spreed_helpers_test.dart b/packages/nextcloud/test/api/spreed/spreed_helpers_test.dart new file mode 100644 index 00000000000..ceb2cb593d7 --- /dev/null +++ b/packages/nextcloud/test/api/spreed/spreed_helpers_test.dart @@ -0,0 +1,101 @@ +import 'package:nextcloud/spreed.dart'; +import 'package:test/test.dart'; + +enum _TestEnum { + one, + two, + three; +} + +void main() { + test('EnumBinary', () { + expect( + _TestEnum.one.binary, + equals(int.parse('000', radix: 2)), + ); + + expect( + _TestEnum.two.binary, + equals(int.parse('001', radix: 2)), + ); + + expect( + _TestEnum.three.binary, + equals(int.parse('010', radix: 2)), + ); + }); + + test('EnumCollectionBinary', () { + expect( + {}.binary, + equals(int.parse('000', radix: 2)), + ); + + expect( + {_TestEnum.one}.binary, + equals(int.parse('000', radix: 2)), + ); + + expect( + {_TestEnum.two, _TestEnum.three}.binary, + equals(int.parse('011', radix: 2)), + ); + }); + + test('EnumByBinary', () { + expect( + _TestEnum.values.byBinary(int.parse('000', radix: 2)), + equals({_TestEnum.one}), + ); + + expect( + _TestEnum.values.byBinary(int.parse('011', radix: 2)), + equals({_TestEnum.two, _TestEnum.three}), + ); + + expect( + _TestEnum.values.byBinary(int.parse('100', radix: 2)), + equals({_TestEnum.one}), + reason: 'When the server adds another entry but the client does not support it yet it is quietly ignored', + ); + + expect( + () => _TestEnum.values.byBinary(-1), + throwsRangeError, + ); + }); + + group('RoomType', () { + group('value -> fromValue roundtrip', () { + for (final type in RoomType.values) { + test(type.name, () { + expect( + RoomType.fromValue(type.value), + equals(type), + ); + }); + } + }); + group('isSingleUser', () { + for (final type in RoomType.values) { + test(type.name, () { + expect( + type.isSingleUser, + type == RoomType.group || type == RoomType.public ? isFalse : isTrue, + ); + }); + } + }); + }); + + group('ParticipantType value -> fromValue roundtrip', () { + for (final type in ParticipantType.values) { + test(type.name, () { + expect( + ParticipantType.fromValue(type.value), + equals(type), + ); + }); + } + }); +} From ae70c87b4ef62d8c6dda1678014821490b850174 Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 17:55:42 +0200 Subject: [PATCH 4/6] style(nextcloud): fix tables test formatting Signed-off-by: Nikolas Rimikis --- .../test/api/tables/tables_test.dart | 128 +++++++++--------- 1 file changed, 62 insertions(+), 66 deletions(-) diff --git a/packages/nextcloud/test/api/tables/tables_test.dart b/packages/nextcloud/test/api/tables/tables_test.dart index 629b2f6ef04..42962e52d69 100644 --- a/packages/nextcloud/test/api/tables/tables_test.dart +++ b/packages/nextcloud/test/api/tables/tables_test.dart @@ -6,78 +6,74 @@ import 'package:timezone/timezone.dart' as tz; import 'package:version/version.dart'; void main() { - presets( - 'tables', - 'tables', - (tester) { - test('Is supported', () async { - final response = await tester.client.core.ocs.getCapabilities(); + presets('tables', 'tables', (tester) { + test('Is supported', () async { + final response = await tester.client.core.ocs.getCapabilities(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - final result = tester.client.tables.getVersionCheck(response.body.ocs.data); - expect(result.versions, isNotNull); - expect(result.versions, isNotEmpty); - expect(result.isSupported, isTrue); - }); + final result = tester.client.tables.getVersionCheck(response.body.ocs.data); + expect(result.versions, isNotNull); + expect(result.versions, isNotEmpty); + expect(result.isSupported, isTrue); + }); - group('v1', () { - group('Tables', () { - test('Create and destroy', () async { - final createResponse = await tester.client.tables.api1.createTable( - $body: tables.Api1CreateTableRequestApplicationJson( - (b) => b - ..title = 'title' - ..emoji = '😀' - ..template = '', - ), - ); - expect(createResponse.statusCode, 200); - expect(() => createResponse.headers, isA()); + group('v1', () { + group('Tables', () { + test('Create and destroy', () async { + final createResponse = await tester.client.tables.api1.createTable( + $body: tables.Api1CreateTableRequestApplicationJson( + (b) => b + ..title = 'title' + ..emoji = '😀' + ..template = '', + ), + ); + expect(createResponse.statusCode, 200); + expect(() => createResponse.headers, isA()); - expect(createResponse.body.id, isPositive); - expect(createResponse.body.title, 'title'); - expect(createResponse.body.emoji, '😀'); - expect(createResponse.body.ownership, 'user1'); - expect(createResponse.body.ownerDisplayName, 'User One'); - expect(createResponse.body.createdBy, 'user1'); - expect(tz.TZDateTime.parse(tz.UTC, createResponse.body.createdAt).isBefore(DateTime.timestamp()), isTrue); - expect(createResponse.body.lastEditBy, 'user1'); - expect(tz.TZDateTime.parse(tz.UTC, createResponse.body.lastEditAt).isBefore(DateTime.timestamp()), isTrue); - expect(createResponse.body.archived, tester.version < Version(0, 7, 0) ? null : false); - expect(createResponse.body.favorite, tester.version < Version(0, 7, 0) ? null : false); - expect(createResponse.body.isShared, false); - expect(createResponse.body.hasShares, false); - expect(createResponse.body.rowsCount, 0); - expect(createResponse.body.views, isEmpty); - expect(createResponse.body.columnsCount, 0); + expect(createResponse.body.id, isPositive); + expect(createResponse.body.title, 'title'); + expect(createResponse.body.emoji, '😀'); + expect(createResponse.body.ownership, 'user1'); + expect(createResponse.body.ownerDisplayName, 'User One'); + expect(createResponse.body.createdBy, 'user1'); + expect(tz.TZDateTime.parse(tz.UTC, createResponse.body.createdAt).isBefore(DateTime.timestamp()), isTrue); + expect(createResponse.body.lastEditBy, 'user1'); + expect(tz.TZDateTime.parse(tz.UTC, createResponse.body.lastEditAt).isBefore(DateTime.timestamp()), isTrue); + expect(createResponse.body.archived, tester.version < Version(0, 7, 0) ? null : false); + expect(createResponse.body.favorite, tester.version < Version(0, 7, 0) ? null : false); + expect(createResponse.body.isShared, false); + expect(createResponse.body.hasShares, false); + expect(createResponse.body.rowsCount, 0); + expect(createResponse.body.views, isEmpty); + expect(createResponse.body.columnsCount, 0); - final deleteResponse = await tester.client.tables.api1.deleteTable( - tableId: createResponse.body.id, - ); - expect(deleteResponse.statusCode, 200); - expect(() => deleteResponse.headers, isA()); + final deleteResponse = await tester.client.tables.api1.deleteTable( + tableId: createResponse.body.id, + ); + expect(deleteResponse.statusCode, 200); + expect(() => deleteResponse.headers, isA()); - expect(deleteResponse.body.id, isPositive); - expect(deleteResponse.body.title, 'title'); - expect(deleteResponse.body.emoji, '😀'); - expect(deleteResponse.body.ownership, 'user1'); - expect(deleteResponse.body.ownerDisplayName, tester.version < Version(0, 7, 0) ? null : ''); - expect(deleteResponse.body.createdBy, 'user1'); - expect(tz.TZDateTime.parse(tz.UTC, deleteResponse.body.createdAt).isBefore(DateTime.timestamp()), isTrue); - expect(deleteResponse.body.lastEditBy, 'user1'); - expect(tz.TZDateTime.parse(tz.UTC, deleteResponse.body.lastEditAt).isBefore(DateTime.timestamp()), isTrue); - expect(deleteResponse.body.archived, tester.version < Version(0, 7, 0) ? null : false); - expect(deleteResponse.body.favorite, tester.version < Version(0, 7, 0) ? null : false); - expect(deleteResponse.body.isShared, false); - expect(deleteResponse.body.hasShares, false); - expect(deleteResponse.body.rowsCount, 0); - expect(deleteResponse.body.views, isEmpty); - expect(deleteResponse.body.columnsCount, 0); - }); + expect(deleteResponse.body.id, isPositive); + expect(deleteResponse.body.title, 'title'); + expect(deleteResponse.body.emoji, '😀'); + expect(deleteResponse.body.ownership, 'user1'); + expect(deleteResponse.body.ownerDisplayName, tester.version < Version(0, 7, 0) ? null : ''); + expect(deleteResponse.body.createdBy, 'user1'); + expect(tz.TZDateTime.parse(tz.UTC, deleteResponse.body.createdAt).isBefore(DateTime.timestamp()), isTrue); + expect(deleteResponse.body.lastEditBy, 'user1'); + expect(tz.TZDateTime.parse(tz.UTC, deleteResponse.body.lastEditAt).isBefore(DateTime.timestamp()), isTrue); + expect(deleteResponse.body.archived, tester.version < Version(0, 7, 0) ? null : false); + expect(deleteResponse.body.favorite, tester.version < Version(0, 7, 0) ? null : false); + expect(deleteResponse.body.isShared, false); + expect(deleteResponse.body.hasShares, false); + expect(deleteResponse.body.rowsCount, 0); + expect(deleteResponse.body.views, isEmpty); + expect(deleteResponse.body.columnsCount, 0); }); }); - }, - ); + }); + }); } From 85832afd93e804b2a8859974549671a37140edff Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 17:57:51 +0200 Subject: [PATCH 5/6] test(nextcloud): split webdav tests Signed-off-by: Nikolas Rimikis --- .../lib/src/api/webdav/models/props.dart | 1 - .../lib/src/api/webdav/utils/utils.dart | 1 + .../utils/webdav_response_converter.dart | 8 +- .../webdav/utils/xml_date_time_converter.dart | 170 ++++++++++++++++++ .../nextcloud/lib/src/utils/date_time.dart | 170 ------------------ packages/nextcloud/lib/webdav.dart | 9 +- .../test/api/webdav/models/path_uri_test.dart | 91 ++++++++++ .../api/webdav/models/webdav_depth_test.dart | 10 ++ .../api/webdav/utils/webdav_uri_test.dart | 43 +++++ .../test/api/webdav/webdav_test.dart | 129 +------------ 10 files changed, 327 insertions(+), 305 deletions(-) create mode 100644 packages/nextcloud/lib/src/api/webdav/utils/xml_date_time_converter.dart create mode 100644 packages/nextcloud/test/api/webdav/models/path_uri_test.dart create mode 100644 packages/nextcloud/test/api/webdav/models/webdav_depth_test.dart create mode 100644 packages/nextcloud/test/api/webdav/utils/webdav_uri_test.dart diff --git a/packages/nextcloud/lib/src/api/webdav/models/props.dart b/packages/nextcloud/lib/src/api/webdav/models/props.dart index 7e579d83c05..63b79eb176d 100644 --- a/packages/nextcloud/lib/src/api/webdav/models/props.dart +++ b/packages/nextcloud/lib/src/api/webdav/models/props.dart @@ -2,7 +2,6 @@ // coverage:ignore-file import 'package:meta/meta.dart'; import 'package:nextcloud/src/api/webdav/webdav.dart'; -import 'package:nextcloud/src/utils/date_time.dart'; import 'package:timezone/timezone.dart' as tz; import 'package:xml/xml.dart'; import 'package:xml_annotation/xml_annotation.dart' as annotation; diff --git a/packages/nextcloud/lib/src/api/webdav/utils/utils.dart b/packages/nextcloud/lib/src/api/webdav/utils/utils.dart index 6bb0f390d43..e3fe51ab46c 100644 --- a/packages/nextcloud/lib/src/api/webdav/utils/utils.dart +++ b/packages/nextcloud/lib/src/api/webdav/utils/utils.dart @@ -1,4 +1,5 @@ export 'options_parser.dart'; export 'webdav_response_converter.dart'; export 'webdav_uri.dart'; +export 'xml_date_time_converter.dart'; export 'xml_duration_converter.dart'; diff --git a/packages/nextcloud/lib/src/api/webdav/utils/webdav_response_converter.dart b/packages/nextcloud/lib/src/api/webdav/utils/webdav_response_converter.dart index f98f8eacd61..f625d38a4d5 100644 --- a/packages/nextcloud/lib/src/api/webdav/utils/webdav_response_converter.dart +++ b/packages/nextcloud/lib/src/api/webdav/utils/webdav_response_converter.dart @@ -13,13 +13,11 @@ final class WebDavResponseConverter with Converter element is XmlElement) as XmlElement; + final xml = _converter.convert(input.body).firstWhere((element) => element is XmlElement) as XmlElement; return WebDavMultistatus.fromXmlElement(xml); } diff --git a/packages/nextcloud/lib/src/api/webdav/utils/xml_date_time_converter.dart b/packages/nextcloud/lib/src/api/webdav/utils/xml_date_time_converter.dart new file mode 100644 index 00000000000..903b60c9103 --- /dev/null +++ b/packages/nextcloud/lib/src/api/webdav/utils/xml_date_time_converter.dart @@ -0,0 +1,170 @@ +import 'package:meta/meta.dart'; +import 'package:nextcloud/utils.dart'; +import 'package:timezone/timezone.dart' as tz; +import 'package:xml/xml.dart' as xml; +import 'package:xml_annotation/xml_annotation.dart' as xml_annotation; + +@internal +final class HttpDateXMLConverter implements xml_annotation.XmlConverter { + const HttpDateXMLConverter(); + + @override + void buildXmlChildren( + tz.TZDateTime? instance, + xml.XmlBuilder builder, { + Map namespaces = const {}, + }) { + if (instance == null) { + return; + } + + final date = formatHttpDate(instance); + builder.text(date); + } + + @override + tz.TZDateTime? fromXmlElement( + xml.XmlElement element, + ) { + final date = element.getText(); + if (date != null) { + return parseHttpDate(date); + } + + return null; + } + + @override + List toXmlAttributes( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + return const []; + } + + @override + List toXmlChildren( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + if (instance == null) { + return const []; + } + + final date = formatHttpDate(instance); + return [ + xml.XmlText(date), + ]; + } +} + +@internal +final class ISO8601XMLConverter implements xml_annotation.XmlConverter { + const ISO8601XMLConverter(); + + @override + void buildXmlChildren( + tz.TZDateTime? instance, + xml.XmlBuilder builder, { + Map namespaces = const {}, + }) { + if (instance == null) { + return; + } + + final date = instance.toIso8601String(); + builder.text(date); + } + + @override + tz.TZDateTime? fromXmlElement( + xml.XmlElement element, + ) { + final date = element.getText(); + if (date != null) { + return tz.TZDateTime.parse(tz.UTC, date); + } + + return null; + } + + @override + List toXmlAttributes( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + return const []; + } + + @override + List toXmlChildren( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + if (instance == null) { + return const []; + } + + final date = instance.toIso8601String(); + return [ + xml.XmlText(date), + ]; + } +} + +@internal +final class UnixEpochXMLConverter implements xml_annotation.XmlConverter { + const UnixEpochXMLConverter(); + + @override + void buildXmlChildren( + tz.TZDateTime? instance, + xml.XmlBuilder builder, { + Map namespaces = const {}, + }) { + if (instance == null) { + return; + } + + final date = instance.secondsSinceEpoch.toString(); + builder.text(date); + } + + @override + tz.TZDateTime? fromXmlElement( + xml.XmlElement element, + ) { + final date = element.getText(); + if (date != null) { + return DateTimeUtils.fromSecondsSinceEpoch( + tz.UTC, + int.parse(date), + ); + } + + return null; + } + + @override + List toXmlAttributes( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + return const []; + } + + @override + List toXmlChildren( + tz.TZDateTime? instance, { + Map namespaces = const {}, + }) { + if (instance == null) { + return const []; + } + + final date = instance.secondsSinceEpoch.toString(); + return [ + xml.XmlText(date), + ]; + } +} diff --git a/packages/nextcloud/lib/src/utils/date_time.dart b/packages/nextcloud/lib/src/utils/date_time.dart index a02d4fc1958..a0fbf08f33c 100644 --- a/packages/nextcloud/lib/src/utils/date_time.dart +++ b/packages/nextcloud/lib/src/utils/date_time.dart @@ -1,9 +1,4 @@ -import 'package:meta/meta.dart'; -import 'package:nextcloud/src/utils/http_date_parser.dart'; -import 'package:nextcloud/utils.dart'; import 'package:timezone/timezone.dart' as tz; -import 'package:xml/xml.dart' as xml; -import 'package:xml_annotation/xml_annotation.dart' as xml_annotation; /// Common methods to work with [DateTime] data. extension DateTimeUtils on DateTime { @@ -33,168 +28,3 @@ extension DateTimeUtils on DateTime { /// In other words: `secondsSinceEpoch.abs() <= 8640000000000`. int get secondsSinceEpoch => millisecondsSinceEpoch ~/ Duration.millisecondsPerSecond; } - -@internal -final class HttpDateXMLConverter implements xml_annotation.XmlConverter { - const HttpDateXMLConverter(); - - @override - void buildXmlChildren( - tz.TZDateTime? instance, - xml.XmlBuilder builder, { - Map namespaces = const {}, - }) { - if (instance == null) { - return; - } - - final date = formatHttpDate(instance); - builder.text(date); - } - - @override - tz.TZDateTime? fromXmlElement( - xml.XmlElement element, - ) { - final date = element.getText(); - if (date != null) { - return parseHttpDate(date); - } - - return null; - } - - @override - List toXmlAttributes( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - return const []; - } - - @override - List toXmlChildren( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - if (instance == null) { - return const []; - } - - final date = formatHttpDate(instance); - return [ - xml.XmlText(date), - ]; - } -} - -@internal -final class ISO8601XMLConverter implements xml_annotation.XmlConverter { - const ISO8601XMLConverter(); - - @override - void buildXmlChildren( - tz.TZDateTime? instance, - xml.XmlBuilder builder, { - Map namespaces = const {}, - }) { - if (instance == null) { - return; - } - - final date = instance.toIso8601String(); - builder.text(date); - } - - @override - tz.TZDateTime? fromXmlElement( - xml.XmlElement element, - ) { - final date = element.getText(); - if (date != null) { - return tz.TZDateTime.parse(tz.UTC, date); - } - - return null; - } - - @override - List toXmlAttributes( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - return const []; - } - - @override - List toXmlChildren( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - if (instance == null) { - return const []; - } - - final date = instance.toIso8601String(); - return [ - xml.XmlText(date), - ]; - } -} - -@internal -final class UnixEpochXMLConverter implements xml_annotation.XmlConverter { - const UnixEpochXMLConverter(); - - @override - void buildXmlChildren( - tz.TZDateTime? instance, - xml.XmlBuilder builder, { - Map namespaces = const {}, - }) { - if (instance == null) { - return; - } - - final date = instance.secondsSinceEpoch.toString(); - builder.text(date); - } - - @override - tz.TZDateTime? fromXmlElement( - xml.XmlElement element, - ) { - final date = element.getText(); - if (date != null) { - return DateTimeUtils.fromSecondsSinceEpoch( - tz.UTC, - int.parse(date), - ); - } - - return null; - } - - @override - List toXmlAttributes( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - return const []; - } - - @override - List toXmlChildren( - tz.TZDateTime? instance, { - Map namespaces = const {}, - }) { - if (instance == null) { - return const []; - } - - final date = instance.secondsSinceEpoch.toString(); - return [ - xml.XmlText(date), - ]; - } -} diff --git a/packages/nextcloud/lib/webdav.dart b/packages/nextcloud/lib/webdav.dart index e50289c759d..681556d5416 100644 --- a/packages/nextcloud/lib/webdav.dart +++ b/packages/nextcloud/lib/webdav.dart @@ -2,7 +2,14 @@ import 'package:nextcloud/src/api/webdav/webdav_client.dart'; import 'package:nextcloud/src/nextcloud_client.dart'; export 'package:nextcloud/src/api/webdav/webdav.dart' - hide DurationXMLConverter, WebDavCSRFClient, constructUri, parseWebDavOptions; + hide + DurationXMLConverter, + HttpDateXMLConverter, + ISO8601XMLConverter, + UnixEpochXMLConverter, + WebDavCSRFClient, + constructUri, + parseWebDavOptions; /// Client for WebDAV. extension WebDAVExtension on NextcloudClient { diff --git a/packages/nextcloud/test/api/webdav/models/path_uri_test.dart b/packages/nextcloud/test/api/webdav/models/path_uri_test.dart new file mode 100644 index 00000000000..399cccae5e1 --- /dev/null +++ b/packages/nextcloud/test/api/webdav/models/path_uri_test.dart @@ -0,0 +1,91 @@ +import 'package:nextcloud/webdav.dart'; +import 'package:test/test.dart'; + +void main() { + group('PathUri', () { + test('isAbsolute', () { + expect(PathUri.parse('').isAbsolute, false); + expect(PathUri.parse('/').isAbsolute, true); + expect(PathUri.parse('test').isAbsolute, false); + expect(PathUri.parse('test/').isAbsolute, false); + expect(PathUri.parse('/test').isAbsolute, true); + expect(PathUri.parse('/test/').isAbsolute, true); + }); + + test('isDirectory', () { + expect(PathUri.parse('').isDirectory, true); + expect(PathUri.parse('/').isDirectory, true); + expect(PathUri.parse('test').isDirectory, false); + expect(PathUri.parse('test/').isDirectory, true); + expect(PathUri.parse('/test').isDirectory, false); + expect(PathUri.parse('/test/').isDirectory, true); + }); + + test('pathSegments', () { + expect(PathUri.parse('').pathSegments, isEmpty); + expect(PathUri.parse('/').pathSegments, isEmpty); + expect(PathUri.parse('test').pathSegments, ['test']); + expect(PathUri.parse('test/').pathSegments, ['test']); + expect(PathUri.parse('/test').pathSegments, ['test']); + expect(PathUri.parse('/test/').pathSegments, ['test']); + }); + + test('path', () { + expect(PathUri.parse('').path, ''); + expect(PathUri.parse('/').path, '/'); + expect(PathUri.parse('test').path, 'test'); + expect(PathUri.parse('test/').path, 'test/'); + expect(PathUri.parse('/test').path, '/test'); + expect(PathUri.parse('/test/').path, '/test/'); + }); + + test('normalization', () { + expect(PathUri.parse('/test/abc/').path, '/test/abc/'); + expect(PathUri.parse('//test//abc//').path, '/test/abc/'); + expect(PathUri.parse('///test///abc///').path, '/test/abc/'); + }); + + test('name', () { + expect(PathUri.parse('').name, ''); + expect(PathUri.parse('test').name, 'test'); + expect(PathUri.parse('/test/').name, 'test'); + expect(PathUri.parse('abc/test').name, 'test'); + expect(PathUri.parse('/abc/test/').name, 'test'); + }); + + test('parent', () { + expect(PathUri.parse('').parent, null); + expect(PathUri.parse('/').parent, null); + expect(PathUri.parse('test').parent, PathUri.parse('')); + expect(PathUri.parse('test/abc').parent, PathUri.parse('test/')); + expect(PathUri.parse('test/abc/').parent, PathUri.parse('test/')); + expect(PathUri.parse('/test/abc').parent, PathUri.parse('/test/')); + expect(PathUri.parse('/test/abc/').parent, PathUri.parse('/test/')); + }); + + test('join', () { + expect(PathUri.parse('').join(PathUri.parse('test')), PathUri.parse('test')); + expect(PathUri.parse('/').join(PathUri.parse('test')), PathUri.parse('/test')); + expect(() => PathUri.parse('test').join(PathUri.parse('abc')), throwsA(isA())); + expect(PathUri.parse('test/').join(PathUri.parse('abc')), PathUri.parse('test/abc')); + expect(PathUri.parse('test/').join(PathUri.parse('abc/123')), PathUri.parse('test/abc/123')); + expect(PathUri.parse('/test/').join(PathUri.parse('abc')), PathUri.parse('/test/abc')); + expect(PathUri.parse('/test/').join(PathUri.parse('/abc')), PathUri.parse('/test/abc')); + expect(PathUri.parse('/test/').join(PathUri.parse('/abc/')), PathUri.parse('/test/abc/')); + }); + + test('rename', () { + expect(PathUri.parse('').rename('test'), PathUri.parse('')); + expect(PathUri.parse('test').rename('abc'), PathUri.parse('abc')); + expect(PathUri.parse('test/').rename('abc'), PathUri.parse('abc/')); + expect(PathUri.parse('test/abc').rename('123'), PathUri.parse('test/123')); + expect(PathUri.parse('test/abc/').rename('123'), PathUri.parse('test/123/')); + expect(() => PathUri.parse('test').rename('abc/'), throwsA(isA())); + expect(() => PathUri.parse('test/').rename('abc/'), throwsA(isA())); + expect(() => PathUri.parse('test').rename('/abc'), throwsA(isA())); + expect(() => PathUri.parse('test/').rename('/abc'), throwsA(isA())); + expect(() => PathUri.parse('test').rename('abc/123'), throwsA(isA())); + expect(() => PathUri.parse('test/').rename('abc/123'), throwsA(isA())); + }); + }); +} diff --git a/packages/nextcloud/test/api/webdav/models/webdav_depth_test.dart b/packages/nextcloud/test/api/webdav/models/webdav_depth_test.dart new file mode 100644 index 00000000000..95d110e68e9 --- /dev/null +++ b/packages/nextcloud/test/api/webdav/models/webdav_depth_test.dart @@ -0,0 +1,10 @@ +import 'package:nextcloud/webdav.dart'; +import 'package:test/test.dart'; + +void main() { + test('WebDavDepth value', () { + expect(WebDavDepth.zero.value, equals('0')); + expect(WebDavDepth.one.value, equals('1')); + expect(WebDavDepth.infinity.value, equals('infinity')); + }); +} diff --git a/packages/nextcloud/test/api/webdav/utils/webdav_uri_test.dart b/packages/nextcloud/test/api/webdav/utils/webdav_uri_test.dart new file mode 100644 index 00000000000..fbe98965cb0 --- /dev/null +++ b/packages/nextcloud/test/api/webdav/utils/webdav_uri_test.dart @@ -0,0 +1,43 @@ +import 'package:nextcloud/src/api/webdav/webdav.dart'; +import 'package:test/test.dart'; + +void main() { + group('constructUri', () { + for (final values in [ + ('http://cloud.example.com', 'http://cloud.example.com'), + ('http://cloud.example.com/', 'http://cloud.example.com'), + ('http://cloud.example.com/subdir', 'http://cloud.example.com/subdir'), + ('http://cloud.example.com/subdir/', 'http://cloud.example.com/subdir'), + ]) { + final baseURL = Uri.parse(values.$1); + final sanitizedBaseURL = Uri.parse(values.$2); + + test(baseURL, () { + expect( + constructUri(baseURL).toString(), + '$sanitizedBaseURL$webdavBase', + ); + expect( + constructUri(baseURL, PathUri.parse('/')).toString(), + '$sanitizedBaseURL$webdavBase', + ); + expect( + constructUri(baseURL, PathUri.parse('test')).toString(), + '$sanitizedBaseURL$webdavBase/test', + ); + expect( + constructUri(baseURL, PathUri.parse('test/')).toString(), + '$sanitizedBaseURL$webdavBase/test', + ); + expect( + constructUri(baseURL, PathUri.parse('/test')).toString(), + '$sanitizedBaseURL$webdavBase/test', + ); + expect( + constructUri(baseURL, PathUri.parse('/test/')).toString(), + '$sanitizedBaseURL$webdavBase/test', + ); + }); + } + }); +} diff --git a/packages/nextcloud/test/api/webdav/webdav_test.dart b/packages/nextcloud/test/api/webdav/webdav_test.dart index 9b74548e485..2585df097ee 100644 --- a/packages/nextcloud/test/api/webdav/webdav_test.dart +++ b/packages/nextcloud/test/api/webdav/webdav_test.dart @@ -4,7 +4,6 @@ import 'dart:typed_data'; import 'package:mocktail/mocktail.dart'; import 'package:nextcloud/nextcloud.dart'; -import 'package:nextcloud/src/api/webdav/webdav.dart'; import 'package:nextcloud/src/utils/date_time.dart'; import 'package:nextcloud/webdav.dart'; import 'package:nextcloud_test/nextcloud_test.dart'; @@ -16,132 +15,6 @@ class MockCallbackFunction extends Mock { } void main() { - group('constructUri', () { - for (final values in [ - ('http://cloud.example.com', 'http://cloud.example.com'), - ('http://cloud.example.com/', 'http://cloud.example.com'), - ('http://cloud.example.com/subdir', 'http://cloud.example.com/subdir'), - ('http://cloud.example.com/subdir/', 'http://cloud.example.com/subdir'), - ]) { - final baseURL = Uri.parse(values.$1); - final sanitizedBaseURL = Uri.parse(values.$2); - - test(baseURL, () { - expect( - constructUri(baseURL).toString(), - '$sanitizedBaseURL$webdavBase', - ); - expect( - constructUri(baseURL, PathUri.parse('/')).toString(), - '$sanitizedBaseURL$webdavBase', - ); - expect( - constructUri(baseURL, PathUri.parse('test')).toString(), - '$sanitizedBaseURL$webdavBase/test', - ); - expect( - constructUri(baseURL, PathUri.parse('test/')).toString(), - '$sanitizedBaseURL$webdavBase/test', - ); - expect( - constructUri(baseURL, PathUri.parse('/test')).toString(), - '$sanitizedBaseURL$webdavBase/test', - ); - expect( - constructUri(baseURL, PathUri.parse('/test/')).toString(), - '$sanitizedBaseURL$webdavBase/test', - ); - }); - } - }); - - group('PathUri', () { - test('isAbsolute', () { - expect(PathUri.parse('').isAbsolute, false); - expect(PathUri.parse('/').isAbsolute, true); - expect(PathUri.parse('test').isAbsolute, false); - expect(PathUri.parse('test/').isAbsolute, false); - expect(PathUri.parse('/test').isAbsolute, true); - expect(PathUri.parse('/test/').isAbsolute, true); - }); - - test('isDirectory', () { - expect(PathUri.parse('').isDirectory, true); - expect(PathUri.parse('/').isDirectory, true); - expect(PathUri.parse('test').isDirectory, false); - expect(PathUri.parse('test/').isDirectory, true); - expect(PathUri.parse('/test').isDirectory, false); - expect(PathUri.parse('/test/').isDirectory, true); - }); - - test('pathSegments', () { - expect(PathUri.parse('').pathSegments, isEmpty); - expect(PathUri.parse('/').pathSegments, isEmpty); - expect(PathUri.parse('test').pathSegments, ['test']); - expect(PathUri.parse('test/').pathSegments, ['test']); - expect(PathUri.parse('/test').pathSegments, ['test']); - expect(PathUri.parse('/test/').pathSegments, ['test']); - }); - - test('path', () { - expect(PathUri.parse('').path, ''); - expect(PathUri.parse('/').path, '/'); - expect(PathUri.parse('test').path, 'test'); - expect(PathUri.parse('test/').path, 'test/'); - expect(PathUri.parse('/test').path, '/test'); - expect(PathUri.parse('/test/').path, '/test/'); - }); - - test('normalization', () { - expect(PathUri.parse('/test/abc/').path, '/test/abc/'); - expect(PathUri.parse('//test//abc//').path, '/test/abc/'); - expect(PathUri.parse('///test///abc///').path, '/test/abc/'); - }); - - test('name', () { - expect(PathUri.parse('').name, ''); - expect(PathUri.parse('test').name, 'test'); - expect(PathUri.parse('/test/').name, 'test'); - expect(PathUri.parse('abc/test').name, 'test'); - expect(PathUri.parse('/abc/test/').name, 'test'); - }); - - test('parent', () { - expect(PathUri.parse('').parent, null); - expect(PathUri.parse('/').parent, null); - expect(PathUri.parse('test').parent, PathUri.parse('')); - expect(PathUri.parse('test/abc').parent, PathUri.parse('test/')); - expect(PathUri.parse('test/abc/').parent, PathUri.parse('test/')); - expect(PathUri.parse('/test/abc').parent, PathUri.parse('/test/')); - expect(PathUri.parse('/test/abc/').parent, PathUri.parse('/test/')); - }); - - test('join', () { - expect(PathUri.parse('').join(PathUri.parse('test')), PathUri.parse('test')); - expect(PathUri.parse('/').join(PathUri.parse('test')), PathUri.parse('/test')); - expect(() => PathUri.parse('test').join(PathUri.parse('abc')), throwsA(isA())); - expect(PathUri.parse('test/').join(PathUri.parse('abc')), PathUri.parse('test/abc')); - expect(PathUri.parse('test/').join(PathUri.parse('abc/123')), PathUri.parse('test/abc/123')); - expect(PathUri.parse('/test/').join(PathUri.parse('abc')), PathUri.parse('/test/abc')); - expect(PathUri.parse('/test/').join(PathUri.parse('/abc')), PathUri.parse('/test/abc')); - expect(PathUri.parse('/test/').join(PathUri.parse('/abc/')), PathUri.parse('/test/abc/')); - }); - - test('rename', () { - expect(PathUri.parse('').rename('test'), PathUri.parse('')); - expect(PathUri.parse('test').rename('abc'), PathUri.parse('abc')); - expect(PathUri.parse('test/').rename('abc'), PathUri.parse('abc/')); - expect(PathUri.parse('test/abc').rename('123'), PathUri.parse('test/123')); - expect(PathUri.parse('test/abc/').rename('123'), PathUri.parse('test/123/')); - expect(() => PathUri.parse('test').rename('abc/'), throwsA(isA())); - expect(() => PathUri.parse('test/').rename('abc/'), throwsA(isA())); - expect(() => PathUri.parse('test').rename('/abc'), throwsA(isA())); - expect(() => PathUri.parse('test/').rename('/abc'), throwsA(isA())); - expect(() => PathUri.parse('test').rename('abc/123'), throwsA(isA())); - expect(() => PathUri.parse('test/').rename('abc/123'), throwsA(isA())); - }); - }); - test('Chunked responses', () async { await HttpServer.bind('127.0.0.1', 0).then((server) async { server.listen((request) { @@ -186,7 +59,7 @@ void main() { ) .forEach(buffer.add); - expect(buffer.toBytes(), Uint8List.fromList(utf8.encode('123'))); + expect(buffer.toBytes(), utf8.encode('123')); expect(progress, [1]); }); }); From 108ae00722ef766c47b88bf4d308ae5c3873580c Mon Sep 17 00:00:00 2001 From: Nikolas Rimikis Date: Thu, 1 Aug 2024 18:01:51 +0200 Subject: [PATCH 6/6] test(nextcloud): group news tests per sub client Signed-off-by: Nikolas Rimikis --- .../nextcloud/test/api/news/news_test.dart | 802 +++++++++--------- .../fixtures/news/{ => feed}/add_feed.regexp | 0 .../news/{ => feed}/add_feed_to_folder.regexp | 0 .../news/{ => feed}/delete_feed.regexp | 0 .../news/{ => feed}/mark_feed_as_read.regexp | 0 .../news/{ => feed}/rename_feed.regexp | 0 .../news/{ => folders}/create_folder.regexp | 0 .../news/{ => folders}/delete_folder.regexp | 0 .../news/{ => folders}/list_folders.regexp | 0 .../{ => folders}/mark_folder_as_read.regexp | 0 .../{ => folders}/move_feed_to_folder.regexp | 0 .../news/{ => folders}/rename_folder.regexp | 0 .../news/{ => items}/list_articles.regexp | 0 .../{ => items}/list_updated_articles.regexp | 0 .../{ => items}/mark_article_as_read.regexp | 0 .../{ => items}/mark_article_as_unread.regexp | 0 ...ultiple_articles_as_read_and_unread.regexp | 0 .../star_and_unstar_multiple_articles.regexp | 0 .../news/{ => items}/star_article.regexp | 0 .../news/{ => items}/unstar_article.regexp | 0 20 files changed, 404 insertions(+), 398 deletions(-) rename packages/nextcloud/test/fixtures/news/{ => feed}/add_feed.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => feed}/add_feed_to_folder.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => feed}/delete_feed.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => feed}/mark_feed_as_read.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => feed}/rename_feed.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/create_folder.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/delete_folder.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/list_folders.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/mark_folder_as_read.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/move_feed_to_folder.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => folders}/rename_folder.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/list_articles.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/list_updated_articles.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/mark_article_as_read.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/mark_article_as_unread.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/mark_multiple_articles_as_read_and_unread.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/star_and_unstar_multiple_articles.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/star_article.regexp (100%) rename packages/nextcloud/test/fixtures/news/{ => items}/unstar_article.regexp (100%) diff --git a/packages/nextcloud/test/api/news/news_test.dart b/packages/nextcloud/test/api/news/news_test.dart index aa7e8dc8ab9..51a1a72b5e5 100644 --- a/packages/nextcloud/test/api/news/news_test.dart +++ b/packages/nextcloud/test/api/news/news_test.dart @@ -38,452 +38,458 @@ void main() { expect(result.isSupported, isTrue); }); - test('Add feed', () async { - var response = await tester.client.news.feeds.listFeeds(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.starredCount, 0); - expect(response.body.newestItemId, null); - expect(response.body.feeds, hasLength(0)); - - response = await addWikipediaFeed(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.starredCount, null); - expect(response.body.newestItemId, isNotNull); - expect(response.body.feeds, hasLength(1)); - expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); - - response = await tester.client.news.feeds.listFeeds(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.starredCount, 0); - expect(response.body.newestItemId, isNotNull); - expect(response.body.feeds, hasLength(1)); - expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); - }); - - test('Delete feed', () async { - var response = await addWikipediaFeed(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.feeds, hasLength(1)); - - final deleteResponse = await tester.client.news.feeds.deleteFeed(feedId: response.body.feeds.single.id); - expect(deleteResponse.statusCode, 200); - expect(() => deleteResponse.body, isA()); - expect(() => deleteResponse.headers, isA()); - - response = await tester.client.news.feeds.listFeeds(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.feeds, hasLength(0)); - }); - - test('Rename feed', () async { - var response = await addWikipediaFeed(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.feeds[0].title, 'Wikipedia featured articles feed'); - - await tester.client.news.feeds.renameFeed( - feedId: response.body.feeds[0].id, - feedTitle: 'test1', - ); - - response = await tester.client.news.feeds.listFeeds(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.feeds[0].title, 'test1'); - }); - - test('Move feed to folder', () async { - var response = await tester.client.news.folders.createFolder(name: 'test1'); - expect(response.body.folders, hasLength(1)); - expect(response.body.folders[0].id, isPositive); - expect(response.body.folders[0].name, 'test1'); - expect(response.body.folders[0].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[0].feeds, hasLength(0)); - - final feedsResponse = await addWikipediaFeed(); - await tester.client.news.feeds.moveFeed( - feedId: feedsResponse.body.feeds[0].id, - folderId: response.body.folders[0].id, - ); - - response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(1)); - expect(response.body.folders[0].id, isPositive); - expect(response.body.folders[0].name, 'test1'); - expect(response.body.folders[0].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[0].feeds, hasLength(0)); - }); - - test('Mark feed as read', () async { - final feedsResponse = await addWikipediaFeed(); - - var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.items.length, greaterThan(0)); - - await tester.client.news.feeds.markFeedAsRead( - feedId: feedsResponse.body.feeds[0].id, - newestItemId: feedsResponse.body.newestItemId!, - ); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.items, hasLength(0)); - }); - - test('List articles', () async { - var response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.items, hasLength(0)); - - await addWikipediaFeed(); - - response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.items.length, greaterThan(0)); - expect(response.body.items[0].body, isNotNull); - expect(response.body.items[0].feedId, isPositive); - expect(response.body.items[0].unread, true); - expect(response.body.items[0].starred, false); - }); - - test('List updated articles', () async { - // Testing this is not easy, because we can't depend on an external source to update the feed - // Therefore we just add a second feed and check that the articles returned after a certain modified timestamp - // are exactly those of the new feed. - // Now that I think of it, maybe we could host our own feed and update that way, but this works for now. - - await addWikipediaFeed(); - - var response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - final wikipediaArticles = response.body.items.length; - expect(wikipediaArticles, greaterThan(0)); - - await addNasaFeed(); - - response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + group('feed', () { + test('Add feed', () async { + var response = await tester.client.news.feeds.listFeeds(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.starredCount, 0); + expect(response.body.newestItemId, null); + expect(response.body.feeds, hasLength(0)); + + response = await addWikipediaFeed(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.starredCount, null); + expect(response.body.newestItemId, isNotNull); + expect(response.body.feeds, hasLength(1)); + expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); + + response = await tester.client.news.feeds.listFeeds(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.starredCount, 0); + expect(response.body.newestItemId, isNotNull); + expect(response.body.feeds, hasLength(1)); + expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); + }); + + test('Add feed to folder', () async { + final foldersResponse = await tester.client.news.folders.createFolder(name: 'test1'); + final response = await addWikipediaFeed(foldersResponse.body.folders[0].id); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.starredCount, null); + expect(response.body.newestItemId, isNotNull); + expect(response.body.feeds, hasLength(1)); + expect(response.body.feeds[0].folderId, isPositive); + expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); + }); + + test('Delete feed', () async { + var response = await addWikipediaFeed(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.feeds, hasLength(1)); + + final deleteResponse = await tester.client.news.feeds.deleteFeed(feedId: response.body.feeds.single.id); + expect(deleteResponse.statusCode, 200); + expect(() => deleteResponse.body, isA()); + expect(() => deleteResponse.headers, isA()); + + response = await tester.client.news.feeds.listFeeds(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.feeds, hasLength(0)); + }); + + test('Rename feed', () async { + var response = await addWikipediaFeed(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.feeds[0].title, 'Wikipedia featured articles feed'); + + await tester.client.news.feeds.renameFeed( + feedId: response.body.feeds[0].id, + feedTitle: 'test1', + ); - final nasaArticles = response.body.items.length - wikipediaArticles; - expect(nasaArticles, greaterThan(0)); + response = await tester.client.news.feeds.listFeeds(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - response = await tester.client.news.items.listUpdatedArticles( - lastModified: response.body.items[response.body.items.length - 1 - nasaArticles].lastModified, - ); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + expect(response.body.feeds[0].title, 'test1'); + }); - expect(response.body.items, hasLength(nasaArticles)); - }); + test('Mark feed as read', () async { + final feedsResponse = await addWikipediaFeed(); - test('Mark article as read', () async { - await addWikipediaFeed(); + var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + expect(response.body.items.length, greaterThan(0)); - final unreadArticles = response.body.items.length; - expect(unreadArticles, greaterThan(0)); + await tester.client.news.feeds.markFeedAsRead( + feedId: feedsResponse.body.feeds[0].id, + newestItemId: feedsResponse.body.newestItemId!, + ); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - await tester.client.news.items.markArticleAsRead( - itemId: response.body.items[0].id, - ); - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - expect(response.body.items, hasLength(unreadArticles - 1)); + expect(response.body.items, hasLength(0)); + }); }); - test('Mark article as unread', () async { - await addWikipediaFeed(); - - var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - final readArticle = response.body.items[0]; - await tester.client.news.items.markArticleAsRead(itemId: readArticle.id); - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + group('folders', () { + test('Move feed to folder', () async { + var response = await tester.client.news.folders.createFolder(name: 'test1'); + expect(response.body.folders, hasLength(1)); + expect(response.body.folders[0].id, isPositive); + expect(response.body.folders[0].name, 'test1'); + expect(response.body.folders[0].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[0].feeds, hasLength(0)); + + final feedsResponse = await addWikipediaFeed(); + await tester.client.news.feeds.moveFeed( + feedId: feedsResponse.body.feeds[0].id, + folderId: response.body.folders[0].id, + ); - final unreadArticles = response.body.items.length; - expect(unreadArticles, greaterThan(0)); + response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + expect(response.body.folders[0].id, isPositive); + expect(response.body.folders[0].name, 'test1'); + expect(response.body.folders[0].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[0].feeds, hasLength(0)); + }); + + test('Create folder', () async { + var response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(0)); + + response = await tester.client.news.folders.createFolder(name: 'test1'); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + expect(response.body.folders[0].id, isPositive); + expect(response.body.folders[0].name, 'test1'); + expect(response.body.folders[0].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[0].feeds, hasLength(0)); + + response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + expect(response.body.folders[0].id, isPositive); + expect(response.body.folders[0].name, 'test1'); + expect(response.body.folders[0].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[0].feeds, hasLength(0)); + }); + + test('Delete folder', () async { + var response = await tester.client.news.folders.createFolder(name: 'test1'); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + + final deleteResponse = await tester.client.news.folders.deleteFolder(folderId: response.body.folders.single.id); + expect(deleteResponse.statusCode, 200); + expect(() => deleteResponse.body, isA()); + expect(() => deleteResponse.headers, isA()); + + response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(0)); + }); + + test('Rename folder', () async { + var response = await tester.client.news.folders.createFolder(name: 'test1'); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + + final deleteResponse = await tester.client.news.folders.renameFolder( + folderId: response.body.folders.single.id, + name: 'test2', + ); + expect(deleteResponse.statusCode, 200); + expect(() => deleteResponse.body, isA()); + expect(() => deleteResponse.headers, isA()); + + response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(1)); + expect(response.body.folders.single.name, 'test2'); + }); + + test('List folders', () async { + var response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(0)); + + await tester.client.news.folders.createFolder(name: 'test1'); + await tester.client.news.folders.createFolder(name: 'test2'); + + response = response = await tester.client.news.folders.listFolders(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.folders, hasLength(2)); + expect(response.body.folders[0].id, isPositive); + expect(response.body.folders[0].name, 'test1'); + expect(response.body.folders[0].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[0].feeds, hasLength(0)); + expect(response.body.folders[1].id, isPositive); + expect(response.body.folders[1].name, 'test2'); + expect(response.body.folders[1].opened, true); + // ignore: deprecated_member_use_from_same_package + expect(response.body.folders[1].feeds, hasLength(0)); + }); + + test('Mark folder as read', () async { + final foldersResponse = await tester.client.news.folders.createFolder(name: 'test1'); + final feedsResponse = await addWikipediaFeed(foldersResponse.body.folders[0].id); + + var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.items.length, greaterThan(0)); + + await tester.client.news.folders.markFolderAsRead( + folderId: foldersResponse.body.folders[0].id, + newestItemId: feedsResponse.body.newestItemId!, + ); - await tester.client.news.items.markArticleAsUnread(itemId: readArticle.id); - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - expect(response.body.items, hasLength(unreadArticles + 1)); + expect(response.body.items, hasLength(0)); + }); }); - test('Mark multiple articles as read and unread', () async { - await addWikipediaFeed(); - var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - final unreadArticles = response.body.items; - expect(unreadArticles, isNotEmpty); - - await tester.client.news.items.readMultipleArticles( - $body: news.ReadMultipleArticlesRequestApplicationJson((b) { - b.itemIds.addAll(unreadArticles.map((a) => a.id)); - }), - ); - - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.body.items, isEmpty); - - await tester.client.news.items.unreadMultipleArticles( - $body: news.UnreadMultipleArticlesRequestApplicationJson((b) { - b.itemIds.addAll(unreadArticles.map((a) => a.id)); - }), - ); - - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.body.items, hasLength(unreadArticles.length)); - }); + group('items', () { + test('List articles', () async { + var response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - test('Star article', () async { - await addWikipediaFeed(); + expect(response.body.items, hasLength(0)); - var response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + await addWikipediaFeed(); - final starredArticles = response.body.items.length; - expect(starredArticles, 0); + response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + expect(response.body.items.length, greaterThan(0)); + expect(response.body.items[0].body, isNotNull); + expect(response.body.items[0].feedId, isPositive); + expect(response.body.items[0].unread, true); + expect(response.body.items[0].starred, false); + }); - await tester.client.news.items.starArticle( - itemId: response.body.items[0].id, - ); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + test('List updated articles', () async { + // Testing this is not easy, because we can't depend on an external source to update the feed + // Therefore we just add a second feed and check that the articles returned after a certain modified timestamp + // are exactly those of the new feed. + // Now that I think of it, maybe we could host our own feed and update that way, but this works for now. - expect(response.body.items, hasLength(1)); - }); + await addWikipediaFeed(); - test('Unstar article', () async { - await addWikipediaFeed(); + var response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - var response = await tester.client.news.items.listArticles(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + final wikipediaArticles = response.body.items.length; + expect(wikipediaArticles, greaterThan(0)); - final item = response.body.items[0]; + await addNasaFeed(); - await tester.client.news.items.starArticle( - itemId: item.id, - ); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - expect(response.body.items, hasLength(1)); + final nasaArticles = response.body.items.length - wikipediaArticles; + expect(nasaArticles, greaterThan(0)); - await tester.client.news.items.unstarArticle( - itemId: item.id, - ); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + response = await tester.client.news.items.listUpdatedArticles( + lastModified: response.body.items[response.body.items.length - 1 - nasaArticles].lastModified, + ); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - expect(response.body.items, hasLength(0)); - }); + expect(response.body.items, hasLength(nasaArticles)); + }); - test('Star and Unstar multiple articles', () async { - await addWikipediaFeed(); - var response = await tester.client.news.items.listArticles(type: news.ListType.allItems.index); - final allIDs = response.body.items.map((e) => e.id); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.body.items, isEmpty); - - await tester.client.news.items.starMultipleArticles( - $body: news.StarMultipleArticlesRequestApplicationJson((b) { - b.itemIds.addAll(allIDs); - }), - ); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.body.items, isNotEmpty); - - await tester.client.news.items.unstarMultipleArticles( - $body: news.UnstarMultipleArticlesRequestApplicationJson((b) { - b.itemIds.addAll(allIDs); - }), - ); - response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); - expect(response.body.items, isEmpty); - }); + test('Mark article as read', () async { + await addWikipediaFeed(); - test('Create folder', () async { - var response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(0)); - - response = await tester.client.news.folders.createFolder(name: 'test1'); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(1)); - expect(response.body.folders[0].id, isPositive); - expect(response.body.folders[0].name, 'test1'); - expect(response.body.folders[0].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[0].feeds, hasLength(0)); - - response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(1)); - expect(response.body.folders[0].id, isPositive); - expect(response.body.folders[0].name, 'test1'); - expect(response.body.folders[0].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[0].feeds, hasLength(0)); - }); + var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - test('Delete folder', () async { - var response = await tester.client.news.folders.createFolder(name: 'test1'); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + final unreadArticles = response.body.items.length; + expect(unreadArticles, greaterThan(0)); - expect(response.body.folders, hasLength(1)); + await tester.client.news.items.markArticleAsRead( + itemId: response.body.items[0].id, + ); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.items, hasLength(unreadArticles - 1)); + }); + + test('Mark article as unread', () async { + await addWikipediaFeed(); + + var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + final readArticle = response.body.items[0]; + await tester.client.news.items.markArticleAsRead(itemId: readArticle.id); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + final unreadArticles = response.body.items.length; + expect(unreadArticles, greaterThan(0)); + + await tester.client.news.items.markArticleAsUnread(itemId: readArticle.id); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.items, hasLength(unreadArticles + 1)); + }); + + test('Mark multiple articles as read and unread', () async { + await addWikipediaFeed(); + var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + final unreadArticles = response.body.items; + expect(unreadArticles, isNotEmpty); + + await tester.client.news.items.readMultipleArticles( + $body: news.ReadMultipleArticlesRequestApplicationJson((b) { + b.itemIds.addAll(unreadArticles.map((a) => a.id)); + }), + ); - final deleteResponse = await tester.client.news.folders.deleteFolder(folderId: response.body.folders.single.id); - expect(deleteResponse.statusCode, 200); - expect(() => deleteResponse.body, isA()); - expect(() => deleteResponse.headers, isA()); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.body.items, isEmpty); - response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + await tester.client.news.items.unreadMultipleArticles( + $body: news.UnreadMultipleArticlesRequestApplicationJson((b) { + b.itemIds.addAll(unreadArticles.map((a) => a.id)); + }), + ); - expect(response.body.folders, hasLength(0)); - }); + response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); + expect(response.body.items, hasLength(unreadArticles.length)); + }); - test('Rename folder', () async { - var response = await tester.client.news.folders.createFolder(name: 'test1'); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + test('Star article', () async { + await addWikipediaFeed(); - expect(response.body.folders, hasLength(1)); + var response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - final deleteResponse = await tester.client.news.folders.renameFolder( - folderId: response.body.folders.single.id, - name: 'test2', - ); - expect(deleteResponse.statusCode, 200); - expect(() => deleteResponse.body, isA()); - expect(() => deleteResponse.headers, isA()); + final starredArticles = response.body.items.length; + expect(starredArticles, 0); - response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - expect(response.body.folders, hasLength(1)); - expect(response.body.folders.single.name, 'test2'); - }); + await tester.client.news.items.starArticle( + itemId: response.body.items[0].id, + ); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - test('List folders', () async { - var response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(0)); - - await tester.client.news.folders.createFolder(name: 'test1'); - await tester.client.news.folders.createFolder(name: 'test2'); - - response = response = await tester.client.news.folders.listFolders(); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.folders, hasLength(2)); - expect(response.body.folders[0].id, isPositive); - expect(response.body.folders[0].name, 'test1'); - expect(response.body.folders[0].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[0].feeds, hasLength(0)); - expect(response.body.folders[1].id, isPositive); - expect(response.body.folders[1].name, 'test2'); - expect(response.body.folders[1].opened, true); - // ignore: deprecated_member_use_from_same_package - expect(response.body.folders[1].feeds, hasLength(0)); - }); + expect(response.body.items, hasLength(1)); + }); - test('Add feed to folder', () async { - final foldersResponse = await tester.client.news.folders.createFolder(name: 'test1'); - final response = await addWikipediaFeed(foldersResponse.body.folders[0].id); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); - - expect(response.body.starredCount, null); - expect(response.body.newestItemId, isNotNull); - expect(response.body.feeds, hasLength(1)); - expect(response.body.feeds[0].folderId, isPositive); - expect(response.body.feeds[0].url, '${tester.targetURL}/static/wikipedia.xml'); - }); + test('Unstar article', () async { + await addWikipediaFeed(); - test('Mark folder as read', () async { - final foldersResponse = await tester.client.news.folders.createFolder(name: 'test1'); - final feedsResponse = await addWikipediaFeed(foldersResponse.body.folders[0].id); + var response = await tester.client.news.items.listArticles(); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - var response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + final item = response.body.items[0]; - expect(response.body.items.length, greaterThan(0)); + await tester.client.news.items.starArticle( + itemId: item.id, + ); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); - await tester.client.news.folders.markFolderAsRead( - folderId: foldersResponse.body.folders[0].id, - newestItemId: feedsResponse.body.newestItemId!, - ); + expect(response.body.items, hasLength(1)); - response = await tester.client.news.items.listArticles(type: news.ListType.unread.index); - expect(response.statusCode, 200); - expect(() => response.headers, isA()); + await tester.client.news.items.unstarArticle( + itemId: item.id, + ); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.statusCode, 200); + expect(() => response.headers, isA()); + + expect(response.body.items, hasLength(0)); + }); + + test('Star and Unstar multiple articles', () async { + await addWikipediaFeed(); + var response = await tester.client.news.items.listArticles(type: news.ListType.allItems.index); + final allIDs = response.body.items.map((e) => e.id); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.body.items, isEmpty); + + await tester.client.news.items.starMultipleArticles( + $body: news.StarMultipleArticlesRequestApplicationJson((b) { + b.itemIds.addAll(allIDs); + }), + ); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.body.items, isNotEmpty); - expect(response.body.items, hasLength(0)); + await tester.client.news.items.unstarMultipleArticles( + $body: news.UnstarMultipleArticlesRequestApplicationJson((b) { + b.itemIds.addAll(allIDs); + }), + ); + response = await tester.client.news.items.listArticles(type: news.ListType.starred.index); + expect(response.body.items, isEmpty); + }); }); }); } diff --git a/packages/nextcloud/test/fixtures/news/add_feed.regexp b/packages/nextcloud/test/fixtures/news/feed/add_feed.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/add_feed.regexp rename to packages/nextcloud/test/fixtures/news/feed/add_feed.regexp diff --git a/packages/nextcloud/test/fixtures/news/add_feed_to_folder.regexp b/packages/nextcloud/test/fixtures/news/feed/add_feed_to_folder.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/add_feed_to_folder.regexp rename to packages/nextcloud/test/fixtures/news/feed/add_feed_to_folder.regexp diff --git a/packages/nextcloud/test/fixtures/news/delete_feed.regexp b/packages/nextcloud/test/fixtures/news/feed/delete_feed.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/delete_feed.regexp rename to packages/nextcloud/test/fixtures/news/feed/delete_feed.regexp diff --git a/packages/nextcloud/test/fixtures/news/mark_feed_as_read.regexp b/packages/nextcloud/test/fixtures/news/feed/mark_feed_as_read.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/mark_feed_as_read.regexp rename to packages/nextcloud/test/fixtures/news/feed/mark_feed_as_read.regexp diff --git a/packages/nextcloud/test/fixtures/news/rename_feed.regexp b/packages/nextcloud/test/fixtures/news/feed/rename_feed.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/rename_feed.regexp rename to packages/nextcloud/test/fixtures/news/feed/rename_feed.regexp diff --git a/packages/nextcloud/test/fixtures/news/create_folder.regexp b/packages/nextcloud/test/fixtures/news/folders/create_folder.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/create_folder.regexp rename to packages/nextcloud/test/fixtures/news/folders/create_folder.regexp diff --git a/packages/nextcloud/test/fixtures/news/delete_folder.regexp b/packages/nextcloud/test/fixtures/news/folders/delete_folder.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/delete_folder.regexp rename to packages/nextcloud/test/fixtures/news/folders/delete_folder.regexp diff --git a/packages/nextcloud/test/fixtures/news/list_folders.regexp b/packages/nextcloud/test/fixtures/news/folders/list_folders.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/list_folders.regexp rename to packages/nextcloud/test/fixtures/news/folders/list_folders.regexp diff --git a/packages/nextcloud/test/fixtures/news/mark_folder_as_read.regexp b/packages/nextcloud/test/fixtures/news/folders/mark_folder_as_read.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/mark_folder_as_read.regexp rename to packages/nextcloud/test/fixtures/news/folders/mark_folder_as_read.regexp diff --git a/packages/nextcloud/test/fixtures/news/move_feed_to_folder.regexp b/packages/nextcloud/test/fixtures/news/folders/move_feed_to_folder.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/move_feed_to_folder.regexp rename to packages/nextcloud/test/fixtures/news/folders/move_feed_to_folder.regexp diff --git a/packages/nextcloud/test/fixtures/news/rename_folder.regexp b/packages/nextcloud/test/fixtures/news/folders/rename_folder.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/rename_folder.regexp rename to packages/nextcloud/test/fixtures/news/folders/rename_folder.regexp diff --git a/packages/nextcloud/test/fixtures/news/list_articles.regexp b/packages/nextcloud/test/fixtures/news/items/list_articles.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/list_articles.regexp rename to packages/nextcloud/test/fixtures/news/items/list_articles.regexp diff --git a/packages/nextcloud/test/fixtures/news/list_updated_articles.regexp b/packages/nextcloud/test/fixtures/news/items/list_updated_articles.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/list_updated_articles.regexp rename to packages/nextcloud/test/fixtures/news/items/list_updated_articles.regexp diff --git a/packages/nextcloud/test/fixtures/news/mark_article_as_read.regexp b/packages/nextcloud/test/fixtures/news/items/mark_article_as_read.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/mark_article_as_read.regexp rename to packages/nextcloud/test/fixtures/news/items/mark_article_as_read.regexp diff --git a/packages/nextcloud/test/fixtures/news/mark_article_as_unread.regexp b/packages/nextcloud/test/fixtures/news/items/mark_article_as_unread.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/mark_article_as_unread.regexp rename to packages/nextcloud/test/fixtures/news/items/mark_article_as_unread.regexp diff --git a/packages/nextcloud/test/fixtures/news/mark_multiple_articles_as_read_and_unread.regexp b/packages/nextcloud/test/fixtures/news/items/mark_multiple_articles_as_read_and_unread.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/mark_multiple_articles_as_read_and_unread.regexp rename to packages/nextcloud/test/fixtures/news/items/mark_multiple_articles_as_read_and_unread.regexp diff --git a/packages/nextcloud/test/fixtures/news/star_and_unstar_multiple_articles.regexp b/packages/nextcloud/test/fixtures/news/items/star_and_unstar_multiple_articles.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/star_and_unstar_multiple_articles.regexp rename to packages/nextcloud/test/fixtures/news/items/star_and_unstar_multiple_articles.regexp diff --git a/packages/nextcloud/test/fixtures/news/star_article.regexp b/packages/nextcloud/test/fixtures/news/items/star_article.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/star_article.regexp rename to packages/nextcloud/test/fixtures/news/items/star_article.regexp diff --git a/packages/nextcloud/test/fixtures/news/unstar_article.regexp b/packages/nextcloud/test/fixtures/news/items/unstar_article.regexp similarity index 100% rename from packages/nextcloud/test/fixtures/news/unstar_article.regexp rename to packages/nextcloud/test/fixtures/news/items/unstar_article.regexp