Compare commits

..

5 Commits

Author SHA1 Message Date
jld3103 2e6e7b6975
refactor(nextcloud): Migrate to nextcloud_test package 1 year ago
jld3103 93b43b3e82
feat(nextcloud_test): Init 1 year ago
jld3103 b131f5d172
chore(nextcloud): Remove unnecessary proxy helper class 1 year ago
jld3103 272f922a5a
chore(nextcloud): Remove custom date range expect function 1 year ago
jld3103 9c0d4b0a5d
chore(nextcloud): Remove unused helper class 1 year ago
  1. 1
      commitlint.yaml
  2. 2
      cspell.json
  3. 60
      packages/app/pubspec.lock
  4. 4
      packages/nextcloud/pubspec.yaml
  5. 4
      packages/nextcloud/pubspec_overrides.yaml
  6. 11
      packages/nextcloud/test/core_test.dart
  7. 10
      packages/nextcloud/test/dashboard_test.dart
  8. 227
      packages/nextcloud/test/helper.dart
  9. 11
      packages/nextcloud/test/news_test.dart
  10. 11
      packages/nextcloud/test/notes_test.dart
  11. 26
      packages/nextcloud/test/notifications_test.dart
  12. 10
      packages/nextcloud/test/provisioning_api_test.dart
  13. 10
      packages/nextcloud/test/settings_test.dart
  14. 44
      packages/nextcloud/test/spreed_test.dart
  15. 10
      packages/nextcloud/test/uppush_test.dart
  16. 10
      packages/nextcloud/test/user_status_test.dart
  17. 23
      packages/nextcloud/test/webdav_test.dart
  18. 1
      packages/nextcloud_test/LICENSE
  19. 3
      packages/nextcloud_test/README.md
  20. 1
      packages/nextcloud_test/analysis_options.yaml
  21. 2
      packages/nextcloud_test/docker/Dockerfile
  22. 0
      packages/nextcloud_test/docker/static/nasa.xml
  23. 0
      packages/nextcloud_test/docker/static/wikipedia.xml
  24. 3
      packages/nextcloud_test/lib/nextcloud_test.dart
  25. 7
      packages/nextcloud_test/lib/src/defaults.dart
  26. 112
      packages/nextcloud_test/lib/src/docker_container.dart
  27. 79
      packages/nextcloud_test/lib/src/test_client.dart
  28. 21
      packages/nextcloud_test/pubspec.yaml
  29. 8
      packages/nextcloud_test/pubspec_overrides.yaml
  30. 2
      tool/build-dev-container.sh

1
commitlint.yaml

@ -25,4 +25,5 @@ rules:
- neon_notifications - neon_notifications
- neon_lints - neon_lints
- nextcloud - nextcloud
- nextcloud_test
- sort_box - sort_box

2
cspell.json

