$pendingShares */ function putChunk(mixed $test, ShareFile $file, int $index, string $chunk, array $pendingShares): mixed { return $test->withSession(['pending_shares' => $pendingShares])->call( 'PUT', route('upload.chunk', ['shareFile' => $file, 'index' => $index]), server: ['CONTENT_TYPE' => 'application/octet-stream', 'HTTP_ACCEPT' => 'application/json'], content: $chunk, ); } test('a chunk for a pending share this session started is stored', function () { Storage::fake('shares'); config(['uploads.chunk_size' => 4]); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 6, null); $response = putChunk($this, $file, 0, encryptedChunk($file, 'abcd', 0, false), [$file->share->token]); $response->assertOk()->assertExactJson(['uploaded_chunks' => 1]); expect($file->refresh()->uploaded_chunks)->toBe(1); }); test('a chunk for a pending share another session started returns 404', function () { Storage::fake('shares'); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 4, null); $response = putChunk($this, $file, 0, encryptedChunk($file, 'abcd', 0, true), ['someOtherToken12']); $response->assertNotFound(); expect($file->refresh()->uploaded_chunks)->toBe(0); }); test('a chunk for a share that is already complete returns 404', function () { Storage::fake('shares'); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 4, null); $file->share->update(['completed_at' => now()]); $response = putChunk($this, $file, 0, encryptedChunk($file, 'abcd', 0, true), [$file->share->token]); $response->assertNotFound(); }); test('a chunk for a removed file returns 404', function () { Storage::fake('shares'); $service = app(ShareService::class); $file = $service->registerFile(null, 'notes.txt', 4, null); $chunk = encryptedChunk($file, 'abcd', 0, true); $token = $file->share->token; $service->removeFile($file); $response = putChunk($this, $file, 0, $chunk, [$token]); $response->assertNotFound(); }); test('a chunk that skips ahead returns 409 with the number of chunks stored', function () { Storage::fake('shares'); config(['uploads.chunk_size' => 4]); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 6, null); $response = putChunk($this, $file, 1, encryptedChunk($file, 'ef', 1, true), [$file->share->token]); $response->assertConflict()->assertExactJson(['uploaded_chunks' => 0]); expect($file->refresh()->uploaded_chunks)->toBe(0); }); test('a chunk sent again after it was stored is acknowledged without storing it twice', function () { Storage::fake('shares'); config(['uploads.chunk_size' => 4]); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 6, null); $chunk = encryptedChunk($file, 'abcd', 0, false); putChunk($this, $file, 0, $chunk, [$file->share->token]); $response = putChunk($this, $file->refresh(), 0, $chunk, [$file->share->token]); $response->assertOk()->assertExactJson(['uploaded_chunks' => 1]); expect($file->refresh()->uploaded_chunks)->toBe(1); }); test('an invalid chunk returns 422 and is not stored', function () { Storage::fake('shares'); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 4, null); $response = putChunk($this, $file, 0, str_repeat("\0", 20), [$file->share->token]); $response->assertUnprocessable(); expect($file->refresh()->uploaded_chunks)->toBe(0); }); test('a chunk is refused until the system password was entered', function () { Storage::fake('shares'); Setting::set('system_password', bcrypt('system-secret')); $file = app(ShareService::class)->registerFile(null, 'notes.txt', 4, null); $response = putChunk($this, $file, 0, encryptedChunk($file, 'abcd', 0, true), [$file->share->token]); $response->assertRedirect(route('system-password')); expect($file->refresh()->uploaded_chunks)->toBe(0); });