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. 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';
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();

282
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<String> 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<String> prepareDockerImage({
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';
class TestNextcloudClient extends NextcloudClient {
TestNextcloudClient(
super.baseURL, {
super.username,
super.password,
super.language,
super.appType,
super.userAgentOverride,
});
}
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 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<TestNextcloudClient> 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<List<int>>();
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<DockerContainer> 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<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 {

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';
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<NewsListFeeds> addWikipediaFeed([final int? folderID]) => client.news.addFeed(
url: wikipediaFeedURL,

10
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();

24
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

10
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();

10
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'];

16
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);
});

Loading…
Cancel
Save