tenant = Tenant::factory()->create(); // ДаДата включена (ключ задан) — иначе обогащение не стартует. config()->set('services.dadata.api_key', 'test-key'); config()->set('services.dadata.secret', 'test-secret'); Cache::flush(); // чистый дневной бюджет }); it('проставляет оператора контактам без оператора', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'МТС']], 200)]); $c = ClientSmsContact::create(['tenant_id' => $this->tenant->id, 'phone' => '79990000001']); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); expect($c->fresh()->operator)->toBe('МТС'); }); it('не трогает контакты, про которых уже всё известно (экономим ДаДату)', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'Билайн']], 200)]); // «Всё известно» = И оператор, И часовой пояс. С Этапа 3 одного оператора мало: // без пояса номеру СМС не уходит вовсе (В-85), и такой контакт мы обязаны догнать. $c = ClientSmsContact::create([ 'tenant_id' => $this->tenant->id, 'phone' => '79990000002', 'operator' => 'mts', 'tz_offset_minutes' => 180, ]); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); expect($c->fresh()->operator)->toBe('mts'); // не перезаписан Http::assertNothingSent(); // ДаДату не дёргали }); it('3.3 — сохраняет часовой пояс из того же ответа, за который уже заплатили', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'МТС', 'timezone' => 'UTC+9']], 200)]); $c = ClientSmsContact::create(['tenant_id' => $this->tenant->id, 'phone' => '79990000005']); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); // UTC+9 — это Якутск: девять часов от Гринвича, то есть 540 минут. expect($c->fresh()->tz_offset_minutes)->toBe(540) ->and($c->fresh()->operator)->toBe('МТС'); // Второго обращения к ДаДате быть не должно: пояс приезжает тем же ответом. Http::assertSentCount(1); }); it('3.3 — непонятный пояс не мешает сохранить оператора', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'МТС', 'timezone' => 'ерунда']], 200)]); $c = ClientSmsContact::create(['tenant_id' => $this->tenant->id, 'phone' => '79990000006']); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); // Ради оператора джоб и ходит — непонятный пояс не должен ронять главное. expect($c->fresh()->operator)->toBe('МТС') ->and($c->fresh()->tz_offset_minutes)->toBeNull(); }); it('3.3 — догоняет уже загруженный номер: оператор есть, пояса нет', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'МТС', 'timezone' => 'UTC+2']], 200)]); // Номера, загруженные ДО Этапа 3: оператора им проставили, а пояса тогда не было // вовсе. Без догона такой номер после выката не получил бы СМС никогда (В-88). $c = ClientSmsContact::create([ 'tenant_id' => $this->tenant->id, 'phone' => '79990000007', 'operator' => 'МТС', ]); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); expect($c->fresh()->tz_offset_minutes)->toBe(120) ->and($c->fresh()->operator)->toBe('МТС'); }); it('сбой ДаДаты не роняет обогащение — оператор остаётся пустым', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response('boom', 500)]); $c = ClientSmsContact::create(['tenant_id' => $this->tenant->id, 'phone' => '79990000003']); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); expect($c->fresh()->operator)->toBeNull(); }); it('при исчерпанном дневном бюджете ДаДату не дёргает', function () { Http::fake(['cleaner.dadata.ru/*' => Http::response([['qc' => 0, 'provider' => 'МТС']], 200)]); // выбираем весь дневной лимит config()->set('services.dadata.daily_cap_rub', 1); app(DaDataBudgetGuard::class)->recordSpend(200); // 2 ₽ > 1 ₽ $c = ClientSmsContact::create(['tenant_id' => $this->tenant->id, 'phone' => '79990000004']); (new EnrichClientSmsContactsOperatorJob($this->tenant->id))->handle( app(DaDataPhoneClient::class), app(DaDataBudgetGuard::class), ); expect($c->fresh()->operator)->toBeNull(); Http::assertNothingSent(); });