: {active, channels:{code:{live,count}}} } } — та же форма значков * витрины, что в воронке (кусок B), считанная из летописи эпизодов, но матч фирм по * firm_inn. finder-сторона (колонки «Греются/Прогреты») — в самой Python-службе, вне репо. * * SharesSupplierPdo обязателен: фирмы/эпизоды на pgsql_supplier. * План: docs/superpowers/plans/2026-07-23-progrev-vitrina-kusok-B.md Task B6. */ uses(DatabaseTransactions::class, SharesSupplierPdo::class); beforeEach(function () { config(['sales.integration_token' => 'TESTTOKEN']); }); it('без токена → 401', function () { $this->postJson('/api/sales/integration/warming-state', ['inns' => ['7700000001']]) ->assertUnauthorized(); }); it('отдаёт состояние прогрева по ИНН из летописи (live/count)', function () { $firm = SalesAdAudienceFirm::create([ 'firm_name' => 'Гарант', 'firm_inn' => '7700000001', 'warmup_started_at' => now(), ]); // Яндекс — открытый эпизод (идёт сейчас); СМС — два закрытых события (×2). SalesAdAudienceWarmingEpisode::create(['firm_id' => $firm->id, 'channel' => 'yandex', 'started_at' => now()]); foreach ([now()->subDays(3), now()] as $t) { SalesAdAudienceWarmingEpisode::create(['firm_id' => $firm->id, 'channel' => 'sms', 'started_at' => $t, 'ended_at' => $t]); } $w = $this->postJson('/api/sales/integration/warming-state', ['inns' => ['7700000001']], ['X-Sales-Token' => 'TESTTOKEN']) ->assertOk() ->json('data.7700000001'); expect($w['active'])->toBeTrue() ->and($w['channels']['yandex']['live'])->toBeTrue() ->and($w['channels']['yandex']['count'])->toBe(1) ->and($w['channels']['sms']['count'])->toBe(2) ->and($w['channels']['sms']['live'])->toBeFalse(); }); it('неизвестный ИНН → пустой прогрев', function () { $res = $this->postJson('/api/sales/integration/warming-state', ['inns' => ['9999999999']], ['X-Sales-Token' => 'TESTTOKEN']) ->assertOk(); expect($res->json('data.9999999999.active'))->toBeFalse() ->and($res->json('data.9999999999.channels'))->toBe([]); }); it('валидация: inns обязателен непустой массив → 422', function () { $this->postJson('/api/sales/integration/warming-state', [], ['X-Sales-Token' => 'TESTTOKEN']) ->assertStatus(422); });