Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
363 changes: 210 additions & 153 deletions packages/nextcloud/test/webdav_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
library webdav_test;

import 'dart:convert';
import 'dart:io';
import 'dart:math';
import 'dart:typed_data';

import 'package:nextcloud/nextcloud.dart';
Expand All @@ -23,12 +23,6 @@ void main() {
});
tearDown(() => container.destroy());

test('Get status', () async {
final options = await client.webdav.options();
expect(options.capabilities, containsAll(['1', '3']));
expect(options.searchCapabilities, hasLength(0));
});

test('List directory', () async {
final responses = (await client.webdav.propfind(
'/',
Expand Down Expand Up @@ -58,152 +52,6 @@ void main() {
expect(responses, hasLength(48));
});

test('Create directory', () async {
final response = await client.webdav.mkcol('test');
expect(response.statusCode, equals(201));
});

test('Upload files', () async {
final pngBytes = File('test/files/test.png').readAsBytesSync();
final txtBytes = File('test/files/test.txt').readAsBytesSync();

var response = await client.webdav.put(pngBytes, 'test.png');
expect(response.statusCode, equals(201));

response = await client.webdav.put(txtBytes, 'test.txt');
expect(response.statusCode, equals(201));

final responses = (await client.webdav.propfind(
'/',
prop: WebDavPropWithoutValues.fromBools(
ocsize: true,
),
))
.responses;
expect(responses, hasLength(12));
expect(
responses.singleWhere((final response) => response.href!.endsWith('/test.png')).propstats.first.prop.ocsize,
pngBytes.lengthInBytes,
);
expect(
responses.singleWhere((final response) => response.href!.endsWith('/test.txt')).propstats.first.prop.ocsize,
txtBytes.lengthInBytes,
);
});

test('Upload file', () async {
final lastModified = DateTime.fromMillisecondsSinceEpoch(DateTime.now().millisecondsSinceEpoch ~/ 1000 * 1000);
final created = lastModified.subtract(const Duration(hours: 1));
final txtBytes = File('test/files/test.txt').readAsBytesSync();

final response = await client.webdav.put(
txtBytes,
'test.txt',
lastModified: lastModified,
created: created,
);
expect(response.statusCode, equals(201));

final props = (await client.webdav.propfind(
'/',
prop: WebDavPropWithoutValues.fromBools(
davgetlastmodified: true,
nccreationtime: true,
),
))
.responses
.singleWhere((final response) => response.href!.endsWith('/test.txt'))
.propstats
.first
.prop;
expect(
webdavDateFormat.parseUtc(props.davgetlastmodified!).millisecondsSinceEpoch,
lastModified.millisecondsSinceEpoch,
);
expect(
DateTime.fromMillisecondsSinceEpoch(props.nccreationtime! * 1000).millisecondsSinceEpoch,
created.millisecondsSinceEpoch,
);
});

test('Download file', () async {
final response = await client.webdav.get('Nextcloud.png');
expect(response, isNotEmpty);
});

test('Delete file', () async {
final response = await client.webdav.delete('Nextcloud.png');
expect(response.statusCode, 204);
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(0));
});

test('Copy file', () async {
final response = await client.webdav.copy(
'Nextcloud.png',
'test.png',
);
expect(response.statusCode, 201);
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(1));
expect(responses.where((final response) => response.href!.endsWith('/test.png')), hasLength(1));
});

test('Copy file (overwrite fail)', () async {
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');

expect(
() => client.webdav.copy('1.txt', '2.txt'),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 412)),
);
});

test('Copy file (overwrite success)', () async {
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');

final response = await client.webdav.copy(
'1.txt',
'2.txt',
overwrite: true,
);
expect(response.statusCode, 204);
});

test('Move file', () async {
final response = await client.webdav.move(
'Nextcloud.png',
'test.png',
);
expect(response.statusCode, 201);
final responses = (await client.webdav.propfind('/')).responses;
expect(responses.where((final response) => response.href!.endsWith('/Nextcloud.png')), hasLength(0));
expect(responses.where((final response) => response.href!.endsWith('/test.png')), hasLength(1));
});

test('Move file (overwrite fail)', () async {
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');

expect(
() => client.webdav.move('1.txt', '2.txt'),
throwsA(predicate((final e) => (e! as DynamiteApiException).statusCode == 412)),
);
});

test('Move file (overwrite success)', () async {
await client.webdav.put(Uint8List.fromList(utf8.encode('1')), '1.txt');
await client.webdav.put(Uint8List.fromList(utf8.encode('2')), '2.txt');

final response = await client.webdav.move(
'1.txt',
'2.txt',
overwrite: true,
);
expect(response.statusCode, 204);
});