@ -14,7 +14,7 @@
"packages/dynamite/dynamite_petstore_example/lib", "packages/dynamite/dynamite_petstore_example/lib",
"packages/file_icons/lib/src/data.dart", "packages/file_icons/lib/src/data.dart",
"packages/neon_lints/lib", "packages/neon_lints/lib",
"tool/dev/static" "packages/nextcloud_test/docker/static"
], ],
"dictionaries": [ "dictionaries": [
"bash", "bash",

60
packages/app/pubspec.lock

@ -1,14 +1,6 @@
# Generated by pub # Generated by pub
# See https://dart.dev/tools/pub/glossary#lockfile # See https://dart.dev/tools/pub/glossary#lockfile
packages: packages:
ansicolor:
dependency: transitive
description:
name: ansicolor
sha256: "8bf17a8ff6ea17499e40a2d2542c2f481cd7615760c6d34065cb22bfd22e6880"
url: "https://pub.dev"
source: hosted
version: "2.0.2"
archive: archive:
dependency: transitive dependency: transitive
description: description:
@ -69,10 +61,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: built_value name: built_value
sha256: "69acb7007eb2a31dc901512bfe0f7b767168be34cb734835d54c070bfa74c1b2" sha256: "723b4021e903217dfc445ec4cf5b42e27975aece1fc4ebbc1ca6329c2d9fb54e"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "8.8.0" version: "8.7.0"
camera: camera:
dependency: transitive dependency: transitive
description: description:
@ -165,10 +157,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: cross_file name: cross_file
sha256: "2f9d2cbccb76127ba28528cb3ae2c2326a122446a83de5a056aaa3880d3882c5" sha256: "445db18de832dba8d851e287aff8ccf169bed30d2e94243cb54c7d2f1ed2142c"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.3.3+7" version: "0.3.3+6"
crypto: crypto:
dependency: transitive dependency: transitive
description: description:
@ -357,10 +349,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_local_notifications name: flutter_local_notifications
sha256: bb5cd63ff7c91d6efe452e41d0d0ae6348925c82eafd10ce170ef585ea04776e sha256: "6d11ea777496061e583623aaf31923f93a9409ef8fcaeeefdd6cd78bf4fe5bb3"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "16.2.0" version: "16.1.0"
flutter_local_notifications_linux: flutter_local_notifications_linux:
dependency: transitive dependency: transitive
description: description:
@ -402,10 +394,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: flutter_native_splash name: flutter_native_splash
sha256: c4d899312b36e7454bedfd0a4740275837b99e532d81c8477579d8183db1de6c sha256: d93394f22f73e810bda59e11ebe83329c5511d6460b6b7509c4e1f3c92d6d625
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.3.6" version: "2.3.5"
flutter_plugin_android_lifecycle: flutter_plugin_android_lifecycle:
dependency: transitive dependency: transitive
description: description:
@ -830,50 +822,42 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: permission_handler name: permission_handler
sha256: "860c6b871c94c78e202dc69546d4d8fd84bd59faeb36f8fb9888668a53ff4f78" sha256: "284a66179cabdf942f838543e10413246f06424d960c92ba95c84439154fcac8"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "11.1.0" version: "11.0.1"
permission_handler_android: permission_handler_android:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_android name: permission_handler_android
sha256: "2f1bec180ee2f5665c22faada971a8f024761f632e93ddc23310487df52dcfa6" sha256: f9fddd3b46109bd69ff3f9efa5006d2d309b7aec0f3c1c5637a60a2d5659e76e
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "12.0.1" version: "11.1.0"
permission_handler_apple: permission_handler_apple:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_apple name: permission_handler_apple
sha256: "1a816084338ada8d574b1cb48390e6e8b19305d5120fe3a37c98825bacc78306" sha256: "99e220bce3f8877c78e4ace901082fb29fa1b4ebde529ad0932d8d664b34f3f5"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "9.2.0" version: "9.1.4"
permission_handler_html:
dependency: transitive
description:
name: permission_handler_html
sha256: d96ff56a757b7f04fa825c469d296c5aebc55f743e87bd639fef91a466a24da8
url: "https://pub.dev"
source: hosted
version: "0.1.0+1"
permission_handler_platform_interface: permission_handler_platform_interface:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_platform_interface name: permission_handler_platform_interface
sha256: d87349312f7eaf6ce0adaf668daf700ac5b06af84338bd8b8574dfbd93ffe1a1 sha256: "6760eb5ef34589224771010805bea6054ad28453906936f843a8cc4d3a55c4a4"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "4.0.2" version: "3.12.0"
permission_handler_windows: permission_handler_windows:
dependency: transitive dependency: transitive
description: description:
name: permission_handler_windows name: permission_handler_windows
sha256: "1e8640c1e39121128da6b816d236e714d2cf17fac5a105dd6acdd3403a628004" sha256: cc074aace208760f1eee6aa4fae766b45d947df85bc831cde77009cdb4720098
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "0.2.0" version: "0.1.3"
petitparser: petitparser:
dependency: transitive dependency: transitive
description: description:
@ -1330,10 +1314,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: url_launcher_web name: url_launcher_web
sha256: "138bd45b3a456dcfafc46d1a146787424f8d2edfbf2809c9324361e58f851cf7" sha256: "7fd2f55fe86cea2897b963e864dc01a7eb0719ecc65fcef4c1cc3d686d718bb2"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.2.1" version: "2.2.0"
url_launcher_windows: url_launcher_windows:
dependency: transitive dependency: transitive
description: description:
@ -1450,10 +1434,10 @@ packages:
dependency: transitive dependency: transitive
description: description:
name: webview_flutter_platform_interface name: webview_flutter_platform_interface
sha256: adb8c03c2be231bea5a8ed0e9039e9d18dbb049603376beaefa15393ede468a5 sha256: "6d9213c65f1060116757a7c473247c60f3f7f332cac33dc417c9e362a9a13e4f"
url: "https://pub.dev" url: "https://pub.dev"
source: hosted source: hosted
version: "2.7.0" version: "2.6.0"
webview_flutter_wkwebview: webview_flutter_wkwebview:
dependency: transitive dependency: transitive
description: description:

4
packages/nextcloud/pubspec.yaml

@ -37,6 +37,10 @@ dev_dependencies:
git: git:
url: https://github.com/nextcloud/neon url: https://github.com/nextcloud/neon
path: packages/neon_lints path: packages/neon_lints
nextcloud_test:
git:
url: https://github.com/nextcloud/neon
path: packages/nextcloud_test
path: ^1.8.3 path: ^1.8.3
process_run: ^0.13.3 process_run: ^0.13.3
test: ^1.24.9 test: ^1.24.9

4
packages/nextcloud/pubspec_overrides.yaml

@ -1,4 +1,4 @@
# melos_managed_dependency_overrides: dynamite,dynamite_runtime,neon_lints # melos_managed_dependency_overrides: dynamite,dynamite_runtime,neon_lints,nextcloud_test
dependency_overrides: dependency_overrides:
dynamite: dynamite:
path: ../dynamite/dynamite path: ../dynamite/dynamite
@ -6,3 +6,5 @@ dependency_overrides:
path: ../dynamite/dynamite_runtime path: ../dynamite/dynamite_runtime
neon_lints: neon_lints:
path: ../neon_lints path: ../neon_lints
nextcloud_test:
path: ../nextcloud_test

11
packages/nextcloud/test/core_test.dart

@ -1,18 +1,17 @@
import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'core', 'core',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
@ -23,8 +22,6 @@ void main() {
expect(() => response.headers, isA<void>()); expect(() => response.headers, isA<void>());
final result = client.core.getVersionCheck(response.body.ocs.data); final result = client.core.getVersionCheck(response.body.ocs.data);
expect(result.versions, isNotNull);
expect(result.versions, isNotEmpty);
expect(result.isSupported, isTrue); expect(result.isSupported, isTrue);
}); });

