2026-07-25 20:59:57 +03:00
|
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
|
|
declare(strict_types=1);
|
|
|
|
|
|
|
|
|
|
|
|
use App\Models\ClientSmsSettings;
|
|
|
|
|
|
use App\Models\ClientSmsTariff;
|
|
|
|
|
|
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
|
|
|
|
|
|
|
|
|
|
|
uses(DatabaseTransactions::class);
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Task 8: SaaS-admin «Клиентские СМС» — тарифная сетка (client_sms_tariffs)
|
|
|
|
|
|
* и общие настройки (client_sms_settings, singleton).
|
|
|
|
|
|
*
|
|
|
|
|
|
* Сидированы миграциями: 5 ступеней (1/9.00, 100/8.50, 250/8.30, 500/8.10,
|
|
|
|
|
|
* 1000/8.00) + settings (fee 2500.00, grace 29 дней).
|
|
|
|
|
|
*/
|
|
|
|
|
|
test('GET /api/admin/sms/tariffs возвращает сидированные ступени и настройки', function () {
|
|
|
|
|
|
$r = $this->getJson('/api/admin/sms/tariffs');
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
|
|
|
|
|
|
$tariffs = $r->json('tariffs');
|
|
|
|
|
|
expect($tariffs)->toHaveCount(5);
|
|
|
|
|
|
expect(collect($tariffs)->pluck('min_qty')->all())->toBe([1, 100, 250, 500, 1000]);
|
|
|
|
|
|
expect($tariffs[0]['price_rub'])->toBe('9.00');
|
|
|
|
|
|
|
|
|
|
|
|
expect($r->json('settings.name_fee_rub_per_operator'))->toBe('2500.00');
|
|
|
|
|
|
expect($r->json('settings.name_debt_grace_days'))->toBe(29);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('PUT /api/admin/sms/tariffs заменяет весь набор ступеней', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/tariffs', [
|
|
|
|
|
|
'rows' => [
|
|
|
|
|
|
['min_qty' => 1, 'price_rub' => '10.00'],
|
|
|
|
|
|
['min_qty' => 500, 'price_rub' => '7.50'],
|
|
|
|
|
|
],
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
|
|
|
|
|
|
expect(ClientSmsTariff::count())->toBe(2);
|
|
|
|
|
|
|
|
|
|
|
|
$rows = ClientSmsTariff::orderBy('min_qty')->get();
|
|
|
|
|
|
expect($rows[0]->min_qty)->toBe(1);
|
|
|
|
|
|
expect($rows[0]->price_rub)->toBe('10.00');
|
|
|
|
|
|
expect($rows[1]->min_qty)->toBe(500);
|
|
|
|
|
|
expect($rows[1]->price_rub)->toBe('7.50');
|
|
|
|
|
|
|
|
|
|
|
|
$responseTariffs = $r->json('tariffs');
|
|
|
|
|
|
expect($responseTariffs)->toHaveCount(2);
|
|
|
|
|
|
expect(collect($responseTariffs)->pluck('min_qty')->all())->toBe([1, 500]);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('PUT /api/admin/sms/settings обновляет singleton настроек', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/settings', [
|
|
|
|
|
|
'name_fee_rub_per_operator' => '3000.00',
|
|
|
|
|
|
'name_debt_grace_days' => 14,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect($r->json('name_fee_rub_per_operator'))->toBe('3000.00');
|
|
|
|
|
|
expect($r->json('name_debt_grace_days'))->toBe(14);
|
|
|
|
|
|
|
|
|
|
|
|
$settings = ClientSmsSettings::first();
|
|
|
|
|
|
expect($settings->name_fee_rub_per_operator)->toBe('3000.00');
|
|
|
|
|
|
expect($settings->name_debt_grace_days)->toBe(14);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('PUT /api/admin/sms/tariffs с некорректной ступенью → 422', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/tariffs', [
|
|
|
|
|
|
'rows' => [
|
|
|
|
|
|
['min_qty' => 0, 'price_rub' => '10.00'],
|
|
|
|
|
|
],
|
|
|
|
|
|
]);
|
|
|
|
|
|
$r->assertStatus(422);
|
|
|
|
|
|
|
|
|
|
|
|
$r2 = $this->putJson('/api/admin/sms/tariffs', [
|
|
|
|
|
|
'rows' => [
|
|
|
|
|
|
['min_qty' => 1, 'price_rub' => '0.00'],
|
|
|
|
|
|
],
|
|
|
|
|
|
]);
|
|
|
|
|
|
$r2->assertStatus(422);
|
|
|
|
|
|
|
|
|
|
|
|
// Исходные 5 ступеней не тронуты.
|
|
|
|
|
|
expect(ClientSmsTariff::count())->toBe(5);
|
|
|
|
|
|
});
|
2026-07-29 10:25:57 +03:00
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* Строка листа 4.3: срок, после которого рассылка считается зависшей, правит
|
|
|
|
|
|
* владелец — без выпуска новой версии. Значение живёт в тех же общих настройках,
|
|
|
|
|
|
* что и границы окна отправки.
|
|
|
|
|
|
*/
|
|
|
|
|
|
test('4.3 — владелец меняет срок «рассылка зависла», и он сохраняется', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/settings', [
|
|
|
|
|
|
'name_fee_rub_per_operator' => '2500.00',
|
|
|
|
|
|
'name_debt_grace_days' => 29,
|
|
|
|
|
|
'stuck_after_minutes' => 120,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect($r->json('stuck_after_minutes'))->toBe(120);
|
|
|
|
|
|
expect(ClientSmsSettings::first()->stuck_after_minutes)->toBe(120);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('4.3 — GET отдаёт срок «рассылка зависла» вместе с остальными настройками', function () {
|
|
|
|
|
|
$r = $this->getJson('/api/admin/sms/tariffs');
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect($r->json('settings.stuck_after_minutes'))->toBe(60);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('4.3 — слишком короткий срок админка не принимает: сторож срывал бы живые рассылки', function () {
|
|
|
|
|
|
$base = ['name_fee_rub_per_operator' => '2500.00', 'name_debt_grace_days' => 29];
|
|
|
|
|
|
|
|
|
|
|
|
$this->putJson('/api/admin/sms/settings', $base + ['stuck_after_minutes' => 0])
|
|
|
|
|
|
->assertStatus(422)
|
|
|
|
|
|
->assertJsonValidationErrors('stuck_after_minutes');
|
|
|
|
|
|
|
|
|
|
|
|
$this->putJson('/api/admin/sms/settings', $base + ['stuck_after_minutes' => 4])
|
|
|
|
|
|
->assertStatus(422);
|
|
|
|
|
|
|
|
|
|
|
|
// Срок не тронут.
|
|
|
|
|
|
expect(ClientSmsSettings::first()->stuck_after_minutes)->toBe(60);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('4.3 — срок дольше суток админка не принимает: деньги висели бы замороженными', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/settings', [
|
|
|
|
|
|
'name_fee_rub_per_operator' => '2500.00',
|
|
|
|
|
|
'name_debt_grace_days' => 29,
|
|
|
|
|
|
'stuck_after_minutes' => 1441,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(422);
|
|
|
|
|
|
expect(ClientSmsSettings::first()->stuck_after_minutes)->toBe(60);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-29 17:30:03 +03:00
|
|
|
|
/**
|
|
|
|
|
|
* Строка листа 4.6: потолок загрузки базы правит владелец (журнал В-161) —
|
|
|
|
|
|
* число, которое никто не может поменять, хуже отсутствующего (мой вывод В-84).
|
|
|
|
|
|
*/
|
|
|
|
|
|
test('4.6 — владелец меняет потолок загрузки базы, и он сохраняется', function () {
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/settings', [
|
|
|
|
|
|
'name_fee_rub_per_operator' => '2500.00',
|
|
|
|
|
|
'name_debt_grace_days' => 29,
|
|
|
|
|
|
'max_upload_phones' => 30000,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect($r->json('max_upload_phones'))->toBe(30000);
|
|
|
|
|
|
expect(ClientSmsSettings::first()->max_upload_phones)->toBe(30000);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('4.6 — GET отдаёт потолок загрузки вместе с остальными настройками', function () {
|
|
|
|
|
|
$r = $this->getJson('/api/admin/sms/tariffs');
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect($r->json('settings.max_upload_phones'))->toBe(50000);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
test('4.6 — потолок вне границ админка не принимает', function () {
|
|
|
|
|
|
$base = ['name_fee_rub_per_operator' => '2500.00', 'name_debt_grace_days' => 29];
|
|
|
|
|
|
|
|
|
|
|
|
// Слишком мало: обычную базу пришлось бы резать на десяток файлов.
|
|
|
|
|
|
$this->putJson('/api/admin/sms/settings', $base + ['max_upload_phones' => 100])
|
|
|
|
|
|
->assertStatus(422)
|
|
|
|
|
|
->assertJsonValidationErrors('max_upload_phones');
|
|
|
|
|
|
|
|
|
|
|
|
// Слишком много: файл читается в память целиком.
|
|
|
|
|
|
$this->putJson('/api/admin/sms/settings', $base + ['max_upload_phones' => 100001])
|
|
|
|
|
|
->assertStatus(422);
|
|
|
|
|
|
|
|
|
|
|
|
// Потолок не тронут.
|
|
|
|
|
|
expect(ClientSmsSettings::first()->max_upload_phones)->toBe(50000);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-29 10:25:57 +03:00
|
|
|
|
test('4.3 — кто присылает только настройки имени, продолжает работать', function () {
|
|
|
|
|
|
// Контракт адреса менять права не имеем: срок необязателен, как и границы окна.
|
|
|
|
|
|
$r = $this->putJson('/api/admin/sms/settings', [
|
|
|
|
|
|
'name_fee_rub_per_operator' => '3000.00',
|
|
|
|
|
|
'name_debt_grace_days' => 20,
|
|
|
|
|
|
]);
|
|
|
|
|
|
|
|
|
|
|
|
$r->assertStatus(200);
|
|
|
|
|
|
expect(ClientSmsSettings::first()->stuck_after_minutes)->toBe(60);
|
|
|
|
|
|
});
|