Changeset View
Changeset View
Standalone View
Standalone View
src/tests/Feature/Controller/FilesTest.php
- This file was added.
<?php | |||||
namespace Tests\Feature\Controller; | |||||
use App\File; | |||||
use App\FilePermission; | |||||
use App\Library; | |||||
use App\User; | |||||
use Illuminate\Support\Facades\Storage; | |||||
use Tests\TestCase; | |||||
class FilesTest extends TestCase | |||||
{ | |||||
/** | |||||
* {@inheritDoc} | |||||
*/ | |||||
public function setUp(): void | |||||
{ | |||||
parent::setUp(); | |||||
File::query()->delete(); | |||||
Library::query()->delete(); | |||||
} | |||||
/** | |||||
* {@inheritDoc} | |||||
*/ | |||||
public function tearDown(): void | |||||
{ | |||||
File::query()->delete(); | |||||
Library::query()->delete(); | |||||
$disk = Storage::disk('files'); | |||||
foreach ($disk->listContents('') as $dir) { | |||||
$disk->deleteDirectory($dir->path()); | |||||
} | |||||
parent::tearDown(); | |||||
} | |||||
/** | |||||
* Test deleting files (DELETE /api/v4/files/<file-id>) | |||||
*/ | |||||
public function testDelete(): void | |||||
{ | |||||
$this->markTestIncomplete(); | |||||
} | |||||
/** | |||||
* Test fetching/creating/updaing file permissions (GET|POST /api/v4/files/<file-id>/permissions) | |||||
*/ | |||||
public function testPermissions(): void | |||||
{ | |||||
$john = $this->getTestUser('john@kolab.org'); | |||||
$jack = $this->getTestUser('jack@kolab.org'); | |||||
$library = $john->libraries()->create(['name' => null]); | |||||
$file = $library->files()->create([ | |||||
'mimetype' => 'text/plain', | |||||
'name' => 'test1.txt', | |||||
'size' => 12345, | |||||
]); | |||||
// Unauth access not allowed | |||||
$response = $this->get("api/v4/files/{$file->id}/permissions"); | |||||
$response->assertStatus(401); | |||||
$response = $this->post("api/v4/files/{$file->id}/permissions", []); | |||||
$response->assertStatus(401); | |||||
// Non-existing file | |||||
$response = $this->actingAs($john)->get("api/v4/files/1234/permissions"); | |||||
$response->assertStatus(404); | |||||
$response = $this->actingAs($john)->post("api/v4/files/1234/permissions", []); | |||||
$response->assertStatus(404); | |||||
// No permissions to the file | |||||
$response = $this->actingAs($jack)->get("api/v4/files/{$file->id}/permissions"); | |||||
$response->assertStatus(403); | |||||
$response = $this->actingAs($jack)->post("api/v4/files/{$file->id}/permissions", []); | |||||
$response->assertStatus(403); | |||||
// Expect an empty list of permissions | |||||
$response = $this->actingAs($john)->get("api/v4/files/{$file->id}/permissions"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertCount(2, $json); | |||||
$this->assertSame([], $json['list']); | |||||
$this->assertSame(0, $json['count']); | |||||
// Empty input | |||||
$response = $this->actingAs($john)->post("api/v4/files/{$file->id}/permissions", []); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame('success', $json['status']); | |||||
$this->assertSame("File permissions updated successfully.", $json['message']); | |||||
// TODO: Test input validation | |||||
// Let's set some permissions | |||||
$post = [ | |||||
'permissions' => [ | |||||
['user' => 'test@gmail.com', 'permissions' => 'read-only'], | |||||
['user' => 'jack@kolab.org', 'permissions' => 'read-write'], | |||||
], | |||||
]; | |||||
$response = $this->actingAs($john)->post("api/v4/files/{$file->id}/permissions", $post); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame('success', $json['status']); | |||||
$this->assertSame("File permissions updated successfully.", $json['message']); | |||||
$permissions = $file->permissions()->orderBy('user')->get(); | |||||
$this->assertCount(2, $permissions); | |||||
$this->assertSame(FilePermission::WRITE | FilePermission::READ, $permissions[0]->permissions); | |||||
$this->assertSame($jack->email, $permissions[0]->user); | |||||
$this->assertSame(FilePermission::READ, $permissions[1]->permissions); | |||||
$this->assertSame('test@gmail.com', $permissions[1]->user); | |||||
// Test update/delete entries | |||||
$post = [ | |||||
'permissions' => [ | |||||
['user' => 'jack@kolab.org', 'permissions' => 'read-only'], | |||||
['user' => 'test1@gmail.com', 'permissions' => 'read-write'], | |||||
['user' => 'test2@kolab.org', 'permissions' => 'full'], | |||||
], | |||||
]; | |||||
$response = $this->actingAs($john)->post("api/v4/files/{$file->id}/permissions", $post); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame('success', $json['status']); | |||||
$this->assertSame("File permissions updated successfully.", $json['message']); | |||||
$permissions = $file->permissions()->orderBy('user')->get(); | |||||
$this->assertCount(3, $permissions); | |||||
$this->assertSame(FilePermission::READ, $permissions[0]->permissions); | |||||
$this->assertSame($jack->email, $permissions[0]->user); | |||||
$this->assertSame(FilePermission::WRITE | FilePermission::READ, $permissions[1]->permissions); | |||||
$this->assertSame('test1@gmail.com', $permissions[1]->user); | |||||
$this->assertSame( | |||||
FilePermission::WRITE | FilePermission::READ | FilePermission::DELETE, | |||||
$permissions[2]->permissions | |||||
); | |||||
$this->assertSame('test2@kolab.org', $permissions[2]->user); | |||||
// Test GET with existing permissions | |||||
$response = $this->actingAs($john)->get("api/v4/files/{$file->id}/permissions"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertCount(2, $json); | |||||
$this->assertSame($post['permissions'], $json['list']); | |||||
$this->assertSame(3, $json['count']); | |||||
} | |||||
/** | |||||
* Test fetching files/folders list | |||||
*/ | |||||
public function testIndex(): void | |||||
{ | |||||
// Unauth access not allowed | |||||
$response = $this->get("api/v4/files"); | |||||
$response->assertStatus(401); | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
// Expect an empty list | |||||
$response = $this->actingAs($user)->get("api/v4/files"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertCount(3, $json); | |||||
$this->assertSame([], $json['list']); | |||||
$this->assertSame(0, $json['count']); | |||||
$this->assertSame(false, $json['hasMore']); | |||||
// Create some files and test again | |||||
$library = $user->libraries()->create(['name' => null]); | |||||
$file1 = $library->files()->create([ | |||||
'mimetype' => 'text/plain', | |||||
'name' => 'test1.txt', | |||||
'size' => 12345, | |||||
]); | |||||
$file2 = $library->files()->create([ | |||||
'mimetype' => 'image/gif', | |||||
'name' => 'test3.gif', | |||||
'size' => 10000, | |||||
]); | |||||
$response = $this->actingAs($user)->get("api/v4/files"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertCount(3, $json); | |||||
$this->assertSame(2, $json['count']); | |||||
$this->assertSame(false, $json['hasMore']); | |||||
$this->assertCount(2, $json['list']); | |||||
$this->assertSame($file1->name, $json['list'][0]['name']); | |||||
$this->assertSame($file1->id, $json['list'][0]['id']); | |||||
$this->assertSame($file1->mimetype, $json['list'][0]['mimetype']); | |||||
$this->assertSame($file1->size, $json['list'][0]['size']); | |||||
$this->assertSame($file2->name, $json['list'][1]['name']); | |||||
$this->assertSame($file2->id, $json['list'][1]['id']); | |||||
$this->assertSame($file2->mimetype, $json['list'][1]['mimetype']); | |||||
$this->assertSame($file2->size, $json['list'][1]['size']); | |||||
// TODO: Test paging | |||||
} | |||||
/** | |||||
* Test fetching file metadata/content (GET /api/v4/files/<file-id>) | |||||
*/ | |||||
public function testShow(): void | |||||
{ | |||||
// Unauth access not allowed | |||||
$response = $this->get("api/v4/files/1234"); | |||||
$response->assertStatus(401); | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
$jack = $this->getTestUser('jack@kolab.org'); | |||||
$file = $this->getTestFile($user, 'teśt.txt', 'Teśt content'); | |||||
// Non-existing file | |||||
$response = $this->actingAs($jack)->get("api/v4/files/1234"); | |||||
$response->assertStatus(404); | |||||
// Unauthorized access | |||||
$response = $this->actingAs($jack)->get("api/v4/files/{$file->id}"); | |||||
$response->assertStatus(403); | |||||
// Get file metadata | |||||
$response = $this->actingAs($user)->get("api/v4/files/{$file->id}"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame($file->id, $json['id']); | |||||
$this->assertSame($file->library_id, $json['library_id']); | |||||
$this->assertSame($file->mimetype, $json['mimetype']); | |||||
$this->assertSame($file->size, $json['size']); | |||||
// Get file content | |||||
$response = $this->actingAs($user)->get("api/v4/files/{$file->id}?download=1"); | |||||
$response->assertStatus(200) | |||||
->assertHeader('Content-Disposition', "attachment; filename=test.txt; filename*=utf-8''te%C5%9Bt.txt") | |||||
->assertHeader('Content-Length', $file->size) | |||||
->assertHeader('Content-Type', $file->mimetype . '; charset=UTF-8'); | |||||
$this->assertSame('Teśt content', $response->streamedContent()); | |||||
// TODO: Test acting as a user with file permissions | |||||
} | |||||
/** | |||||
* Test creating files (POST /api/v4/files) | |||||
*/ | |||||
public function testStore(): void | |||||
{ | |||||
// Unauth access not allowed | |||||
$response = $this->post("api/v4/files"); | |||||
$response->assertStatus(401); | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
// TODO: Test name validation | |||||
/* | |||||
$response = $this->sendRawBody($user, 'POST', "api/v4/files", [], ''); | |||||
$response = $this->actingAs($user)->post("api/v4/files", []); | |||||
$response->assertStatus(422); | |||||
$json = $response->json(); | |||||
// $this->assertCount(3, $json); | |||||
*/ | |||||
// Create a file - the simple method | |||||
$body = "test content"; | |||||
$headers = []; | |||||
$response = $this->sendRawBody($user, 'POST', "api/v4/files?name=test.txt", $headers, $body); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame('success', $json['status']); | |||||
$this->assertSame("File created successfully.", $json['message']); | |||||
$this->assertSame('text/plain', $json['mimetype']); | |||||
$this->assertSame(strlen($body), $json['size']); | |||||
$this->assertSame('test.txt', $json['name']); | |||||
$file = File::find($json['id']); | |||||
$this->assertSame($file->mimetype, $json['mimetype']); | |||||
$this->assertSame($file->library_id, $json['library_id']); | |||||
$this->assertSame($json['size'], $file->size); | |||||
$this->assertSame('test.txt', $file->name); | |||||
$disk = Storage::disk('files'); | |||||
$path = $file->path . '/' . $file->id; | |||||
$this->assertSame($body, $disk->read($path)); | |||||
// TODO: Test acting as another user with/without file permissions | |||||
} | |||||
/** | |||||
* Test creating files - resumable (POST /api/v4/files) | |||||
*/ | |||||
public function testStoreResumable(): void | |||||
{ | |||||
$user = $this->getTestUser('john@kolab.org'); | |||||
$response = $this->actingAs($user)->post("api/v4/files?name=test2.txt&upload=resumable&size=400"); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$this->assertSame(0, $json['uploaded']); | |||||
$this->assertMatchesRegularExpression( | |||||
'/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/', | |||||
$json['uploadId'] | |||||
); | |||||
$uploadId = $json['uploadId']; | |||||
$size = 0; | |||||
$fileContent = ''; | |||||
for ($x = 0; $x <= 2; $x++) { | |||||
$body = str_repeat("$x", 100); | |||||
$response = $this->sendRawBody($user, 'POST', "api/v4/files?upload={$uploadId}&from={$size}", [], $body); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$size += 100; | |||||
$fileContent .= $body; | |||||
$this->assertSame($size, $json['uploaded']); | |||||
$this->assertSame($uploadId, $json['uploadId']); | |||||
} | |||||
$body = str_repeat("$x", 100); | |||||
$response = $this->sendRawBody($user, 'POST', "api/v4/files?upload={$uploadId}&from={$size}", [], $body); | |||||
$response->assertStatus(200); | |||||
$json = $response->json(); | |||||
$size += 100; | |||||
$fileContent .= $body; | |||||
$this->assertSame('success', $json['status']); | |||||
// $this->assertSame("", $json['message']); | |||||
$this->assertSame('text/plain', $json['mimetype']); | |||||
$this->assertSame($size, $json['size']); | |||||
$this->assertSame('test2.txt', $json['name']); | |||||
$file = File::find($json['id']); | |||||
$this->assertSame($json['mimetype'], $file->mimetype); | |||||
$this->assertSame($json['library_id'], $file->library_id); | |||||
$this->assertSame($size, $file->size); | |||||
$this->assertSame('test2.txt', $file->name); | |||||
$disk = Storage::disk('files'); | |||||
$path = $file->path . '/' . $file->id; | |||||
$this->assertSame($fileContent, $disk->read($path)); | |||||
} | |||||
/** | |||||
* Test updating files (PUT /api/v4/files/<file-id>) | |||||
*/ | |||||
public function testUpdate(): void | |||||
{ | |||||
$this->markTestIncomplete(); | |||||
} | |||||
/** | |||||
* Create a test file. | |||||
* | |||||
* @param \App\User $user File owner | |||||
* @param string $name File name | |||||
* @param string $content File content | |||||
*/ | |||||
protected function getTestFile(User $user, string $name, string $content = '') | |||||
{ | |||||
$library = $user->libraries()->first(); | |||||
if (!$library) { | |||||
$library = $user->libraries()->create(['name' => null]); | |||||
} | |||||
$disk = Storage::disk('files'); | |||||
$file = $library->files()->create([ | |||||
'mimetype' => File::TYPE_INCOMPLETE, | |||||
'name' => $name, | |||||
]); | |||||
$path = $file->path . '/' . $file->id; | |||||
$disk->write($path, $content); | |||||
$file->size = $disk->fileSize($path); | |||||
$file->mimetype = $disk->mimeType($path) ?: 'application/octet-stream'; | |||||
$file->save(); | |||||
return $file; | |||||
} | |||||
/** | |||||
* Invoke a HTTP request with a custom raw body | |||||
* | |||||
* @param \App\User $user Authenticated user | |||||
* @param string $method Request method (POST, PUT) | |||||
* @param string $uri Request URL | |||||
* @param array $headers Request headers | |||||
* @param string $content Raw body content | |||||
* | |||||
* @return \Illuminate\Testing\TestResponse HTTP Response object | |||||
*/ | |||||
protected function sendRawBody(User $user, string $method, string $uri, array $headers, string $content) | |||||
{ | |||||
$headers['Content-Length'] = strlen($content); | |||||
$server = $this->transformHeadersToServerVars($headers); | |||||
$cookies = $this->prepareCookiesForRequest(); | |||||
return $this->actingAs($user)->call($method, $uri, [], $cookies, [], $server, $content); | |||||
} | |||||
} |