diff --git a/packages/nextcloud/test/core_test.dart b/packages/nextcloud/test/core_test.dart index e94829b9..1696bd6b 100644 --- a/packages/nextcloud/test/core_test.dart +++ b/packages/nextcloud/test/core_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage( + final image = await getDockerImage( apps: [ 'news', 'notes', @@ -12,9 +12,13 @@ Future main() async { ); group('core', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); test('Is supported', () async { final response = await client.core.isSupported(); diff --git a/packages/nextcloud/test/helper.dart b/packages/nextcloud/test/helper.dart index c5962b2e..0d339906 100644 --- a/packages/nextcloud/test/helper.dart +++ b/packages/nextcloud/test/helper.dart @@ -12,25 +12,30 @@ const String nextcloudVersion = '25.0.1'; const String defaultUsername = 'user1'; const String defaultPassword = 'user1'; -class TestNextcloudClient extends NextcloudClient { - TestNextcloudClient( - super.baseURL, - this.containerID, { - super.username, - super.password, - super.language, - super.appType, - super.userAgentOverride, +class DockerImage { + DockerImage({ + required this.name, }); - final String containerID; + final String name; +} + +class DockerContainer { + DockerContainer({ + required this.id, + required this.port, + }); + + final String id; + + final int port; Future runOccCommand(final List args) async { final result = await runExecutableArguments( 'docker', [ 'exec', - containerID, + id, 'php', '-f', 'occ', @@ -48,7 +53,7 @@ class TestNextcloudClient extends NextcloudClient { 'docker', [ 'kill', - containerID, + id, ], ); @@ -57,7 +62,7 @@ class TestNextcloudClient extends NextcloudClient { 'docker', [ 'logs', - containerID, + id, ], stdoutEncoding: utf8, )) @@ -66,7 +71,7 @@ class TestNextcloudClient extends NextcloudClient { 'docker', [ 'exec', - containerID, + id, 'cat', 'data/nextcloud.log', ], @@ -78,147 +83,162 @@ class TestNextcloudClient extends NextcloudClient { } } -class TestHelper { - static Future prepareDockerImage({ - final List? users, - final List? apps, - }) async { - final hash = sha1 - .convert( - utf8.encode( - [ - if (users != null) - for (final user in users) user.toString(), - if (apps != null) ...apps, - ].join(), - ), - ) - .toString(); - - final dockerImageName = 'nextcloud-neon-$hash'; +class TestNextcloudClient extends NextcloudClient { + TestNextcloudClient( + super.baseURL, { + super.username, + super.password, + super.language, + super.appType, + super.userAgentOverride, + }); +} +Future getTestClient( + final DockerContainer container, { + final String? username = defaultUsername, + final String? password = defaultPassword, + final bool useAppPassword = false, + final AppType appType = AppType.unknown, + final String? userAgentOverride, +}) async { + // ignore: prefer_asserts_with_message + assert(!useAppPassword || (username != null && password != null)); + + var clientPassword = password; + if (useAppPassword) { final inputStream = StreamController>(); final process = runExecutableArguments( 'docker', [ - 'build', - '-t', - dockerImageName, + 'exec', + '-i', + container.id, + 'php', '-f', - '-', - './test', + 'occ', + 'user:add-app-password', + username!, ], - stdout: stdout, - stderr: stderr, stdin: inputStream.stream, ); - inputStream.add( - utf8.encode( - TestDockerHelper.generateInstructions( - nextcloudVersion, - users: users, - apps: apps, - ), - ), - ); + inputStream.add(utf8.encode(password!)); await inputStream.close(); final result = await process; if (result.exitCode != 0) { - throw Exception('Failed to build docker image'); + throw Exception('Failed to run generate app password command\n${result.stderr}\n${result.stdout}'); } - - return dockerImageName; + clientPassword = (result.stdout as String).split('\n')[1]; } - static Future getPreparedClient( - final String dockerImageName, { - final String? username = defaultUsername, - final String? password = defaultPassword, - final bool useAppPassword = false, - final AppType appType = AppType.unknown, - final String? userAgentOverride, - }) async { - // ignore: prefer_asserts_with_message - assert(!useAppPassword || (username != null && password != null)); - - late ProcessResult result; - late int port; - while (true) { - port = randomPort(); - result = await runExecutableArguments( - 'docker', - [ - 'run', - '--rm', - '-d', - '-p', - '$port:80', - '--add-host', - 'host.docker.internal:host-gateway', - 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}'); + final client = TestNextcloudClient( + 'http://localhost:${container.port}', + username: username, + password: clientPassword, + appType: appType, + userAgentOverride: userAgentOverride, + ); + + while (true) { + // Test will timeout after 30s + try { + await client.core.getStatus(); + break; + } catch (_) { + await Future.delayed(const Duration(milliseconds: 100)); } + } - final containerID = result.stdout.toString().replaceAll('\n', ''); + return client; +} - var clientPassword = password; - if (useAppPassword) { - final inputStream = StreamController>(); - final process = runExecutableArguments( - 'docker', - [ - 'exec', - '-i', - containerID, - 'php', - '-f', - 'occ', - 'user:add-app-password', - username!, - ], - stdin: inputStream.stream, - ); - inputStream.add(utf8.encode(password!)); - 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}'); - } - clientPassword = (result.stdout as String).split('\n')[1]; +Future getDockerContainer(final DockerImage image) async { + late ProcessResult result; + late int port; + while (true) { + port = randomPort(); + result = await runExecutableArguments( + 'docker', + [ + 'run', + '--rm', + '-d', + '-p', + '$port:80', + '--add-host', + 'host.docker.internal:host-gateway', + image.name, + ], + ); + // 125 means the docker run command itself has failed which indicated the port is already used + if (result.exitCode != 125) { + break; } + } - final client = TestNextcloudClient( - 'http://localhost:$port', - containerID, - username: username, - password: clientPassword, - appType: appType, - userAgentOverride: userAgentOverride, - ); + if (result.exitCode != 0) { + throw Exception('Failed to run docker container: ${result.stderr}'); + } - while (true) { - // Test will timeout after 30s - try { - await client.core.getStatus(); - break; - } catch (_) { - await Future.delayed(const Duration(milliseconds: 100)); - } - } + return DockerContainer( + id: result.stdout.toString().replaceAll('\n', ''), + port: port, + ); +} - return client; +Future getDockerImage({ + final List? users, + final List? apps, +}) async { + final hash = sha1 + .convert( + utf8.encode( + [ + if (users != null) + for (final user in users) user.toString(), + if (apps != null) ...apps, + ].join(), + ), + ) + .toString(); + + final dockerImageName = 'nextcloud-neon-$hash'; + + final inputStream = StreamController>(); + final process = runExecutableArguments( + 'docker', + [ + 'build', + '-t', + dockerImageName, + '-f', + '-', + './test', + ], + stdout: stdout, + stderr: stderr, + stdin: inputStream.stream, + ); + inputStream.add( + utf8.encode( + TestDockerHelper.generateInstructions( + nextcloudVersion, + users: users, + apps: apps, + ), + ), + ); + await inputStream.close(); + + final result = await process; + if (result.exitCode != 0) { + throw Exception('Failed to build docker image'); } + + return DockerImage( + name: dockerImageName, + ); } class TestNextcloudUser { diff --git a/packages/nextcloud/test/news_test.dart b/packages/nextcloud/test/news_test.dart index dd5a88bd..d16c265c 100644 --- a/packages/nextcloud/test/news_test.dart +++ b/packages/nextcloud/test/news_test.dart @@ -7,12 +7,16 @@ const wikipediaFeedURL = 'https://en.wikipedia.org/w/api.php?action=featuredfeed const nasaFeedURL = 'https://www.nasa.gov/rss/dyn/breaking_news.rss'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(apps: ['news']); + final image = await getDockerImage(apps: ['news']); group('news', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); Future addWikipediaFeed([final int? folderID]) => client.news.addFeed( url: wikipediaFeedURL, diff --git a/packages/nextcloud/test/notes_test.dart b/packages/nextcloud/test/notes_test.dart index a585bb41..1af2e752 100644 --- a/packages/nextcloud/test/notes_test.dart +++ b/packages/nextcloud/test/notes_test.dart @@ -4,12 +4,16 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(apps: ['notes']); + final image = await getDockerImage(apps: ['notes']); group('notes', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); test('Is supported', () async { final response = await client.notes.isSupported(); diff --git a/packages/nextcloud/test/notifications_test.dart b/packages/nextcloud/test/notifications_test.dart index 2f4d4cc7..7be7e7ae 100644 --- a/packages/nextcloud/test/notifications_test.dart +++ b/packages/nextcloud/test/notifications_test.dart @@ -8,18 +8,20 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(); + final image = await getDockerImage(); group('notifications', () { + late DockerContainer container; late TestNextcloudClient client; - setUp( - () async => client = await TestHelper.getPreparedClient( - dockerImageName, + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient( + container, username: 'admin', password: 'admin', - ), - ); - tearDown(() => client.destroy()); + ); + }); + tearDown(() => container.destroy()); Future sendTestNotification() async { await client.notifications.sendAdminNotification( @@ -97,16 +99,18 @@ Future main() async { }); group('push notifications', () { + late DockerContainer container; late TestNextcloudClient client; setUp(() async { - client = await TestHelper.getPreparedClient( - dockerImageName, + container = await getDockerContainer(image); + client = await getTestClient( + container, username: 'admin', // We need to use app passwords in order to register push devices useAppPassword: true, ); }); - tearDown(() => client.destroy()); + tearDown(() => container.destroy()); // The key size has to be 2048, other sizes are not accepted by Nextcloud (at the moment at least) // ignore: avoid_redundant_argument_values diff --git a/packages/nextcloud/test/provisioning_api_test.dart b/packages/nextcloud/test/provisioning_api_test.dart index 87442d5c..27b64df2 100644 --- a/packages/nextcloud/test/provisioning_api_test.dart +++ b/packages/nextcloud/test/provisioning_api_test.dart @@ -4,12 +4,16 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(); + final image = await getDockerImage(); group('provisioning_api', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); test('Get current user', () async { final user = await client.provisioningApi.getCurrentUser(); diff --git a/packages/nextcloud/test/user_status_test.dart b/packages/nextcloud/test/user_status_test.dart index 70e2f220..e682939e 100644 --- a/packages/nextcloud/test/user_status_test.dart +++ b/packages/nextcloud/test/user_status_test.dart @@ -4,12 +4,16 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(); + final image = await getDockerImage(); group('user_status', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); test('Find all predefined statuses', () async { final expectedStatusIDs = ['meeting', 'commuting', 'remote-work', 'sick-leave', 'vacationing']; diff --git a/packages/nextcloud/test/webdav_test.dart b/packages/nextcloud/test/webdav_test.dart index addbbbb0..6d37ee16 100644 --- a/packages/nextcloud/test/webdav_test.dart +++ b/packages/nextcloud/test/webdav_test.dart @@ -8,16 +8,22 @@ import 'package:test/test.dart'; import 'helper.dart'; Future main() async { - final dockerImageName = await TestHelper.prepareDockerImage(); + final image = await getDockerImage(); group('webdav', () { + late DockerContainer container; late TestNextcloudClient client; - setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); - tearDown(() => client.destroy()); + setUp(() async { + container = await getDockerContainer(image); + client = await getTestClient(container); + }); + tearDown(() => container.destroy()); test('Fail without username', () async { - await client.destroy(); - client = await TestHelper.getPreparedClient(dockerImageName, username: null); + client = await getTestClient( + container, + username: null, + ); expect(() => client.webdav, throwsException); });