(string) Str::uuid(), 'name' => 'Test User', 'email' => 'transfer+'.uniqid().'@example.com', ]); } private function createShareTransfer(User $user, string $title, string $filename): Transfer { $this->actingAs($user)->post(route('transfer.transfers.store'), [ 'title' => $title, 'files' => [UploadedFile::fake()->create($filename, 50, 'application/pdf')], ])->assertRedirect(); return Transfer::query()->where('title', $title)->firstOrFail(); } private function createFolder(User $user, string $name): Transfer { $this->actingAs($user)->post(route('transfer.files.folders.store'), [ 'name' => $name, ])->assertRedirect(); return Transfer::query()->where('title', $name)->where('is_folder', true)->firstOrFail(); } public function test_owner_can_download_file(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $transfer = $this->createShareTransfer($user, 'Docs', 'brief.pdf'); $file = $transfer->files()->first(); $this->actingAs($user) ->get(route('transfer.files.download', $file)) ->assertOk(); } public function test_owner_can_delete_file(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $transfer = $this->createShareTransfer($user, 'Docs', 'brief.pdf'); $file = $transfer->files()->first(); $this->actingAs($user) ->delete(route('transfer.files.destroy', $file)) ->assertRedirect(); $this->assertDatabaseMissing('transfer_files', ['id' => $file->id]); } public function test_owner_can_move_file_into_user_folder(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->createShareTransfer($user, 'Loose file', 'one.pdf'); $file = Transfer::query()->where('title', 'Loose file')->first()->files()->first(); $folder = $this->createFolder($user, 'Destination folder'); $this->actingAs($user) ->post(route('transfer.files.move'), [ 'destination' => $folder->id, 'files' => [$file->id], ]) ->assertRedirect(route('transfer.files.index', ['folder' => $folder->id])); $this->assertSame($folder->id, $file->fresh()->transfer_id); } public function test_files_index_shows_user_folder(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $folder = $this->createFolder($user, 'BKC'); $this->actingAs($user) ->get(route('transfer.files.index')) ->assertOk() ->assertSee('BKC'); $this->actingAs($user) ->get(route('transfer.files.index', ['folder' => $folder->id])) ->assertOk() ->assertSee('BKC'); } public function test_transfers_index_excludes_files_page_storage(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $folder = $this->createFolder($user, 'Private folder'); $this->actingAs($user) ->postJson(route('transfer.files.upload'), [ 'upload_folder_name' => 'Uploaded folder', 'files' => [UploadedFile::fake()->create('secret.txt', 10, 'text/plain')], ]) ->assertOk(); $this->actingAs($user) ->post(route('transfer.transfers.store'), [ 'title' => 'Shared deck', 'files' => [UploadedFile::fake()->create('deck.pdf', 50, 'application/pdf')], ]) ->assertRedirect(); $this->actingAs($user) ->get(route('transfer.transfers.index')) ->assertOk() ->assertSee('Shared deck') ->assertDontSee('Private folder') ->assertDontSee('Uploaded folder'); } public function test_files_index_shows_root_storage_and_share_files(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user) ->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('stored.txt', 10, 'text/plain')], ]) ->assertRedirect(); $this->actingAs($user) ->post(route('transfer.transfers.store'), [ 'title' => 'Shared deck', 'files' => [UploadedFile::fake()->create('deck.pdf', 50, 'application/pdf')], ]) ->assertRedirect(); $this->actingAs($user) ->get(route('transfer.files.index')) ->assertOk() ->assertSee('stored.txt') ->assertSee('deck.pdf'); } public function test_upload_detects_duplicate_filenames(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user)->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('brief.pdf', 50, 'application/pdf')], ])->assertRedirect(); $this->actingAs($user) ->postJson(route('transfer.files.upload.check'), [ 'filenames' => ['brief.pdf', 'new.docx'], ]) ->assertOk() ->assertJsonPath('conflicts.0.filename', 'brief.pdf'); } public function test_upload_can_replace_duplicate_file(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user)->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('brief.pdf', 50, 'application/pdf')], ])->assertRedirect(); $this->actingAs($user) ->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('brief.pdf', 80, 'application/pdf')], 'resolutions' => ['brief.pdf' => 'replace'], ]) ->assertRedirect(); $this->assertSame(1, \App\Models\TransferFile::query()->where('original_name', 'brief.pdf')->count()); $this->assertDatabaseHas('transfer_files', ['original_name' => 'brief.pdf']); } public function test_upload_keep_both_renames_duplicate(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user)->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('brief.pdf', 50, 'application/pdf')], ])->assertRedirect(); $this->actingAs($user) ->post(route('transfer.files.upload'), [ 'files' => [UploadedFile::fake()->create('brief.pdf', 80, 'application/pdf')], 'resolutions' => ['brief.pdf' => 'keep_both'], ]) ->assertRedirect(); $this->assertDatabaseHas('transfer_files', ['original_name' => 'brief.pdf']); $this->assertDatabaseHas('transfer_files', ['original_name' => 'brief (1).pdf']); } public function test_upload_stores_basename_when_folder_path_in_filename(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user) ->post(route('transfer.files.upload'), [ 'files' => [ UploadedFile::fake()->createWithContent( 'red-logo/icon.svg', '', ), ], ]) ->assertRedirect(); $this->assertDatabaseHas('transfer_files', ['original_name' => 'icon.svg']); $this->assertDatabaseMissing('transfer_files', ['original_name' => 'red-logo/icon.svg']); } public function test_folder_upload_creates_folder_with_files(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $this->actingAs($user) ->postJson(route('transfer.files.upload'), [ 'upload_folder_name' => 'Brand assets', 'files' => [ UploadedFile::fake()->create('logo.svg', 10, 'image/svg+xml'), UploadedFile::fake()->create('cover.png', 10, 'image/png'), ], ]) ->assertOk() ->assertJsonPath('count', 2) ->assertJsonPath('folder_id', fn ($id) => is_int($id)); $folder = Transfer::query() ->where('user_id', $user->id) ->where('is_folder', true) ->where('title', 'Brand assets') ->first(); $this->assertNotNull($folder); $this->assertSame(2, $folder->files()->count()); $this->assertDatabaseHas('transfer_files', [ 'transfer_id' => $folder->id, 'original_name' => 'logo.svg', ]); $this->assertDatabaseHas('transfer_files', [ 'transfer_id' => $folder->id, 'original_name' => 'cover.png', ]); } public function test_owner_can_download_folder_as_zip(): void { Storage::fake('qr'); $this->fakeTransferBillingApi(); $user = $this->createUser(); $folder = $this->createFolder($user, 'Forum'); $this->actingAs($user) ->postJson(route('transfer.files.upload'), [ 'folder' => $folder->id, 'files' => [ UploadedFile::fake()->create('notes.txt', 10, 'text/plain'), UploadedFile::fake()->create('photo.jpg', 10, 'image/jpeg'), ], ]) ->assertOk(); $this->actingAs($user) ->get(route('transfer.files.folders.download', $folder)) ->assertOk() ->assertHeader('content-type', 'application/zip'); } }