Files
portal/app/tests/Feature/ClientTg/AdminTgApiTest.php
T
Дмитрий 6f469fb13d feat(телеграм-реклама): деньги на общем балансе + чистка СМС-фантазий + медиа объявления
Денежная модель (куски 1-2): реклама в Телеграме (МТС Маркетолог, робот-в-браузере)
оплачивается с ОБЩЕГО баланса тенанта (как СМС), наценка 40%. Списание при запуске,
возврат при отказе модерации/сбое/отмене (сальдо-идемпотентность tg_ad_charge/tg_ad_refund).
Старый рекламный кошелёк-заморозка убран из потока кампаний (таблицы ad_wallets НЕ удалены).
Новый TelegramCampaignChargeService (зеркало SmsChargeService); F5 money-safety сохранён
(сбой с mts_campaign_id → needs_review без возврата). Уборщик добивает зависшие queued с возвратом.

Чистка СМС-наследия (аудит модуля построчно 5 проверяющими): модуль ресейлит ПОКАЗЫ рекламы,
а не рассылку сообщений — вырезаны выдуманные сущности, механически скопированные из СМС:
- «имя/бренд отправителя» + помесячная абонплата (в телеге МТС имени отправителя нет):
  ChargeTgNameFeeJob, TelegramSenderService, SenderController, модели Sender/Setting,
  фронт-панель TelegramSenderPanel, админ-карточка, updateTgSettings, тип tg_name_fee.
  Таблицы client_tg_senders/_settings дропнуты.
- мёртвые таблицы client_tg_messages (доставка по номеру) и client_tg_templates (шаблоны
  сообщений) — без модели/использования, дропнуты (миграция 000017, create-миграции удалены).
- client_tg_optouts переосмыслен как ручной список «не показывать этим номерам» (не «отписки»).
- лексика рассылки → рекламы: «Авторассылка»→«Авто-реклама», «кошелёк/заморожено»→«общий баланс».

Медиа объявления (реальный пробел показов): новый POST /api/telegram/campaigns/{id}/media
(картинка/видео к черновику, png/jpg/gif/mp4 ≤50 МБ, замена удаляет старый файл) + поле
загрузки в форме кабинета. Робот уже принимал media_path (RunTelegramCampaignJob → mediaFile
→ cabinet.js fillAdMedia) — не хватало только приёма файла от клиента.

Схема: db/CHANGELOG_schema.md v8.94 (CHECK += tg_ad_charge/tg_ad_refund) + v8.95 (дроп
СМС-таблиц, tg_name_fee убран). Тесты зелёные поштучно (квирк партиций — гонять по одному):
Schema/Models/AdminTg/CampaignCharge/CampaignApi/Audience/Media; фронт advertising-telegram-view
19 + admin-tg/auto-rule 24. pint/deptrac(0) чисто; larastan исключён (Pest-$this шум). Робот
bots/mts-telegram-ads НЕ трогали.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-29 06:26:07 +03:00

60 lines
2.4 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
declare(strict_types=1);
use App\Models\ClientTg\Tariff;
use Illuminate\Foundation\Testing\DatabaseTransactions;
/**
* SaaS-admin «Клиентская Telegram-реклама» — тарифная сетка (client_tg_tariffs).
* План §Сессия 4, задача 4.4. Зеркало AdminTariffTest (СМС). Гейт группы —
* saas-admin,admin-db (crm_admin_user); admin-db переключает соединение на
* pgsql_admin, а SharesAdminPdo (глобально в Pest.php) отдаёт ему ту же PDO-сессию,
* что у default (видимость засеянной схемы).
*
* Сидированы миграциями: 5 ступеней (1/0.45, 1000/0.42, 5000/0.40, 10000/0.38, 50000/0.36).
*/
uses(DatabaseTransactions::class);
test('GET /api/admin/telegram/tariffs возвращает сидированные ступени', function () {
$r = $this->getJson('/api/admin/telegram/tariffs');
$r->assertStatus(200);
$tariffs = $r->json('tariffs');
expect($tariffs)->toHaveCount(5);
expect(collect($tariffs)->pluck('min_qty')->all())->toBe([1, 1000, 5000, 10000, 50000]);
expect($tariffs[0]['price_rub'])->toBe('0.45');
});
test('PUT /api/admin/telegram/tariffs заменяет весь набор ступеней', function () {
$r = $this->putJson('/api/admin/telegram/tariffs', [
'rows' => [
['min_qty' => 1, 'price_rub' => '0.50'],
['min_qty' => 5000, 'price_rub' => '0.35'],
],
]);
$r->assertStatus(200);
expect(Tariff::count())->toBe(2);
$rows = Tariff::orderBy('min_qty')->get();
expect($rows[0]->min_qty)->toBe(1);
expect($rows[0]->price_rub)->toBe('0.50');
expect($rows[1]->min_qty)->toBe(5000);
expect($rows[1]->price_rub)->toBe('0.35');
expect(collect($r->json('tariffs'))->pluck('min_qty')->all())->toBe([1, 5000]);
});
test('PUT /api/admin/telegram/tariffs с некорректной ступенью → 422', function () {
$this->putJson('/api/admin/telegram/tariffs', [
'rows' => [['min_qty' => 0, 'price_rub' => '0.50']],
])->assertStatus(422);
$this->putJson('/api/admin/telegram/tariffs', [
'rows' => [['min_qty' => 1, 'price_rub' => '0.00']],
])->assertStatus(422);
// Исходные 5 ступеней не тронуты.
expect(Tariff::count())->toBe(5);
});