10
packages/nextcloud/test/dashboard_test.dart

@ -1,17 +1,17 @@
import 'package:nextcloud/dashboard.dart'; import 'package:nextcloud/dashboard.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'dashboard', 'dashboard',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

227
packages/nextcloud/test/helper.dart

@ -1,227 +0,0 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart';
import 'package:process_run/cmd_run.dart';
import 'package:test/test.dart';
import 'package:universal_io/io.dart';
const retryCount = 3;
const timeout = Timeout(Duration(seconds: 30));
class DockerContainer {
DockerContainer({
required this.id,
required this.port,
});
final String id;
final int port;
Future<void> runOccCommand(final List<String> args) async {
final result = await runExecutableArguments(
'docker',
[
'exec',
id,
'php',
'-f',
'occ',
...args,
],
stdout: stdout,
stderr: stderr,
);
if (result.exitCode != 0) {
throw Exception('Failed to run occ command');
}
}
void destroy() => unawaited(
runExecutableArguments(
'docker',
[
'kill',
id,
],
),
);
Future<String> serverLogs() async => (await runExecutableArguments(
'docker',
[
'logs',
id,
],
stdoutEncoding: utf8,
))
.stdout as String;
Future<String> nextcloudLogs() async => (await runExecutableArguments(
'docker',
[
'exec',
id,
'cat',
'data/nextcloud.log',
],
stdoutEncoding: utf8,
))
.stdout as String;
Future<String> allLogs() async => '${await serverLogs()}\n\n${await nextcloudLogs()}';
}
class TestNextcloudClient extends NextcloudClient {
TestNextcloudClient(
super.baseURL, {
super.loginName,
super.password,
super.appPassword,
super.language,
super.appType,
super.userAgentOverride,
super.cookieJar,
});
}
Future<TestNextcloudClient> getTestClient(
final DockerContainer container, {
final String? username = 'user1',
final AppType appType = AppType.unknown,
final String? userAgentOverride,
}) async {
String? appPassword;
if (username != null) {
final inputStream = StreamController<List<int>>();
final process = runExecutableArguments(
'docker',
[
'exec',
'-i',
container.id,
'php',
'-f',
'occ',
'user:add-app-password',
username,
],
stdin: inputStream.stream,
);
inputStream.add(utf8.encode(username));
await inputStream.close();
final result = await process;
if (result.exitCode != 0) {
throw Exception('Failed to run generate app password command\n${result.stderr}\n${result.stdout}');
}
appPassword = (result.stdout as String).split('\n')[1];
}
final client = TestNextcloudClient(
Uri(
scheme: 'http',
host: 'localhost',
port: container.port,
),
loginName: username,
password: username,
appPassword: appPassword,
appType: appType,
userAgentOverride: userAgentOverride,
cookieJar: CookieJar(),
);
var i = 0;
while (true) {
try {
await client.core.getStatus();
break;
} catch (error) {
if (error is HttpException || error is DynamiteApiException) {
i++;
await Future<void>.delayed(const Duration(milliseconds: 100));
if (i >= 300) {
throw TimeoutException('Failed to get the status of the Server. $error');
}
} else {
rethrow;
}
}
}
return client;
}
Future<DockerContainer> getDockerContainer() async {
const dockerImageName = 'ghcr.io/nextcloud/neon/dev';
var result = await runExecutableArguments(
'docker',
[
'images',
'-q',
dockerImageName,
],
);
if (result.exitCode != 0) {
throw Exception('Querying docker image failed: ${result.stderr}');
}
if (result.stdout.toString().isEmpty) {
throw Exception('Missing docker image $dockerImageName. Please build it using ./tool/build-dev-container.sh');
}
late int port;
while (true) {
port = randomPort();
result = await runExecutableArguments(
'docker',
[
'run',
'--rm',
'-d',
'--add-host',
'host.docker.internal:host-gateway',
'-p',
'$port:80',
dockerImageName,
],
);
// 125 means the docker run command itself has failed which indicated the port is already used
if (result.exitCode != 125) {
break;
}
}
if (result.exitCode != 0) {
throw Exception('Failed to run docker container: ${result.stderr}');
}
return DockerContainer(
id: result.stdout.toString().replaceAll('\n', ''),
port: port,
);
}
class TestNextcloudUser {
TestNextcloudUser(
this.username,
this.password, {
this.displayName,
});
final String username;
final String password;
final String? displayName;
}
int randomPort() => 1024 + Random().nextInt(65535 - 1024);
void expectDateInReasonableTimeRange(final DateTime actual, final DateTime expected) {
const duration = Duration(seconds: 10);
expect(actual.isAfter(expected.subtract(duration)), isTrue);
expect(actual.isBefore(expected.add(duration)), isTrue);
}

