diff --git a/src/tests/Feature/Console/Sku/ListUsersTest.php b/src/tests/Feature/Console/Sku/ListUsersTest.php index b5aaa1ed..e91726d1 100644 --- a/src/tests/Feature/Console/Sku/ListUsersTest.php +++ b/src/tests/Feature/Console/Sku/ListUsersTest.php @@ -1,87 +1,88 @@ deleteTestUser('sku-list-users@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('sku-list-users@kolabnow.com'); parent::tearDown(); } /** * Test command runs */ public function testHandle(): void { // Warning: We're not using artisan() here, as this will not // allow us to test "empty output" cases $code = \Artisan::call('sku:list-users meet'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $this->assertSame('', $output); $code = \Artisan::call('sku:list-users unknown'); $output = trim(\Artisan::output()); $this->assertSame(1, $code); $this->assertSame("Unable to find the SKU.", $output); $code = \Artisan::call('sku:list-users 2fa'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $this->assertSame("ned@kolab.org", $output); $code = \Artisan::call('sku:list-users mailbox'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $expected = [ + "fred@" . \config('app.domain'), "jack@kolab.org", "joe@kolab.org", "john@kolab.org", "ned@kolab.org", "reseller@" . \config('app.domain') ]; $this->assertSame(implode("\n", $expected), $output); $code = \Artisan::call('sku:list-users domain-hosting'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $this->assertSame("john@kolab.org", $output); $sku = \App\Sku::where('title', 'meet')->first(); $user = $this->getTestUser('sku-list-users@kolabnow.com'); $user->assignSku($sku); $code = \Artisan::call('sku:list-users meet'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $this->assertSame($user->email, $output); $user->assignSku($sku); $code = \Artisan::call('sku:list-users meet'); $output = trim(\Artisan::output()); $this->assertSame(0, $code); $this->assertSame($user->email, $output); } } diff --git a/src/tests/Feature/Controller/Admin/SkusTest.php b/src/tests/Feature/Controller/Admin/SkusTest.php index 4d2c618a..f7678b54 100644 --- a/src/tests/Feature/Controller/Admin/SkusTest.php +++ b/src/tests/Feature/Controller/Admin/SkusTest.php @@ -1,124 +1,124 @@ delete(); $this->clearBetaEntitlements(); $this->clearMeetEntitlements(); } /** * {@inheritDoc} */ public function tearDown(): void { Sku::where('title', 'test')->delete(); $this->clearBetaEntitlements(); $this->clearMeetEntitlements(); parent::tearDown(); } /** * Test fetching SKUs list for a domain (GET /domains//skus) */ public function testDomainSkus(): void { $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); $domain = $this->getTestDOmain('kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(401); // Non-admin access not allowed $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(403); $response = $this->actingAs($admin)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(1, $json); // Note: Details are tested where we test API\V4\SkusController } /** * Test fetching SKUs list */ public function testIndex(): void { $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); $sku = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); // Unauth access not allowed $response = $this->get("api/v4/skus"); $response->assertStatus(401); // User access not allowed on admin API $response = $this->actingAs($user)->get("api/v4/skus"); $response->assertStatus(403); $response = $this->actingAs($admin)->get("api/v4/skus"); $response->assertStatus(200); $json = $response->json(); - $this->assertCount(13, $json); + $this->assertCount(14, $json); $this->assertSame(100, $json[0]['prio']); $this->assertSame($sku->id, $json[0]['id']); $this->assertSame($sku->title, $json[0]['title']); $this->assertSame($sku->name, $json[0]['name']); $this->assertSame($sku->description, $json[0]['description']); $this->assertSame($sku->cost, $json[0]['cost']); $this->assertSame($sku->units_free, $json[0]['units_free']); $this->assertSame($sku->period, $json[0]['period']); $this->assertSame($sku->active, $json[0]['active']); $this->assertSame('user', $json[0]['type']); $this->assertSame('Mailbox', $json[0]['handler']); } /** * Test fetching SKUs list for a user (GET /users//skus) */ public function testUserSkus(): void { $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(401); // Non-admin access not allowed $response = $this->actingAs($user)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(403); $response = $this->actingAs($admin)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(6, $json); // Note: Details are tested where we test API\V4\SkusController } } diff --git a/src/tests/Feature/Controller/FilesTest.php b/src/tests/Feature/Controller/FilesTest.php index bf19fddc..f31a75f0 100644 --- a/src/tests/Feature/Controller/FilesTest.php +++ b/src/tests/Feature/Controller/FilesTest.php @@ -1,714 +1,717 @@ delete(); } /** * {@inheritDoc} */ public function tearDown(): void { Item::query()->delete(); $disk = LaravelStorage::disk('files'); foreach ($disk->listContents('') as $dir) { $disk->deleteDirectory($dir->path()); } parent::tearDown(); } /** * Test deleting files (DELETE /api/v4/files/) */ public function testDelete(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $file = $this->getTestFile($john, 'teśt.txt', 'Teśt content'); // Unauth access $response = $this->delete("api/v4/files/{$file->id}"); $response->assertStatus(401); // Unauth access $response = $this->actingAs($jack)->delete("api/v4/files/{$file->id}"); $response->assertStatus(403); // Non-existing file $response = $this->actingAs($john)->delete("api/v4/files/123"); $response->assertStatus(404); // File owner access $response = $this->actingAs($john)->delete("api/v4/files/{$file->id}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame('success', $json['status']); $this->assertSame("File deleted successfully.", $json['message']); $this->assertSame(null, Item::find($file->id)); // Note: The file is expected to stay still in the filesystem, we're not testing this here. // TODO: Test acting as another user with permissions } /** * Test file downloads (GET /api/v4/files/downloads/) */ public function testDownload(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $file = $this->getTestFile($john, 'teśt.txt', 'Teśt content'); // Unauth access $response = $this->get("api/v4/files/{$file->id}?downloadUrl=1"); $response->assertStatus(401); $response = $this->actingAs($jack)->get("api/v4/files/{$file->id}?downloadUrl=1"); $response->assertStatus(403); // Non-existing file $response = $this->actingAs($john)->get("api/v4/files/123456?downloadUrl=1"); $response->assertStatus(404); // Get downloadLink for the file $response = $this->actingAs($john)->get("api/v4/files/{$file->id}?downloadUrl=1"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($file->id, $json['id']); $link = $json['downloadUrl']; // Fetch the file content $response = $this->get(substr($link, strpos($link, '/api/') + 1)); $response->assertStatus(200) ->assertHeader('Content-Disposition', "attachment; filename=test.txt; filename*=utf-8''te%C5%9Bt.txt") ->assertHeader('Content-Length', $file->getProperty('size')) ->assertHeader('Content-Type', $file->getProperty('mimetype') . '; charset=UTF-8'); $this->assertSame('Teśt content', $response->streamedContent()); // Test acting as another user with read permission $permission = $this->getTestFilePermission($file, $jack, 'r'); $response = $this->actingAs($jack)->get("api/v4/files/{$permission->key}?downloadUrl=1"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($file->id, $json['id']); $link = $json['downloadUrl']; // Fetch the file content $response = $this->get(substr($link, strpos($link, '/api/') + 1)); $response->assertStatus(200) ->assertHeader('Content-Disposition', "attachment; filename=test.txt; filename*=utf-8''te%C5%9Bt.txt") ->assertHeader('Content-Length', $file->getProperty('size')) ->assertHeader('Content-Type', $file->getProperty('mimetype') . '; charset=UTF-8'); $this->assertSame('Teśt content', $response->streamedContent()); // Test downloading a multi-chunk file $file = $this->getTestFile($john, 'test2.txt', ['T1', 'T2']); $response = $this->actingAs($john)->get("api/v4/files/{$file->id}?downloadUrl=1"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($file->id, $json['id']); $link = $json['downloadUrl']; // Fetch the file content $response = $this->get(substr($link, strpos($link, '/api/') + 1)); $response->assertStatus(200) ->assertHeader('Content-Disposition', "attachment; filename=test2.txt") ->assertHeader('Content-Length', $file->getProperty('size')) ->assertHeader('Content-Type', $file->getProperty('mimetype')); $this->assertSame('T1T2', $response->streamedContent()); } /** * Test fetching/creating/updaing/deleting file permissions (GET|POST|PUT /api/v4/files//permissions) */ public function testPermissions(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $file = $this->getTestFile($john, 'test1.txt', []); // 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(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertSame(["The user field is required."], $json['errors']['user']); $this->assertSame(["The permissions field is required."], $json['errors']['permissions']); // Test more input validation $post = ['user' => 'user', 'permissions' => 'read']; $response = $this->actingAs($john)->post("api/v4/files/{$file->id}/permissions", $post); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertSame(["The user must be a valid email address."], $json['errors']['user']); $this->assertSame("The file permission is invalid.", $json['errors']['permissions']); // Let's add some permission $post = ['user' => 'jack@kolab.org', 'permissions' => 'read-only']; $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 created successfully.", $json['message']); $permission = $file->properties()->where('key', 'like', 'share-%')->orderBy('value')->first(); $this->assertSame("{$jack->email}:r", $permission->value); $this->assertSame($permission->key, $json['id']); $this->assertSame($jack->email, $json['user']); $this->assertSame('read-only', $json['permissions']); $this->assertSame(\App\Utils::serviceUrl('file/' . $permission->key), $json['link']); // Error handling on use of the same user $post = ['user' => 'jack@kolab.org', 'permissions' => 'read-only']; $response = $this->actingAs($john)->post("api/v4/files/{$file->id}/permissions", $post); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame("File permission already exists.", $json['errors']['user']); // Test update $response = $this->actingAs($john)->put("api/v4/files/{$file->id}/permissions/1234", $post); $response->assertStatus(404); $post = ['user' => 'jack@kolab.org', 'permissions' => 'read-write']; $response = $this->actingAs($john)->put("api/v4/files/{$file->id}/permissions/{$permission->key}", $post); $response->assertStatus(200); $json = $response->json(); $this->assertSame('success', $json['status']); $this->assertSame("File permissions updated successfully.", $json['message']); $permission->refresh(); $this->assertSame("{$jack->email}:rw", $permission->value); $this->assertSame($permission->key, $json['id']); $this->assertSame($jack->email, $json['user']); $this->assertSame('read-write', $json['permissions']); $this->assertSame(\App\Utils::serviceUrl('file/' . $permission->key), $json['link']); // Input validation on update $post = ['user' => 'jack@kolab.org', 'permissions' => 'read']; $response = $this->actingAs($john)->put("api/v4/files/{$file->id}/permissions/{$permission->key}", $post); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame("The file permission is invalid.", $json['errors']['permissions']); // 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->assertCount(1, $json['list']); $this->assertSame(1, $json['count']); $this->assertSame($permission->key, $json['list'][0]['id']); $this->assertSame($jack->email, $json['list'][0]['user']); $this->assertSame('read-write', $json['list'][0]['permissions']); $this->assertSame(\App\Utils::serviceUrl('file/' . $permission->key), $json['list'][0]['link']); // Delete permission $response = $this->actingAs($john)->delete("api/v4/files/{$file->id}/permissions/1234"); $response->assertStatus(404); $response = $this->actingAs($john)->delete("api/v4/files/{$file->id}/permissions/{$permission->key}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame('success', $json['status']); $this->assertSame("File permissions deleted successfully.", $json['message']); $this->assertCount(0, $file->properties()->where('key', 'like', 'share-%')->get()); } /** * Test fetching files/folders list (GET /api/v4/files) */ 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 $file1 = $this->getTestFile($user, 'test1.txt', [], ['mimetype' => 'text/plain', 'size' => 12345]); $file2 = $this->getTestFile($user, 'test2.gif', [], ['mimetype' => 'image/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('test1.txt', $json['list'][0]['name']); $this->assertSame($file1->id, $json['list'][0]['id']); $this->assertSame('test2.gif', $json['list'][1]['name']); $this->assertSame($file2->id, $json['list'][1]['id']); // Searching $response = $this->actingAs($user)->get("api/v4/files?search=t2"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(3, $json); $this->assertSame(1, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(1, $json['list']); $this->assertSame('test2.gif', $json['list'][0]['name']); $this->assertSame($file2->id, $json['list'][0]['id']); // TODO: Test paging } /** * Test fetching file metadata (GET /api/v4/files/) */ public function testShow(): void { // Unauth access not allowed $response = $this->get("api/v4/files/1234"); $response->assertStatus(401); $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $file = $this->getTestFile($john, '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($john)->get("api/v4/files/{$file->id}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($file->id, $json['id']); $this->assertSame($file->getProperty('mimetype'), $json['mimetype']); $this->assertSame((int) $file->getProperty('size'), $json['size']); $this->assertSame($file->getProperty('name'), $json['name']); $this->assertSame(true, $json['isOwner']); $this->assertSame(true, $json['canUpdate']); $this->assertSame(true, $json['canDelete']); // Get file content $response = $this->actingAs($john)->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->getProperty('size')) ->assertHeader('Content-Type', $file->getProperty('mimetype') . '; charset=UTF-8'); $this->assertSame('Teśt content', $response->streamedContent()); // Test acting as a user with file permissions $permission = $this->getTestFilePermission($file, $jack, 'r'); $response = $this->actingAs($jack)->get("api/v4/files/{$permission->key}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($file->id, $json['id']); $this->assertSame(false, $json['isOwner']); $this->assertSame(false, $json['canUpdate']); $this->assertSame(false, $json['canDelete']); } /** * Test creating files (POST /api/v4/files) */ public function testStore(): void { // Unauth access not allowed $response = $this->post("api/v4/files"); $response->assertStatus(401); $john = $this->getTestUser('john@kolab.org'); // Test input validation $response = $this->sendRawBody($john, 'POST', "api/v4/files", [], ''); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame(["The name field is required."], $json['errors']['name']); $response = $this->sendRawBody($john, 'POST', "api/v4/files?name=*.txt", [], ''); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame(["The file name is invalid."], $json['errors']['name']); // Create a file - the simple method $body = "test content"; $headers = []; $response = $this->sendRawBody($john, '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 = Item::find($json['id']); $this->assertSame(Item::TYPE_FILE, $file->type); $this->assertSame($json['mimetype'], $file->getProperty('mimetype')); $this->assertSame($json['size'], (int) $file->getProperty('size')); $this->assertSame($json['name'], $file->getProperty('name')); $this->assertSame($body, $this->getTestFileContent($file)); } /** * Test creating files - resumable (POST /api/v4/files) */ public function testStoreResumable(): void { $john = $this->getTestUser('john@kolab.org'); $response = $this->actingAs($john)->post("api/v4/files?name=test2.txt&media=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(null, 'POST', "api/v4/files/uploads/{$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(null, 'POST', "api/v4/files/uploads/{$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 = Item::find($json['id']); $this->assertSame(Item::TYPE_FILE, $file->type); $this->assertSame($json['mimetype'], $file->getProperty('mimetype')); $this->assertSame($json['size'], (int) $file->getProperty('size')); $this->assertSame($json['name'], $file->getProperty('name')); $this->assertSame($fileContent, $this->getTestFileContent($file)); } /** * Test updating files (PUT /api/v4/files/) */ public function testUpdate(): void { // Unauth access not allowed $response = $this->put("api/v4/files/1234"); $response->assertStatus(401); $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $file = $this->getTestFile($john, 'teśt.txt', 'Teśt content'); // Non-existing file $response = $this->actingAs($john)->put("api/v4/files/1234", []); $response->assertStatus(404); // Unauthorized access $response = $this->actingAs($jack)->put("api/v4/files/{$file->id}", []); $response->assertStatus(403); // Test name validation $post = ['name' => 'test/test.txt']; $response = $this->actingAs($john)->put("api/v4/files/{$file->id}", $post); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame(["The file name is invalid."], $json['errors']['name']); $post = ['name' => 'new name.txt', 'media' => 'test']; $response = $this->actingAs($john)->put("api/v4/files/{$file->id}", $post); $response->assertStatus(422); $json = $response->json(); $this->assertSame('error', $json['status']); $this->assertSame("The specified media is invalid.", $json['errors']['media']); // Rename a file $post = ['name' => 'new namś.txt']; $response = $this->actingAs($john)->put("api/v4/files/{$file->id}", $post); $response->assertStatus(200); $json = $response->json(); $this->assertSame('success', $json['status']); $this->assertSame("File updated successfully.", $json['message']); $file->refresh(); $this->assertSame($post['name'], $file->getProperty('name')); // Update file content $body = "Test1\nTest2"; $response = $this->sendRawBody($john, 'PUT', "api/v4/files/{$file->id}?media=content", [], $body); $response->assertStatus(200); $json = $response->json(); $this->assertSame('success', $json['status']); $this->assertSame("File updated successfully.", $json['message']); $file->refresh(); $this->assertSame($body, $this->getTestFileContent($file)); $this->assertSame('text/plain', $file->getProperty('mimetype')); $this->assertSame(strlen($body), (int) $file->getProperty('size')); // TODO: Test acting as another user with file permissions // TODO: Test media=resumable } /** * Create a test file. * * @param \App\User $user File owner * @param string $name File name * @param string|array $content File content * @param array $props Extra file properties * * @return \App\Fs\Item */ protected function getTestFile(User $user, string $name, $content = [], $props = []): Item { $disk = LaravelStorage::disk('files'); $file = $user->fsItems()->create(['type' => Item::TYPE_FILE]); $size = 0; $mimetype = ''; if (is_array($content) && empty($content)) { // do nothing, we don't need the body here } else { foreach ((array) $content as $idx => $chunk) { $chunkId = \App\Utils::uuidStr(); $path = Storage::chunkLocation($chunkId, $file); $disk->write($path, $chunk); if (!$size) { $mimetype = $disk->mimeType($path); } $size += strlen($chunk); $file->chunks()->create([ 'chunk_id' => $chunkId, 'sequence' => $idx, 'size' => strlen($chunk), ]); } } $properties = [ 'name' => $name, 'size' => $size, 'mimetype' => $mimetype ?: 'application/octet-stream', ]; $file->setProperties($props + $properties); return $file; } /** * Get contents of a test file. * * @param \App\Fs\Item $file File record * * @return string */ protected function getTestFileContent(Item $file): string { $content = ''; $file->chunks()->orderBy('sequence')->get()->each(function ($chunk) use ($file, &$content) { $disk = LaravelStorage::disk('files'); $path = Storage::chunkLocation($chunk->chunk_id, $file); $content .= $disk->read($path); }); return $content; } /** * Create a test file permission. * * @param \App\Fs\Item $file The file * @param \App\User $user File owner * @param string $permission File permission * * @return \App\Fs\Property File permission property */ protected function getTestFilePermission(Item $file, User $user, string $permission): Property { $shareId = 'share-' . \App\Utils::uuidStr(); return $file->properties()->create([ 'key' => $shareId, 'value' => "{$user->email}:{$permission}", ]); } /** * 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(); if ($user) { return $this->actingAs($user)->call($method, $uri, [], $cookies, [], $server, $content); } else { // TODO: Make sure this does not use "acting user" set earlier return $this->call($method, $uri, [], $cookies, [], $server, $content); } } } diff --git a/src/tests/Feature/Controller/Reseller/SkusTest.php b/src/tests/Feature/Controller/Reseller/SkusTest.php index cb8291ca..97195fec 100644 --- a/src/tests/Feature/Controller/Reseller/SkusTest.php +++ b/src/tests/Feature/Controller/Reseller/SkusTest.php @@ -1,173 +1,173 @@ delete(); $this->clearBetaEntitlements(); $this->clearMeetEntitlements(); } /** * {@inheritDoc} */ public function tearDown(): void { Sku::where('title', 'test')->delete(); $this->clearBetaEntitlements(); $this->clearMeetEntitlements(); parent::tearDown(); } /** * Test fetching SKUs list for a domain (GET /domains//skus) */ public function testDomainSkus(): void { $reseller1 = $this->getTestUser('reseller@' . \config('app.domain')); $reseller2 = $this->getTestUser('reseller@sample-tenant.dev-local'); $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); $domain = $this->getTestDomain('kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(401); // User access not allowed $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(403); // Admin access not allowed $response = $this->actingAs($admin)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(403); // Reseller from another tenant $response = $this->actingAs($reseller2)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(404); // Reseller access $response = $this->actingAs($reseller1)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(1, $json); // Note: Details are tested where we test API\V4\SkusController } /** * Test fetching SKUs list */ public function testIndex(): void { $reseller1 = $this->getTestUser('reseller@' . \config('app.domain')); $reseller2 = $this->getTestUser('reseller@sample-tenant.dev-local'); $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); $sku = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); // Unauth access not allowed $response = $this->get("api/v4/skus"); $response->assertStatus(401); // User access not allowed $response = $this->actingAs($user)->get("api/v4/skus"); $response->assertStatus(403); // Admin access not allowed $response = $this->actingAs($admin)->get("api/v4/skus"); $response->assertStatus(403); $response = $this->actingAs($reseller1)->get("api/v4/skus"); $response->assertStatus(200); $json = $response->json(); - $this->assertCount(13, $json); + $this->assertCount(14, $json); $this->assertSame(100, $json[0]['prio']); $this->assertSame($sku->id, $json[0]['id']); $this->assertSame($sku->title, $json[0]['title']); $this->assertSame($sku->name, $json[0]['name']); $this->assertSame($sku->description, $json[0]['description']); $this->assertSame($sku->cost, $json[0]['cost']); $this->assertSame($sku->units_free, $json[0]['units_free']); $this->assertSame($sku->period, $json[0]['period']); $this->assertSame($sku->active, $json[0]['active']); $this->assertSame('user', $json[0]['type']); $this->assertSame('Mailbox', $json[0]['handler']); // Test with another tenant $sku = Sku::where('title', 'mailbox')->where('tenant_id', $reseller2->tenant_id)->first(); $response = $this->actingAs($reseller2)->get("api/v4/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(6, $json); $this->assertSame(100, $json[0]['prio']); $this->assertSame($sku->id, $json[0]['id']); $this->assertSame($sku->title, $json[0]['title']); $this->assertSame($sku->name, $json[0]['name']); $this->assertSame($sku->description, $json[0]['description']); $this->assertSame($sku->cost, $json[0]['cost']); $this->assertSame($sku->units_free, $json[0]['units_free']); $this->assertSame($sku->period, $json[0]['period']); $this->assertSame($sku->active, $json[0]['active']); $this->assertSame('user', $json[0]['type']); $this->assertSame('Mailbox', $json[0]['handler']); } /** * Test fetching SKUs list for a user (GET /users//skus) */ public function testUserSkus(): void { $reseller1 = $this->getTestUser('reseller@' . \config('app.domain')); $reseller2 = $this->getTestUser('reseller@sample-tenant.dev-local'); $admin = $this->getTestUser('jeroen@jeroen.jeroen'); $user = $this->getTestUser('john@kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(401); // User access not allowed $response = $this->actingAs($user)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(403); // Admin access not allowed $response = $this->actingAs($admin)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(403); // Reseller from another tenant $response = $this->actingAs($reseller2)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(404); // Reseller access $response = $this->actingAs($reseller1)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(6, $json); // Note: Details are tested where we test API\V4\SkusController } } diff --git a/src/tests/Feature/Controller/SkusTest.php b/src/tests/Feature/Controller/SkusTest.php index c4258d13..3ee2ddf1 100644 --- a/src/tests/Feature/Controller/SkusTest.php +++ b/src/tests/Feature/Controller/SkusTest.php @@ -1,282 +1,291 @@ clearBetaEntitlements(); $this->clearMeetEntitlements(); Sku::where('title', 'test')->delete(); } /** * {@inheritDoc} */ public function tearDown(): void { $this->clearBetaEntitlements(); $this->clearMeetEntitlements(); Sku::where('title', 'test')->delete(); parent::tearDown(); } /** * Test fetching SKUs list for a domain (GET /domains//skus) */ public function testDomainSkus(): void { $user = $this->getTestUser('john@kolab.org'); $domain = $this->getTestDomain('kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(401); // Create an sku for another tenant, to make sure it is not included in the result $nsku = Sku::create([ 'title' => 'test', 'name' => 'Test', 'description' => '', 'active' => true, 'cost' => 100, 'handler_class' => 'App\Handlers\Domain', ]); $tenant = Tenant::whereNotIn('id', [\config('app.tenant_id')])->first(); $nsku->tenant_id = $tenant->id; $nsku->save(); $response = $this->actingAs($user)->get("api/v4/domains/{$domain->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(1, $json); $this->assertSkuElement('domain-hosting', $json[0], [ 'prio' => 0, 'type' => 'domain', 'handler' => 'DomainHosting', 'enabled' => false, 'readonly' => false, ]); } /** * Test fetching SKUs list */ public function testIndex(): void { // Unauth access not allowed $response = $this->get("api/v4/skus"); $response->assertStatus(401); $user = $this->getTestUser('john@kolab.org'); $sku = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); // Create an sku for another tenant, to make sure it is not included in the result $nsku = Sku::create([ 'title' => 'test', 'name' => 'Test', 'description' => '', 'active' => true, 'cost' => 100, 'handler_class' => 'App\Handlers\Mailbox', ]); $tenant = Tenant::whereNotIn('id', [\config('app.tenant_id')])->first(); $nsku->tenant_id = $tenant->id; $nsku->save(); $response = $this->actingAs($user)->get("api/v4/skus"); $response->assertStatus(200); $json = $response->json(); - $this->assertCount(13, $json); + $this->assertCount(14, $json); $this->assertSame(100, $json[0]['prio']); $this->assertSame($sku->id, $json[0]['id']); $this->assertSame($sku->title, $json[0]['title']); $this->assertSame($sku->name, $json[0]['name']); $this->assertSame($sku->description, $json[0]['description']); $this->assertSame($sku->cost, $json[0]['cost']); $this->assertSame($sku->units_free, $json[0]['units_free']); $this->assertSame($sku->period, $json[0]['period']); $this->assertSame($sku->active, $json[0]['active']); $this->assertSame('user', $json[0]['type']); $this->assertSame('Mailbox', $json[0]['handler']); } /** * Test fetching SKUs list for a user (GET /users//skus) */ public function testUserSkus(): void { $user = $this->getTestUser('john@kolab.org'); // Unauth access not allowed $response = $this->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(401); // Create an sku for another tenant, to make sure it is not included in the result $nsku = Sku::create([ 'title' => 'test', 'name' => 'Test', 'description' => '', 'active' => true, 'cost' => 100, 'handler_class' => 'Mailbox', ]); $tenant = Tenant::whereNotIn('id', [\config('app.tenant_id')])->first(); $nsku->tenant_id = $tenant->id; $nsku->save(); $response = $this->actingAs($user)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(6, $json); $this->assertSkuElement('mailbox', $json[0], [ 'prio' => 100, 'type' => 'user', 'handler' => 'Mailbox', 'enabled' => true, 'readonly' => true, ]); $this->assertSkuElement('storage', $json[1], [ 'prio' => 90, 'type' => 'user', 'handler' => 'Storage', 'enabled' => true, 'readonly' => true, 'range' => [ 'min' => 5, 'max' => 100, 'unit' => 'GB', ] ]); $this->assertSkuElement('groupware', $json[2], [ 'prio' => 80, 'type' => 'user', 'handler' => 'Groupware', 'enabled' => false, 'readonly' => false, ]); $this->assertSkuElement('activesync', $json[3], [ 'prio' => 70, 'type' => 'user', 'handler' => 'Activesync', 'enabled' => false, 'readonly' => false, 'required' => ['Groupware'], ]); $this->assertSkuElement('2fa', $json[4], [ 'prio' => 60, 'type' => 'user', 'handler' => 'Auth2F', 'enabled' => false, 'readonly' => false, 'forbidden' => ['Activesync'], ]); $this->assertSkuElement('meet', $json[5], [ 'prio' => 50, 'type' => 'user', 'handler' => 'Meet', 'enabled' => false, 'readonly' => false, 'required' => ['Groupware'], ]); // Test inclusion of beta SKUs $sku = Sku::withEnvTenantContext()->where('title', 'beta')->first(); $user->assignSku($sku); $response = $this->actingAs($user)->get("api/v4/users/{$user->id}/skus"); $response->assertStatus(200); $json = $response->json(); - $this->assertCount(10, $json); + $this->assertCount(11, $json); $this->assertSkuElement('beta', $json[6], [ 'prio' => 10, 'type' => 'user', 'handler' => 'Beta', 'enabled' => false, 'readonly' => false, ]); $this->assertSkuElement('beta-distlists', $json[7], [ 'prio' => 10, 'type' => 'user', 'handler' => 'Beta\Distlists', 'enabled' => false, 'readonly' => false, 'required' => ['Beta'], ]); $this->assertSkuElement('beta-resources', $json[8], [ 'prio' => 10, 'type' => 'user', 'handler' => 'Beta\Resources', 'enabled' => false, 'readonly' => false, 'required' => ['Beta'], ]); $this->assertSkuElement('beta-shared-folders', $json[9], [ 'prio' => 10, 'type' => 'user', 'handler' => 'Beta\SharedFolders', 'enabled' => false, 'readonly' => false, 'required' => ['Beta'], ]); + + $this->assertSkuElement('files', $json[10], [ + 'prio' => 10, + 'type' => 'user', + 'handler' => 'Files', + 'enabled' => false, + 'readonly' => false, + 'required' => ['Beta'], + ]); } /** * Assert content of the SKU element in an API response * * @param string $sku_title The SKU title * @param array $result The result to assert * @param array $other Other items the SKU itself does not include */ protected function assertSkuElement($sku_title, $result, $other = []): void { $sku = Sku::withEnvTenantContext()->where('title', $sku_title)->first(); $this->assertSame($sku->id, $result['id']); $this->assertSame($sku->title, $result['title']); $this->assertSame($sku->name, $result['name']); $this->assertSame($sku->description, $result['description']); $this->assertSame($sku->cost, $result['cost']); $this->assertSame($sku->units_free, $result['units_free']); $this->assertSame($sku->period, $result['period']); $this->assertSame($sku->active, $result['active']); foreach ($other as $key => $value) { $this->assertSame($value, $result[$key]); } $this->assertCount(8 + count($other), $result); } } diff --git a/src/tests/Feature/Controller/WalletsTest.php b/src/tests/Feature/Controller/WalletsTest.php index e706d5b8..b72796ef 100644 --- a/src/tests/Feature/Controller/WalletsTest.php +++ b/src/tests/Feature/Controller/WalletsTest.php @@ -1,356 +1,356 @@ deleteTestUser('wallets-controller@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('wallets-controller@kolabnow.com'); parent::tearDown(); } /** * Test for getWalletNotice() method */ public function testGetWalletNotice(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $package = \App\Package::withObjectTenantContext($user)->where('title', 'kolab')->first(); $user->assignPackage($package); $wallet = $user->wallets()->first(); $controller = new WalletsController(); $method = new \ReflectionMethod($controller, 'getWalletNotice'); $method->setAccessible(true); // User/entitlements created today, balance=0 $notice = $method->invoke($controller, $wallet); $this->assertSame('You are in your free trial period.', $notice); $wallet->owner->created_at = Carbon::now()->subDays(15); $wallet->owner->save(); $notice = $method->invoke($controller, $wallet); $this->assertSame('Your free trial is about to end, top up to continue.', $notice); // User/entitlements created today, balance=-10 CHF $wallet->balance = -1000; $notice = $method->invoke($controller, $wallet); $this->assertSame('You are out of credit, top up your balance now.', $notice); // User/entitlements created slightly more than a month ago, balance=9,99 CHF (monthly) $wallet->owner->created_at = Carbon::now()->subMonthsWithoutOverflow(1)->subDays(1); $wallet->owner->save(); // test "1 month" $wallet->balance = 990; $notice = $method->invoke($controller, $wallet); - $this->assertMatchesRegularExpression('/\((3 weeks)\)/', $notice); + $this->assertMatchesRegularExpression('/\((1 month)\)/', $notice); // test "2 months" $wallet->balance = 990 * 2.6; $notice = $method->invoke($controller, $wallet); - $this->assertMatchesRegularExpression('/\(2 months 1 week\)/', $notice); + $this->assertMatchesRegularExpression('/\(2 months 2 weeks\)/', $notice); // Change locale to make sure the text is localized by Carbon \app()->setLocale('de'); // test "almost 2 years" $wallet->balance = 990 * 23.5; $notice = $method->invoke($controller, $wallet); - $this->assertMatchesRegularExpression('/\(1 Jahr 9 Monate\)/', $notice); + $this->assertMatchesRegularExpression('/\(1 Jahr 11 Monate\)/', $notice); // Old entitlements, 100% discount $this->backdateEntitlements($wallet->entitlements, Carbon::now()->subDays(40)); $discount = \App\Discount::withObjectTenantContext($user)->where('discount', 100)->first(); $wallet->discount()->associate($discount); $notice = $method->invoke($controller, $wallet->refresh()); $this->assertSame(null, $notice); } /** * Test fetching pdf receipt */ public function testReceiptDownload(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/receipts/2020-05"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts/2020-05"); $response->assertStatus(403); // Invalid receipt id (current month) $receiptId = date('Y-m'); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(404); // Invalid receipt id $receiptId = '1000-03'; $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(404); // Valid receipt id $year = intval(date('Y')) - 1; $receiptId = "$year-12"; $filename = \config('app.name') . " Receipt for $year-12"; $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts/{$receiptId}"); $response->assertStatus(200); $response->assertHeader('content-type', 'application/pdf'); $response->assertHeader('content-disposition', 'attachment; filename="' . $filename . '"'); $response->assertHeader('content-length'); $length = $response->headers->get('content-length'); $content = $response->content(); $this->assertStringStartsWith("%PDF-1.", $content); $this->assertEquals(strlen($content), $length); } /** * Test fetching list of receipts */ public function testReceipts(): void { $user = $this->getTestUser('wallets-controller@kolabnow.com'); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); $wallet->payments()->delete(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(403); // Empty list expected $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); // Insert a payment to the database $date = Carbon::create(intval(date('Y')) - 1, 4, 30); $payment = Payment::create([ 'id' => 'AAA1', 'status' => PaymentProvider::STATUS_PAID, 'type' => PaymentProvider::TYPE_ONEOFF, 'description' => 'Paid in April', 'wallet_id' => $wallet->id, 'provider' => 'stripe', 'amount' => 1111, 'currency' => 'CHF', 'currency_amount' => 1111, ]); $payment->updated_at = $date; $payment->save(); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/receipts"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([$date->format('Y-m')], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(1, $json['count']); $this->assertSame(false, $json['hasMore']); } /** * Test fetching a wallet (GET /api/v4/wallets/:id) */ public function testShow(): void { $john = $this->getTestUser('john@kolab.org'); $jack = $this->getTestUser('jack@kolab.org'); $wallet = $john->wallets()->first(); $wallet->balance = -100; $wallet->save(); // Accessing a wallet of someone else $response = $this->actingAs($jack)->get("api/v4/wallets/{$wallet->id}"); $response->assertStatus(403); // Accessing non-existing wallet $response = $this->actingAs($jack)->get("api/v4/wallets/aaa"); $response->assertStatus(404); // Wallet owner $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}"); $response->assertStatus(200); $json = $response->json(); $this->assertSame($wallet->id, $json['id']); $this->assertSame('CHF', $json['currency']); $this->assertSame($wallet->balance, $json['balance']); $this->assertTrue(empty($json['description'])); $this->assertTrue(!empty($json['notice'])); } /** * Test fetching wallet transactions */ public function testTransactions(): void { $package_kolab = \App\Package::where('title', 'kolab')->first(); $user = $this->getTestUser('wallets-controller@kolabnow.com'); $user->assignPackage($package_kolab); $john = $this->getTestUser('john@kolab.org'); $wallet = $user->wallets()->first(); // Unauth access not allowed $response = $this->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(401); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(403); // Expect empty list $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame([], $json['list']); $this->assertSame(1, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); // Create some sample transactions $transactions = $this->createTestTransactions($wallet); $transactions = array_reverse($transactions); $pages = array_chunk($transactions, 10 /* page size*/); // Get the first page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(1, $json['page']); $this->assertSame(10, $json['count']); $this->assertSame(true, $json['hasMore']); $this->assertCount(10, $json['list']); foreach ($pages[0] as $idx => $transaction) { $this->assertSame($transaction->id, $json['list'][$idx]['id']); $this->assertSame($transaction->type, $json['list'][$idx]['type']); $this->assertSame(\config('app.currency'), $json['list'][$idx]['currency']); $this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']); $this->assertFalse($json['list'][$idx]['hasDetails']); $this->assertFalse(array_key_exists('user', $json['list'][$idx])); } $search = null; // Get the second page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=2"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(2, $json['page']); $this->assertSame(2, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(2, $json['list']); foreach ($pages[1] as $idx => $transaction) { $this->assertSame($transaction->id, $json['list'][$idx]['id']); $this->assertSame($transaction->type, $json['list'][$idx]['type']); $this->assertSame($transaction->shortDescription(), $json['list'][$idx]['description']); $this->assertSame( $transaction->type == Transaction::WALLET_DEBIT, $json['list'][$idx]['hasDetails'] ); $this->assertFalse(array_key_exists('user', $json['list'][$idx])); if ($transaction->type == Transaction::WALLET_DEBIT) { $search = $transaction->id; } } // Get a non-existing page $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?page=3"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(3, $json['page']); $this->assertSame(0, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(0, $json['list']); // Sub-transaction searching $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction=123"); $response->assertStatus(404); $response = $this->actingAs($user)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}"); $response->assertStatus(200); $json = $response->json(); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame(1, $json['page']); $this->assertSame(2, $json['count']); $this->assertSame(false, $json['hasMore']); $this->assertCount(2, $json['list']); $this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][0]['type']); $this->assertSame(Transaction::ENTITLEMENT_BILLED, $json['list'][1]['type']); // Test that John gets 404 if he tries to access // someone else's transaction ID on his wallet's endpoint $wallet = $john->wallets()->first(); $response = $this->actingAs($john)->get("api/v4/wallets/{$wallet->id}/transactions?transaction={$search}"); $response->assertStatus(404); } } diff --git a/src/tests/Feature/SkuTest.php b/src/tests/Feature/SkuTest.php index e9fad72c..05a85a2c 100644 --- a/src/tests/Feature/SkuTest.php +++ b/src/tests/Feature/SkuTest.php @@ -1,109 +1,109 @@ deleteTestUser('jane@kolabnow.com'); } /** * {@inheritDoc} */ public function tearDown(): void { $this->deleteTestUser('jane@kolabnow.com'); parent::tearDown(); } public function testPackageEntitlements(): void { $user = $this->getTestUser('jane@kolabnow.com'); $wallet = $user->wallets()->first(); $package = Package::withEnvTenantContext()->where('title', 'lite')->first(); $user = $user->assignPackage($package); $this->backdateEntitlements($user->fresh()->entitlements, Carbon::now()->subMonthsWithoutOverflow(1)); $wallet->chargeEntitlements(); $this->assertTrue($wallet->balance < 0); } public function testSkuEntitlements(): void { - $this->assertCount(5, Sku::withEnvTenantContext()->where('title', 'mailbox')->first()->entitlements); + $this->assertCount(6, Sku::withEnvTenantContext()->where('title', 'mailbox')->first()->entitlements); } public function testSkuPackages(): void { $this->assertCount(2, Sku::withEnvTenantContext()->where('title', 'mailbox')->first()->packages); } public function testSkuHandlerDomainHosting(): void { $sku = Sku::withEnvTenantContext()->where('title', 'domain-hosting')->first(); $entitlement = $sku->entitlements->first(); $this->assertSame( Handlers\DomainHosting::entitleableClass(), $entitlement->entitleable_type ); } public function testSkuHandlerMailbox(): void { $sku = Sku::withEnvTenantContext()->where('title', 'mailbox')->first(); $entitlement = $sku->entitlements->first(); $this->assertSame( Handlers\Mailbox::entitleableClass(), $entitlement->entitleable_type ); } public function testSkuHandlerStorage(): void { $sku = Sku::withEnvTenantContext()->where('title', 'storage')->first(); $entitlement = $sku->entitlements->first(); $this->assertSame( Handlers\Storage::entitleableClass(), $entitlement->entitleable_type ); } public function testSkuTenant(): void { $sku = Sku::withEnvTenantContext()->where('title', 'storage')->first(); $tenant = $sku->tenant()->first(); $this->assertInstanceof(\App\Tenant::class, $tenant); $tenant = $sku->tenant; $this->assertInstanceof(\App\Tenant::class, $tenant); } }