Files
portal/app/tests/Feature/Advertising/ChargeCampaignSpendJobTest.php
T

122 lines
4.7 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\Events\AdvertisingStopped;
use App\Jobs\ChargeCampaignSpendJob;
use App\Models\AdCampaign;
use App\Models\AdWallet;
use App\Models\AdWalletTransaction;
use App\Models\Tenant;
use App\Services\Advertising\AdWalletService;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Event;
use Illuminate\Support\Facades\Http;
use Tests\Concerns\SharesSupplierPdo;
// Джоб перечисляет кампании через pgsql_supplier (BYPASSRLS) — без share PDO
// вставленные в тест-транзакции кампании (default pgsql-соединение) не видны
// второму соединению до commit'а. RefreshDatabase раньше не был подключён в
// этом файле — подключаем оба вместе (см. общее правило других Advertising-тестов).
uses(RefreshDatabase::class);
uses(SharesSupplierPdo::class);
function configureYandexDirectForSpend(): void
{
config(['services.yandex_direct.enabled' => true]);
config(['services.yandex_direct.token' => 'DIRTOKEN']);
config(['services.yandex_direct.base_url' => 'https://api-sandbox.direct.yandex.com']);
}
/** TSV: Clicks=7, Cost=200.00 ₽ (яндексовы рубли, до наценки). */
function fakeYandexReportsSpend(string $tsv = "7\t200.00"): void
{
Http::fake(['*/json/v5/reports' => Http::response($tsv, 200)]);
}
function makeRunningCampaign(int $tenantId): AdCampaign
{
return AdCampaign::create([
'tenant_id' => $tenantId,
'name' => 'Кампания списания',
'status' => AdCampaign::STATUS_RUNNING,
'yandex_campaign_id' => 222,
'audience_days' => 10,
'weekly_budget_rub' => '500.00',
]);
}
it('charges the client the marked-up cost of yesterday\'s Direct spend', function () {
configureYandexDirectForSpend();
fakeYandexReportsSpend();
$tenant = Tenant::factory()->create();
app(AdWalletService::class)->topup($tenant->id, '1000.00', 'yandex', 'тест');
$campaign = makeRunningCampaign($tenant->id);
app(ChargeCampaignSpendJob::class)->handle();
$wallet = AdWallet::where('tenant_id', $tenant->id)->first();
expect($wallet->balance_rub)->toBe('740.00'); // 1000.00 260.00
$date = Carbon::now()->subDay()->toDateString();
$tx = AdWalletTransaction::where('tenant_id', $tenant->id)
->where('external_key', "yandex:{$campaign->id}:{$date}")
->first();
expect($tx)->not->toBeNull()
->and($tx->type)->toBe(AdWalletTransaction::TYPE_CHARGE)
->and($tx->amount_rub)->toBe('-260.00');
});
it('does not double-charge on a second run (idempotent by external_key)', function () {
configureYandexDirectForSpend();
fakeYandexReportsSpend();
$tenant = Tenant::factory()->create();
app(AdWalletService::class)->topup($tenant->id, '1000.00', 'yandex', 'тест');
makeRunningCampaign($tenant->id);
app(ChargeCampaignSpendJob::class)->handle();
$balanceAfterFirst = AdWallet::where('tenant_id', $tenant->id)->first()->balance_rub;
app(ChargeCampaignSpendJob::class)->handle();
$balanceAfterSecond = AdWallet::where('tenant_id', $tenant->id)->first()->balance_rub;
expect($balanceAfterSecond)->toBe($balanceAfterFirst)
->and($balanceAfterSecond)->toBe('740.00');
});
it('stops all advertising when the charge leaves the tenant insolvent', function () {
Event::fake([AdvertisingStopped::class]);
configureYandexDirectForSpend();
fakeYandexReportsSpend();
$tenant = Tenant::factory()->create();
// Малый баланс, крупная заморозка → после списания 260.00 баланс уйдёт ниже frozen.
app(AdWalletService::class)->topup($tenant->id, '300.00', 'yandex', 'тест');
$campaign = makeRunningCampaign($tenant->id);
app(AdWalletService::class)->freeze($tenant->id, 'yandex', 'campaign', $campaign->id, '200.00');
app(ChargeCampaignSpendJob::class)->handle();
Event::assertDispatched(AdvertisingStopped::class, fn ($e) => $e->tenantId === $tenant->id);
});
it('does nothing and sends no HTTP when the yandex_direct switch is off', function () {
config(['services.yandex_direct.enabled' => false]);
fakeYandexReportsSpend();
$tenant = Tenant::factory()->create();
app(AdWalletService::class)->topup($tenant->id, '1000.00', 'yandex', 'тест');
makeRunningCampaign($tenant->id);
app(ChargeCampaignSpendJob::class)->handle();
Http::assertNothingSent();
$wallet = AdWallet::where('tenant_id', $tenant->id)->first();
expect($wallet->balance_rub)->toBe('1000.00');
});