11
packages/nextcloud/test/news_test.dart

@ -2,19 +2,18 @@ import 'dart:async';
import 'package:nextcloud/news.dart' as news; import 'package:nextcloud/news.dart' as news;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'news', 'news',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
@ -30,8 +29,6 @@ void main() {
test('Is supported', () async { test('Is supported', () async {
final result = await client.news.getVersionCheck(); final result = await client.news.getVersionCheck();
expect(result.versions, isNotNull);
expect(result.versions, isNotEmpty);
expect(result.isSupported, isTrue); expect(result.isSupported, isTrue);
}); });

11
packages/nextcloud/test/notes_test.dart

@ -1,19 +1,18 @@
import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/notes.dart' as notes; import 'package:nextcloud/notes.dart' as notes;
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'notes', 'notes',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
@ -23,8 +22,6 @@ void main() {
expect(() => response.headers, isA<void>()); expect(() => response.headers, isA<void>());
final result = client.notes.getVersionCheck(response.body.ocs.data); final result = client.notes.getVersionCheck(response.body.ocs.data);
expect(result.versions, isNotNull);
expect(result.versions, isNotEmpty);
expect(result.isSupported, isTrue); expect(result.isSupported, isTrue);
}); });

26
packages/nextcloud/test/notifications_test.dart