test('Get file props', () async {
final response = (await client.webdav.propfind(
'Nextcloud.png',
Expand Down Expand Up @@ -428,5 +276,214 @@ void main() {
.prop;
expect(props.ocfavorite, 0);
});

group('litmus', () {
group('basic', () {
test('options', () async {
final options = await client.webdav.options();
expect(options.capabilities, contains('1'));
expect(options.capabilities, contains('3'));
// Nextcloud only contains a fake plugin for Class 2 support: https://github.com/nextcloud/server/blob/master/apps/dav/lib/Connector/Sabre/FakeLockerPlugin.php
// It does not actually support locking and is only there for compatibility reasons.
expect(options.capabilities, isNot(contains('2')));
});

for (final (name, path) in [
('put_get', 'res'),
('put_get_utf8_segment', 'res-%e2%82%ac'),
]) {
test(name, () async {
final content = Uint8List.fromList(utf8.encode('This is a test file'));

final response = await client.webdav.put(content, path);
expect(response.statusCode, 201);

final downloadedContent = await client.webdav.get(path);
expect(downloadedContent, equals(content));
});
}

test('put_no_parent', () async {
expect(
() => client.webdav.put(Uint8List(0), '409me/noparent.txt'),
// https://github.com/nextcloud/server/issues/39625
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
);
});

test('delete', () async {
await client.webdav.put(Uint8List(0), 'test.txt');

final response = await client.webdav.delete('test.txt');
expect(response.statusCode, 204);
});

test('delete_null', () async {
expect(
() => client.webdav.delete('test.txt'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 404)),
);
});

// delete_fragment: This test is not applicable because the fragment is already removed on the client side

test('mkcol', () async {
final response = await client.webdav.mkcol('test');
expect(response.statusCode, 201);
});

test('mkcol_again', () async {
await client.webdav.mkcol('test');

expect(
() => client.webdav.mkcol('test'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 405)),
);
});

test('delete_coll', () async {
var response = await client.webdav.mkcol('test');

response = await client.webdav.delete('test');
expect(response.statusCode, 204);
});

test('mkcol_no_parent', () async {
expect(
() => client.webdav.mkcol('409me/noparent'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
);
});

// mkcol_with_body: This test is not applicable because we only write valid request bodies
});

group('copymove', () {
test('copy_simple', () async {
await client.webdav.mkcol('src');

final response = await client.webdav.copy('src', 'dst');
expect(response.statusCode, 201);
});

test('copy_overwrite', () async {
await client.webdav.mkcol('src');
await client.webdav.mkcol('dst');

expect(
() => client.webdav.copy('src', 'dst'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
);

final response = await client.webdav.copy('src', 'dst', overwrite: true);
expect(response.statusCode, 204);
});

test('copy_nodestcoll', () async {
await client.webdav.mkcol('src');

expect(
() => client.webdav.copy('src', 'nonesuch/dst'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 409)),
);
});

test('copy_coll', () async {
await client.webdav.mkcol('src');
await client.webdav.mkcol('src/sub');
for (var i = 0; i < 10; i++) {
await client.webdav.put(Uint8List(0), 'src/$i.txt');
}
await client.webdav.copy('src', 'dst1');
await client.webdav.copy('src', 'dst2');

expect(
() => client.webdav.copy('src', 'dst1'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
);

var response = await client.webdav.copy('src', 'dst2', overwrite: true);
expect(response.statusCode, 204);

for (var i = 0; i < 10; i++) {
response = await client.webdav.delete('dst1/$i.txt');
expect(response.statusCode, 204);
}

response = await client.webdav.delete('dst1/sub');
expect(response.statusCode, 204);

response = await client.webdav.delete('dst2');
expect(response.statusCode, 204);
});

// copy_shallow: Does not work on litmus, let's wait for https://github.com/nextcloud/server/issues/39627

test('move', () async {
await client.webdav.put(Uint8List(0), 'src1.txt');
await client.webdav.put(Uint8List(0), 'src2.txt');
await client.webdav.mkcol('coll');

var response = await client.webdav.move('src1.txt', 'dst.txt');
expect(response.statusCode, 201);

expect(
() => client.webdav.move('src2.txt', 'dst.txt'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
);

response = await client.webdav.move('src2.txt', 'dst.txt', overwrite: true);
expect(response.statusCode, 204);
});

test('move_coll', () async {
await client.webdav.mkcol('src');
await client.webdav.mkcol('src/sub');
for (var i = 0; i < 10; i++) {
await client.webdav.put(Uint8List(0), 'src/$i.txt');
}
await client.webdav.put(Uint8List(0), 'noncoll');
await client.webdav.copy('src', 'dst2');
await client.webdav.move('src', 'dst1');

expect(
() => client.webdav.move('dst1', 'dst2'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
);

await client.webdav.move('dst2', 'dst1', overwrite: true);
await client.webdav.copy('dst1', 'dst2');

for (var i = 0; i < 10; i++) {
final response = await client.webdav.delete('dst1/$i.txt');
expect(response.statusCode, 204);
}

final response = await client.webdav.delete('dst1/sub');
expect(response.statusCode, 204);

expect(
() => client.webdav.move('dst2', 'noncoll'),
throwsA(predicate<DynamiteApiException>((final e) => e.statusCode == 412)),
);
});
});

group('largefile', () {
final largefileSize = pow(10, 9).toInt(); // 1GB

// large_put: Already covered by large_get

test('large_get', () async {
final response = await client.webdav.put(Uint8List(largefileSize), 'test.txt');
expect(response.statusCode, 201);

final downloadedContent = await client.webdav.get('test.txt');
expect(downloadedContent, hasLength(largefileSize));
});
});

// props: Most of them are either not applicable or hard/impossible to implement because we don't allow just writing any props
});
});
}