tenant = Tenant::factory()->create(); $this->user = User::factory()->create(['tenant_id' => $this->tenant->id]); $this->actingAs($this->user); }); /** @param list $colA */ function uploadedXlsx(array $colA): UploadedFile { $ss = new Spreadsheet; $sheet = $ss->getActiveSheet(); foreach (array_values($colA) as $i => $v) { $sheet->setCellValueExplicit('A'.($i + 1), $v, DataType::TYPE_STRING); } $path = tempnam(sys_get_temp_dir(), 'baza').'.xlsx'; (new XlsxWriter($ss))->save($path); return new UploadedFile($path, 'baza.xlsx', null, null, true); } it('загружает базу из Excel-файла: добавляет номера, сообщает отброшенные', function () { Bus::fake(); $file = uploadedXlsx(['Телефон', '79990000001', '+7 999 000-00-02', 'мусор']); $res = $this->post('/api/sms/contacts/file', ['file' => $file], ['Accept' => 'application/json']); $res->assertOk() ->assertJsonPath('added', 2) // 79990000001 + 79990000002 ->assertJsonPath('rejected', 1); // «мусор» expect($res->json('rejected_samples'))->toContain('мусор'); expect(ClientSmsContact::where('tenant_id', $this->tenant->id)->count())->toBe(2); Bus::assertDispatched(EnrichClientSmsContactsOperatorJob::class); }); it('отдаёт пример файла для скачивания (Excel)', function () { $res = $this->get('/api/sms/contacts/example'); $res->assertOk(); expect($res->headers->get('content-type'))->toContain('spreadsheetml'); expect(substr((string) $res->getContent(), 0, 2))->toBe('PK'); }); it('файл не Excel отклоняется валидацией', function () { $bad = UploadedFile::fake()->create('spisok.txt', 1, 'text/plain'); $this->post('/api/sms/contacts/file', ['file' => $bad], ['Accept' => 'application/json']) ->assertStatus(422) ->assertJsonValidationErrors('file'); });