@ -1,17 +1,17 @@
import 'dart:async'; import 'dart:async';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/notifications.dart'; import 'package:nextcloud/notifications.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group('notifications', () { group('notifications', () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient( client = await TestNextcloudClient.create(
container, container,
username: 'admin', username: 'admin',
); );
@ -42,7 +42,10 @@ void main() {
expect(response.ocs.data[0].notificationId, 2); expect(response.ocs.data[0].notificationId, 2);
expect(response.ocs.data[0].app, 'admin_notifications'); expect(response.ocs.data[0].app, 'admin_notifications');
expect(response.ocs.data[0].user, 'admin'); expect(response.ocs.data[0].user, 'admin');
expectDateInReasonableTimeRange(DateTime.parse(response.ocs.data[0].datetime), startTime); expect(
DateTime.parse(response.ocs.data[0].datetime).millisecondsSinceEpoch,
closeTo(startTime.millisecondsSinceEpoch, 10E3),
);
expect(response.ocs.data[0].objectType, 'admin_notifications'); expect(response.ocs.data[0].objectType, 'admin_notifications');
expect(response.ocs.data[0].objectId, isNotNull); expect(response.ocs.data[0].objectId, isNotNull);
expect(response.ocs.data[0].subject, '123'); expect(response.ocs.data[0].subject, '123');
@ -67,7 +70,10 @@ void main() {
expect(response.body.ocs.data.notificationId, 2); expect(response.body.ocs.data.notificationId, 2);
expect(response.body.ocs.data.app, 'admin_notifications'); expect(response.body.ocs.data.app, 'admin_notifications');
expect(response.body.ocs.data.user, 'admin'); expect(response.body.ocs.data.user, 'admin');
expectDateInReasonableTimeRange(DateTime.parse(response.body.ocs.data.datetime), startTime); expect(
DateTime.parse(response.body.ocs.data.datetime).millisecondsSinceEpoch,
closeTo(startTime.millisecondsSinceEpoch, 10E3),
);
expect(response.body.ocs.data.objectType, 'admin_notifications'); expect(response.body.ocs.data.objectType, 'admin_notifications');
expect(response.body.ocs.data.objectId, isNotNull); expect(response.body.ocs.data.objectId, isNotNull);
expect(response.body.ocs.data.subject, '123'); expect(response.body.ocs.data.subject, '123');
@ -101,10 +107,10 @@ void main() {
group('Push', () { group('Push', () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient( client = await TestNextcloudClient.create(
container, container,
username: 'admin', username: 'admin',
); );

10
packages/nextcloud/test/provisioning_api_test.dart

@ -1,17 +1,17 @@
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/provisioning_api.dart'; import 'package:nextcloud/provisioning_api.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'provisioning_api', 'provisioning_api',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient( client = await TestNextcloudClient.create(
container, container,
username: 'admin', username: 'admin',
); );

10
packages/nextcloud/test/settings_test.dart

@ -1,19 +1,19 @@
import 'dart:convert'; import 'dart:convert';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/settings.dart'; import 'package:nextcloud/settings.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'settings', 'settings',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient( client = await TestNextcloudClient.create(
container, container,
username: 'admin', username: 'admin',
); );

44
packages/nextcloud/test/spreed_test.dart

@ -3,20 +3,20 @@ import 'dart:convert';
import 'package:built_value/json_object.dart'; import 'package:built_value/json_object.dart';
import 'package:nextcloud/core.dart' as core; import 'package:nextcloud/core.dart' as core;
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/spreed.dart' as spreed; import 'package:nextcloud/spreed.dart' as spreed;
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'spreed', 'spreed',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client1; late NextcloudClient client1;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client1 = await getTestClient(container); client1 = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
@ -35,8 +35,6 @@ void main() {
expect(() => response.headers, isA<void>()); expect(() => response.headers, isA<void>());
final result = client1.spreed.getVersionCheck(response.body.ocs.data); final result = client1.spreed.getVersionCheck(response.body.ocs.data);
expect(result.versions, isNotNull);
expect(result.versions, isNotEmpty);
expect(result.isSupported, isTrue); expect(result.isSupported, isTrue);
}); });
@ -185,10 +183,7 @@ void main() {
expect(response.body.ocs.data!.actorType, spreed.ActorType.users.name); expect(response.body.ocs.data!.actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data!.actorId, 'user1'); expect(response.body.ocs.data!.actorId, 'user1');
expect(response.body.ocs.data!.actorDisplayName, 'User One'); expect(response.body.ocs.data!.actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data!.timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data!.timestamp * 1000),
startTime,
);
expect(response.body.ocs.data!.message, 'bla'); expect(response.body.ocs.data!.message, 'bla');
expect(response.body.ocs.data!.messageType, spreed.MessageType.comment.name); expect(response.body.ocs.data!.messageType, spreed.MessageType.comment.name);
}); });
@ -229,10 +224,7 @@ void main() {
expect(response.body.ocs.data[0].actorType, spreed.ActorType.users.name); expect(response.body.ocs.data[0].actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data[0].actorId, 'user1'); expect(response.body.ocs.data[0].actorId, 'user1');
expect(response.body.ocs.data[0].actorDisplayName, 'User One'); expect(response.body.ocs.data[0].actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data[0].timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data[0].timestamp * 1000),
startTime,
);
expect(response.body.ocs.data[0].message, '123'); expect(response.body.ocs.data[0].message, '123');
expect(response.body.ocs.data[0].messageType, spreed.MessageType.comment.name); expect(response.body.ocs.data[0].messageType, spreed.MessageType.comment.name);
@ -240,10 +232,7 @@ void main() {
expect(response.body.ocs.data[0].parent!.actorType, spreed.ActorType.users.name); expect(response.body.ocs.data[0].parent!.actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data[0].parent!.actorId, 'user1'); expect(response.body.ocs.data[0].parent!.actorId, 'user1');
expect(response.body.ocs.data[0].parent!.actorDisplayName, 'User One'); expect(response.body.ocs.data[0].parent!.actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data[0].parent!.timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data[0].parent!.timestamp * 1000),
startTime,
);
expect(response.body.ocs.data[0].parent!.message, 'bla'); expect(response.body.ocs.data[0].parent!.message, 'bla');
expect(response.body.ocs.data[0].parent!.messageType, spreed.MessageType.comment.name); expect(response.body.ocs.data[0].parent!.messageType, spreed.MessageType.comment.name);
@ -251,10 +240,7 @@ void main() {
expect(response.body.ocs.data[1].actorType, spreed.ActorType.users.name); expect(response.body.ocs.data[1].actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data[1].actorId, 'user1'); expect(response.body.ocs.data[1].actorId, 'user1');
expect(response.body.ocs.data[1].actorDisplayName, 'User One'); expect(response.body.ocs.data[1].actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data[1].timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data[1].timestamp * 1000),
startTime,
);
expect(response.body.ocs.data[1].message, 'bla'); expect(response.body.ocs.data[1].message, 'bla');
expect(response.body.ocs.data[1].messageType, spreed.MessageType.comment.name); expect(response.body.ocs.data[1].messageType, spreed.MessageType.comment.name);
@ -262,10 +248,7 @@ void main() {
expect(response.body.ocs.data[2].actorType, spreed.ActorType.users.name); expect(response.body.ocs.data[2].actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data[2].actorId, 'user1'); expect(response.body.ocs.data[2].actorId, 'user1');
expect(response.body.ocs.data[2].actorDisplayName, 'User One'); expect(response.body.ocs.data[2].actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data[2].timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data[2].timestamp * 1000),
startTime,
);
expect(response.body.ocs.data[2].message, 'You created the conversation'); expect(response.body.ocs.data[2].message, 'You created the conversation');
expect(response.body.ocs.data[2].systemMessage, 'conversation_created'); expect(response.body.ocs.data[2].systemMessage, 'conversation_created');
expect(response.body.ocs.data[2].messageType, spreed.MessageType.system.name); expect(response.body.ocs.data[2].messageType, spreed.MessageType.system.name);
@ -302,10 +285,7 @@ void main() {
expect(response.body.ocs.data[0].actorType, spreed.ActorType.users.name); expect(response.body.ocs.data[0].actorType, spreed.ActorType.users.name);
expect(response.body.ocs.data[0].actorId, 'user1'); expect(response.body.ocs.data[0].actorId, 'user1');
expect(response.body.ocs.data[0].actorDisplayName, 'User One'); expect(response.body.ocs.data[0].actorDisplayName, 'User One');
expectDateInReasonableTimeRange( expect(response.body.ocs.data[0].timestamp * 1000, closeTo(startTime.millisecondsSinceEpoch, 10E3));
DateTime.fromMillisecondsSinceEpoch(response.body.ocs.data[0].timestamp * 1000),
startTime,
);
expect(response.body.ocs.data[0].message, '123'); expect(response.body.ocs.data[0].message, '123');
expect(response.body.ocs.data[0].messageType, spreed.MessageType.comment.name); expect(response.body.ocs.data[0].messageType, spreed.MessageType.comment.name);
}); });
@ -379,7 +359,7 @@ void main() {
final room1 = (await client1.spreed.room.joinRoom(token: room.token)).body.ocs.data; final room1 = (await client1.spreed.room.joinRoom(token: room.token)).body.ocs.data;
await client1.spreed.call.joinCall(token: room.token); await client1.spreed.call.joinCall(token: room.token);
final client2 = await getTestClient( final client2 = await TestNextcloudClient.create(
container, container,
username: 'user2', username: 'user2',
); );

10
packages/nextcloud/test/uppush_test.dart

@ -1,17 +1,17 @@
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/uppush.dart'; import 'package:nextcloud/uppush.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'uppush', 'uppush',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient( client = await TestNextcloudClient.create(
container, container,
username: 'admin', username: 'admin',
); );

10
packages/nextcloud/test/user_status_test.dart

@ -1,17 +1,17 @@
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud/user_status.dart' as user_status; import 'package:nextcloud/user_status.dart' as user_status;
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'helper.dart';
void main() { void main() {
group( group(
'user_status', 'user_status',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());

23
packages/nextcloud/test/webdav_test.dart

@ -3,11 +3,10 @@ import 'dart:math';
import 'dart:typed_data'; import 'dart:typed_data';
import 'package:nextcloud/nextcloud.dart'; import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/nextcloud_test.dart';
import 'package:test/test.dart'; import 'package:test/test.dart';
import 'package:universal_io/io.dart'; import 'package:universal_io/io.dart';
import 'helper.dart';
void main() { void main() {
group('constructUri', () { group('constructUri', () {
for (final values in [ for (final values in [
@ -139,11 +138,11 @@ void main() {
'webdav', 'webdav',
() { () {
late DockerContainer container; late DockerContainer container;
late TestNextcloudClient client; late NextcloudClient client;
setUp(() async { setUp(() async {
container = await getDockerContainer(); container = await DockerContainer.create();
client = await getTestClient(container); client = await TestNextcloudClient.create(container);
}); });
tearDown(() => container.destroy()); tearDown(() => container.destroy());
@ -281,12 +280,18 @@ void main() {
expect(response.isCollection, isTrue); expect(response.isCollection, isTrue);
expect(response.mimeType, isNull); expect(response.mimeType, isNull);
expect(response.size, data.lengthInBytes); expect(response.size, data.lengthInBytes);
expectDateInReasonableTimeRange(response.lastModified!, DateTime.now()); expect(
response.lastModified!.millisecondsSinceEpoch,
closeTo(DateTime.now().millisecondsSinceEpoch, 10E3),
);
expect(response.name, 'test'); expect(response.name, 'test');
expect(response.isDirectory, isTrue); expect(response.isDirectory, isTrue);
expect(response.props.davgetcontenttype, isNull); expect(response.props.davgetcontenttype, isNull);
expectDateInReasonableTimeRange(webdavDateFormat.parseUtc(response.props.davgetlastmodified!), DateTime.now()); expect(
webdavDateFormat.parseUtc(response.props.davgetlastmodified!).millisecondsSinceEpoch,
closeTo(DateTime.now().millisecondsSinceEpoch, 10E3),
);
expect(response.props.davresourcetype!.collection, isNotNull); expect(response.props.davresourcetype!.collection, isNotNull);
expect(response.props.ocsize, data.lengthInBytes); expect(response.props.ocsize, data.lengthInBytes);
}); });
@ -355,8 +360,8 @@ void main() {
.prop; .prop;
expect(props.ocfavorite, 1); expect(props.ocfavorite, 1);
expect(webdavDateFormat.parseUtc(props.davgetlastmodified!), lastModifiedDate); expect(webdavDateFormat.parseUtc(props.davgetlastmodified!), lastModifiedDate);
expect(DateTime.fromMillisecondsSinceEpoch(props.nccreationtime! * 1000).isAtSameMomentAs(createdDate), isTrue); expect(props.nccreationtime! * 1000, createdDate.millisecondsSinceEpoch);
expectDateInReasonableTimeRange(DateTime.fromMillisecondsSinceEpoch(props.ncuploadtime! * 1000), uploadTime); expect(props.ncuploadtime! * 1000, closeTo(uploadTime.millisecondsSinceEpoch, 10E3));
}); });
test('Remove properties', () async { test('Remove properties', () async {

1
packages/nextcloud_test/LICENSE

@ -0,0 +1 @@
../../assets/AGPL-3.0.txt

3
packages/nextcloud_test/README.md

@ -0,0 +1,3 @@
# nextcloud_test
A helper package for running tests in the [nextcloud](../nextcloud) package.

1
packages/nextcloud_test/analysis_options.yaml

@ -0,0 +1 @@
include: package:neon_lints/dart.yaml

2
tool/Dockerfile.dev → packages/nextcloud_test/docker/Dockerfile

@ -18,7 +18,7 @@ RUN (sh /entrypoint.sh php -S 0.0.0.0:80 &) && \
# Do not setup the demo user here # Do not setup the demo user here
for user in admin user1 user2; do curl -u "$user:$user" -H "ocs-apirequest: true" -s -o /dev/null http://localhost/ocs/v2.php/cloud/user; done for user in admin user1 user2; do curl -u "$user:$user" -H "ocs-apirequest: true" -s -o /dev/null http://localhost/ocs/v2.php/cloud/user; done
COPY dev/static /usr/src/nextcloud/static COPY static /usr/src/nextcloud/static
ENV PHP_CLI_SERVER_WORKERS=10 ENV PHP_CLI_SERVER_WORKERS=10
CMD ["php", "-S", "0.0.0.0:80"] CMD ["php", "-S", "0.0.0.0:80"]

0
tool/dev/static/nasa.xml → packages/nextcloud_test/docker/static/nasa.xml

0
tool/dev/static/wikipedia.xml → packages/nextcloud_test/docker/static/wikipedia.xml

3
packages/nextcloud_test/lib/nextcloud_test.dart

@ -0,0 +1,3 @@
export 'src/defaults.dart';
export 'src/docker_container.dart';
export 'src/test_client.dart';

7
packages/nextcloud_test/lib/src/defaults.dart

@ -0,0 +1,7 @@
import 'package:test/test.dart';
/// Default retry count for test groups.
const retryCount = 3;
/// Default timeout for test groups.
const timeout = Timeout(Duration(seconds: 30));

112
packages/nextcloud_test/lib/src/docker_container.dart

@ -0,0 +1,112 @@
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:process_run/process_run.dart';
int _randomPort() => 1024 + Random().nextInt(65535 - 1024);
/// Represents a docker container on the system.
class DockerContainer {
DockerContainer._({
required this.id,
required this.port,
});
/// Creates a new docker container and returns its representation.
static Future<DockerContainer> create() async {
const dockerImageName = 'ghcr.io/nextcloud/neon/dev';
var result = await runExecutableArguments(
'docker',
[
'images',
'-q',
dockerImageName,
],
);
if (result.exitCode != 0) {
throw Exception('Querying docker image failed: ${result.stderr}');
}
if (result.stdout.toString().isEmpty) {
throw Exception('Missing docker image $dockerImageName. Please build it using ./tool/build-dev-container.sh');
}
late int port;
while (true) {
port = _randomPort();
result = await runExecutableArguments(
'docker',
[
'run',
'--rm',
'-d',
'--add-host',
'host.docker.internal:host-gateway',
'-p',
'$port:80',
dockerImageName,
],
);
// 125 means the docker run command itself has failed which indicated the port is already used
if (result.exitCode != 125) {
break;
}
}
if (result.exitCode != 0) {
throw Exception('Failed to run docker container: ${result.stderr}');
}
return DockerContainer._(
id: result.stdout.toString().replaceAll('\n', ''),
port: port,
);
}
/// ID of the docker container.
final String id;
/// Assigned port of docker container.
final int port;
/// Removes the docker container from the system.
void destroy() => unawaited(
runExecutableArguments(
'docker',
[
'kill',
id,
],
),
);
/// Reads the server logs.
Future<String> serverLogs() async => (await runExecutableArguments(
'docker',
[
'logs',
id,
],
stdoutEncoding: utf8,
))
.stdout as String;
/// Reads the Nextcloud logs.
Future<String> nextcloudLogs() async => (await runExecutableArguments(
'docker',
[
'exec',
id,
'cat',
'data/nextcloud.log',
],
stdoutEncoding: utf8,
))
.stdout as String;
/// Reads all logs.
///
/// Combines the output of [serverLogs] and [nextcloudLogs].
Future<String> allLogs() async => '${await serverLogs()}\n\n${await nextcloudLogs()}';
}

79
packages/nextcloud_test/lib/src/test_client.dart

@ -0,0 +1,79 @@
import 'dart:async';
import 'dart:convert';
import 'package:nextcloud/core.dart';
import 'package:nextcloud/nextcloud.dart';
import 'package:nextcloud_test/src/docker_container.dart';
import 'package:process_run/process_run.dart';
import 'package:universal_io/io.dart';
/// An extension for creating [NextcloudClient]s based on [DockerContainer]s.
extension TestNextcloudClient on NextcloudClient {
/// Creates a new [NextcloudClient] for a given [container] and [username].
///
/// It is expected that the password of the user matches the its [username].
/// This is the case for the available test docker containers.
static Future<NextcloudClient> create(
final DockerContainer container, {
final String? username = 'user1',
}) async {
String? appPassword;
if (username != null) {
final inputStream = StreamController<List<int>>();
final process = runExecutableArguments(
'docker',
[
'exec',
'-i',
container.id,
'php',
'-f',
'occ',
'user:add-app-password',
username,
],
stdin: inputStream.stream,
);
inputStream.add(utf8.encode(username));
await inputStream.close();
final result = await process;
if (result.exitCode != 0) {
throw Exception('Failed to run generate app password command\n${result.stderr}\n${result.stdout}');
}
appPassword = (result.stdout as String).split('\n')[1];
}
final client = NextcloudClient(
Uri(
scheme: 'http',
host: 'localhost',
port: container.port,
),
loginName: username,
password: username,
appPassword: appPassword,
cookieJar: CookieJar(),
);
var i = 0;
while (true) {
try {
await client.core.getStatus();
break;
} catch (error) {
if (error is HttpException || error is DynamiteApiException) {
i++;
await Future<void>.delayed(const Duration(milliseconds: 100));
if (i >= 300) {
throw TimeoutException('Failed to get the status of the Server. $error');
}
} else {
rethrow;
}
}
}
return client;
}
}

21
packages/nextcloud_test/pubspec.yaml

@ -0,0 +1,21 @@
name: nextcloud_test
version: 1.0.0
publish_to: none
environment:
sdk: '>=3.1.0 <4.0.0'
dependencies:
nextcloud:
git:
url: https://github.com/nextcloud/neon
path: packages/nextcloud
process_run: ^0.13.0
test: ^1.24.0
universal_io: ^2.0.0
dev_dependencies:
neon_lints:
git:
url: https://github.com/nextcloud/neon
path: packages/neon_lints

8
packages/nextcloud_test/pubspec_overrides.yaml

@ -0,0 +1,8 @@
# melos_managed_dependency_overrides: dynamite_runtime,neon_lints,nextcloud
dependency_overrides:
dynamite_runtime:
path: ../dynamite/dynamite_runtime
neon_lints:
path: ../neon_lints
nextcloud:
path: ../nextcloud

2
tool/build-dev-container.sh

@ -6,4 +6,4 @@ source tool/common.sh
tag="$(image_tag "dev:latest")" tag="$(image_tag "dev:latest")"
# shellcheck disable=SC2046 # shellcheck disable=SC2046
docker buildx build --tag "$tag" $(cache_build_args "$tag") -f - ./tool < tool/Dockerfile.dev docker buildx build --tag "$tag" $(cache_build_args "$tag") -f - ./packages/nextcloud_test/docker < packages/nextcloud_test/docker/Dockerfile

Loading…
Cancel
Save