Browse Source

Merge pull request #125 from provokateurin/feature/refactor-docker-tests

nextcloud: Refactor test docker container setup
pull/129/head
Kate 2 years ago committed by GitHub
parent
commit
0fd5d909e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 10
      packages/nextcloud/test/core_test.dart
  2. 208
      packages/nextcloud/test/helper.dart
  3. 10
      packages/nextcloud/test/news_test.dart
  4. 10
      packages/nextcloud/test/notes_test.dart
  5. 22
      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();

208
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, final String name;
super.language, }
super.appType,
super.userAgentOverride, class DockerContainer {
DockerContainer({
required this.id,
required this.port,
}); });
final String containerID; 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,70 +83,77 @@ 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; 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));
}
} }
static Future<TestNextcloudClient> getPreparedClient( return client;
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));
Future<DockerContainer> getDockerContainer(final DockerImage image) async {
late ProcessResult result; late ProcessResult result;
late int port; late int port;
while (true) { while (true) {
@ -156,7 +168,7 @@ class TestHelper {
'$port:80', '$port:80',
'--add-host', '--add-host',
'host.docker.internal:host-gateway', 'host.docker.internal:host-gateway',
dockerImageName, image.name,
], ],
); );
// 125 means the docker run command itself has failed which indicated the port is already used // 125 means the docker run command itself has failed which indicated the port is already used
@ -169,56 +181,64 @@ class TestHelper {
throw Exception('Failed to run docker container: ${result.stderr}'); throw Exception('Failed to run docker container: ${result.stderr}');
} }
final containerID = result.stdout.toString().replaceAll('\n', ''); return DockerContainer(
id: result.stdout.toString().replaceAll('\n', ''),
port: port,
);
}
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';
var clientPassword = password;
if (useAppPassword) {
final inputStream = StreamController<List<int>>(); final inputStream = StreamController<List<int>>();
final process = runExecutableArguments( final process = runExecutableArguments(
'docker', 'docker',
[ [
'exec', 'build',
'-i', '-t',
containerID, dockerImageName,
'php',
'-f', '-f',
'occ', '-',
'user:add-app-password', './test',
username!,
], ],
stdout: stdout,
stderr: stderr,
stdin: inputStream.stream, stdin: inputStream.stream,
); );
inputStream.add(utf8.encode(password!)); inputStream.add(
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 run generate app password command\n${result.stderr}\n${result.stdout}'); throw Exception('Failed to build docker image');
}
clientPassword = (result.stdout as String).split('\n')[1];
} }
final client = TestNextcloudClient( return DockerImage(
'http://localhost:$port', name: dockerImageName,
containerID,
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));
}
}
return client;
}
} }
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();

22
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