Browse Source

nextcloud: Refactor test docker container setup

pull/125/head
jld3103 2 years ago
parent
commit
b6765c0d12
No known key found for this signature in database
GPG Key ID: 9062417B9E8EB7B3
  1. 10
      packages/nextcloud/test/core_test.dart
  2. 282
      packages/nextcloud/test/helper.dart
  3. 10
      packages/nextcloud/test/news_test.dart
  4. 10
      packages/nextcloud/test/notes_test.dart
  5. 24
      packages/nextcloud/test/notifications_test.dart
  6. 10
      packages/nextcloud/test/provisioning_api_test.dart
  7. 10
      packages/nextcloud/test/user_status_test.dart
  8. 16
      packages/nextcloud/test/webdav_test.dart

10
packages/nextcloud/test/core_test.dart

@ -4,7 +4,7 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage( final image = await getDockerImage(
apps: [ apps: [
'news', 'news',
'notes', 'notes',
@ -12,9 +12,13 @@ Future main() async {
); );
group('core', () { group('core', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
test('Is supported', () async { test('Is supported', () async {
final response = await client.core.isSupported(); final response = await client.core.isSupported();

282
packages/nextcloud/test/helper.dart

@ -12,25 +12,30 @@ const String nextcloudVersion = '25.0.1';
const String defaultUsername = 'user1'; const String defaultUsername = 'user1';
const String defaultPassword = 'user1'; const String defaultPassword = 'user1';
class TestNextcloudClient extends NextcloudClient { class DockerImage {
TestNextcloudClient( DockerImage({
super.baseURL, required this.name,
this.containerID, {
super.username,
super.password,
super.language,
super.appType,
super.userAgentOverride,
}); });
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<String> args) async { Future runOccCommand(final List<String> args) async {
final result = await runExecutableArguments( final result = await runExecutableArguments(
'docker', 'docker',
[ [
'exec', 'exec',
containerID, id,
'php', 'php',
'-f', '-f',
'occ', 'occ',
@ -48,7 +53,7 @@ class TestNextcloudClient extends NextcloudClient {
'docker', 'docker',
[ [
'kill', 'kill',
containerID, id,
], ],
); );
@ -57,7 +62,7 @@ class TestNextcloudClient extends NextcloudClient {
'docker', 'docker',
[ [
'logs', 'logs',
containerID, id,
], ],
stdoutEncoding: utf8, stdoutEncoding: utf8,
)) ))
@ -66,7 +71,7 @@ class TestNextcloudClient extends NextcloudClient {
'docker', 'docker',
[ [
'exec', 'exec',
containerID, id,
'cat', 'cat',
'data/nextcloud.log', 'data/nextcloud.log',
], ],
@ -78,147 +83,162 @@ class TestNextcloudClient extends NextcloudClient {
} }
} }
class TestHelper { class TestNextcloudClient extends NextcloudClient {
static Future<String> prepareDockerImage({ TestNextcloudClient(
final List<TestNextcloudUser>? users, super.baseURL, {
final List<String>? apps, super.username,
}) async { super.password,
final hash = sha1 super.language,
.convert( super.appType,
utf8.encode( super.userAgentOverride,
<String>[ });
if (users != null) }
for (final user in users) user.toString(),
if (apps != null) ...apps,
].join(),
),
)
.toString();
final dockerImageName = 'nextcloud-neon-$hash';
Future<TestNextcloudClient> 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<List<int>>(); final inputStream = StreamController<List<int>>();
final process = runExecutableArguments( final process = runExecutableArguments(
'docker', 'docker',
[ [
'build', 'exec',
'-t', '-i',
dockerImageName, container.id,
'php',
'-f', '-f',
'-', 'occ',
'./test', 'user:add-app-password',
username!,
], ],
stdout: stdout,
stderr: stderr,
stdin: inputStream.stream, stdin: inputStream.stream,
); );
inputStream.add( inputStream.add(utf8.encode(password!));
utf8.encode(
TestDockerHelper.generateInstructions(
nextcloudVersion,
users: users,
apps: apps,
),
),
);
await inputStream.close(); await inputStream.close();
final result = await process; final result = await process;
if (result.exitCode != 0) { 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}');
} }
clientPassword = (result.stdout as String).split('\n')[1];
return dockerImageName;
} }
static Future<TestNextcloudClient> getPreparedClient( final client = TestNextcloudClient(
final String dockerImageName, { 'http://localhost:${container.port}',
final String? username = defaultUsername, username: username,
final String? password = defaultPassword, password: clientPassword,
final bool useAppPassword = false, appType: appType,
final AppType appType = AppType.unknown, userAgentOverride: userAgentOverride,
final String? userAgentOverride, );
}) async {
// ignore: prefer_asserts_with_message while (true) {
assert(!useAppPassword || (username != null && password != null)); // Test will timeout after 30s
try {
late ProcessResult result; await client.core.getStatus();
late int port; break;
while (true) { } catch (_) {
port = randomPort(); await Future.delayed(const Duration(milliseconds: 100));
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 containerID = result.stdout.toString().replaceAll('\n', ''); return client;
}
var clientPassword = password; Future<DockerContainer> getDockerContainer(final DockerImage image) async {
if (useAppPassword) { late ProcessResult result;
final inputStream = StreamController<List<int>>(); late int port;
final process = runExecutableArguments( while (true) {
'docker', port = randomPort();
[ result = await runExecutableArguments(
'exec', 'docker',
'-i', [
containerID, 'run',
'php', '--rm',
'-f', '-d',
'occ', '-p',
'user:add-app-password', '$port:80',
username!, '--add-host',
], 'host.docker.internal:host-gateway',
stdin: inputStream.stream, image.name,
); ],
inputStream.add(utf8.encode(password!)); );
await inputStream.close(); // 125 means the docker run command itself has failed which indicated the port is already used
if (result.exitCode != 125) {
final result = await process; break;
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];
} }
}
final client = TestNextcloudClient( if (result.exitCode != 0) {
'http://localhost:$port', throw Exception('Failed to run docker container: ${result.stderr}');
containerID, }
username: username,
password: clientPassword,
appType: appType,
userAgentOverride: userAgentOverride,
);
while (true) { return DockerContainer(
// Test will timeout after 30s id: result.stdout.toString().replaceAll('\n', ''),
try { port: port,
await client.core.getStatus(); );
break; }
} catch (_) {
await Future.delayed(const Duration(milliseconds: 100));
}
}
return client; Future<DockerImage> getDockerImage({
final List<TestNextcloudUser>? users,
final List<String>? apps,
}) async {
final hash = sha1
.convert(
utf8.encode(
<String>[
if (users != null)
for (final user in users) user.toString(),
if (apps != null) ...apps,
].join(),
),
)
.toString();
final dockerImageName = 'nextcloud-neon-$hash';
final inputStream = StreamController<List<int>>();
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 { class TestNextcloudUser {

10
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'; const nasaFeedURL = 'https://www.nasa.gov/rss/dyn/breaking_news.rss';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(apps: ['news']); final image = await getDockerImage(apps: ['news']);
group('news', () { group('news', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
Future<NewsListFeeds> addWikipediaFeed([final int? folderID]) => client.news.addFeed( Future<NewsListFeeds> addWikipediaFeed([final int? folderID]) => client.news.addFeed(
url: wikipediaFeedURL, url: wikipediaFeedURL,

10
packages/nextcloud/test/notes_test.dart

@ -4,12 +4,16 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(apps: ['notes']); final image = await getDockerImage(apps: ['notes']);
group('notes', () { group('notes', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
test('Is supported', () async { test('Is supported', () async {
final response = await client.notes.isSupported(); final response = await client.notes.isSupported();

24
packages/nextcloud/test/notifications_test.dart

@ -8,18 +8,20 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(); final image = await getDockerImage();
group('notifications', () { group('notifications', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp( setUp(() async {
() async => client = await TestHelper.getPreparedClient( container = await getDockerContainer(image);
dockerImageName, client = await getTestClient(
container,
username: 'admin', username: 'admin',
password: 'admin', password: 'admin',
), );
); });
tearDown(() => client.destroy()); tearDown(() => container.destroy());
Future sendTestNotification() async { Future sendTestNotification() async {
await client.notifications.sendAdminNotification( await client.notifications.sendAdminNotification(
@ -97,16 +99,18 @@ Future main() async {
}); });
group('push notifications', () { group('push notifications', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async { setUp(() async {
client = await TestHelper.getPreparedClient( container = await getDockerContainer(image);
dockerImageName, client = await getTestClient(
container,
username: 'admin', username: 'admin',
// We need to use app passwords in order to register push devices // We need to use app passwords in order to register push devices
useAppPassword: true, 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) // The key size has to be 2048, other sizes are not accepted by Nextcloud (at the moment at least)
// ignore: avoid_redundant_argument_values // ignore: avoid_redundant_argument_values

10
packages/nextcloud/test/provisioning_api_test.dart

@ -4,12 +4,16 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(); final image = await getDockerImage();
group('provisioning_api', () { group('provisioning_api', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
test('Get current user', () async { test('Get current user', () async {
final user = await client.provisioningApi.getCurrentUser(); final user = await client.provisioningApi.getCurrentUser();

10
packages/nextcloud/test/user_status_test.dart

@ -4,12 +4,16 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(); final image = await getDockerImage();
group('user_status', () { group('user_status', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
test('Find all predefined statuses', () async { test('Find all predefined statuses', () async {
final expectedStatusIDs = ['meeting', 'commuting', 'remote-work', 'sick-leave', 'vacationing']; final expectedStatusIDs = ['meeting', 'commuting', 'remote-work', 'sick-leave', 'vacationing'];

16
packages/nextcloud/test/webdav_test.dart

@ -8,16 +8,22 @@ import 'package:test/test.dart';
import 'helper.dart'; import 'helper.dart';
Future main() async { Future main() async {
final dockerImageName = await TestHelper.prepareDockerImage(); final image = await getDockerImage();
group('webdav', () { group('webdav', () {
late DockerContainer container;
late TestNextcloudClient client; late TestNextcloudClient client;
setUp(() async => client = await TestHelper.getPreparedClient(dockerImageName)); setUp(() async {
tearDown(() => client.destroy()); container = await getDockerContainer(image);
client = await getTestClient(container);
});
tearDown(() => container.destroy());
test('Fail without username', () async { test('Fail without username', () async {
await client.destroy(); client = await getTestClient(
client = await TestHelper.getPreparedClient(dockerImageName, username: null); container,
username: null,
);
expect(() => client.webdav, throwsException); expect(() => client.webdav, throwsException);
}); });

Loading…
Cancel
Save