create(); AdWallet::create(['tenant_id' => $tenant->id, 'balance_rub' => $balance, 'frozen_rub' => '0.00']); return [$tenant, app(AdWalletService::class)]; } /** @return Collection */ function letopis(int $tenantId, string $type) { return AdWalletTransaction::where('tenant_id', $tenantId)->where('type', $type)->get(); } it('заморозка записана настоящей суммой, а не нулём', function () { [$tenant, $svc] = tenantSKoshelkom(); $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); $rows = letopis($tenant->id, AdWalletTransaction::TYPE_FREEZE); expect($rows)->toHaveCount(1); $row = $rows->first(); // Знак как у списания: деньги ушли из свободных. Сам баланс заморозка не двигает. expect((string) $row->amount_rub)->toBe('-3333.36') ->and((string) $row->balance_rub_after)->toBe('10000.00') ->and($row->channel)->toBe('yandex') ->and($row->related_type)->toBe('campaign') ->and($row->related_id)->toBe(6) // Клиент должен понять, ПОД ЧТО заперты деньги, не выходя из кошелька. ->and($row->description)->toContain('6'); }); it('снятие заморозки записано отдельной строкой с суммой', function () { [$tenant, $svc] = tenantSKoshelkom(); $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); $svc->release($tenant->id, 'yandex', 'campaign', 6); $rows = letopis($tenant->id, AdWalletTransaction::TYPE_RELEASE); expect($rows)->toHaveCount(1); $row = $rows->first(); expect((string) $row->amount_rub)->toBe('3333.36') ->and((string) $row->balance_rub_after)->toBe('10000.00') ->and($row->related_id)->toBe(6) ->and($row->description)->toContain('6'); }); it('повторная заморозка той же кампании не плодит вторую строку', function () { [$tenant, $svc] = tenantSKoshelkom(); $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); // идемпотентность expect(letopis($tenant->id, AdWalletTransaction::TYPE_FREEZE))->toHaveCount(1); }); it('снятие несуществующей заморозки летопись не трогает', function () { [$tenant, $svc] = tenantSKoshelkom(); $svc->release($tenant->id, 'yandex', 'campaign', 777); expect(letopis($tenant->id, AdWalletTransaction::TYPE_RELEASE))->toHaveCount(0); }); it('пауза и возобновление оставляют читаемую пару строк', function () { [$tenant, $svc] = tenantSKoshelkom(); $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); $svc->release($tenant->id, 'yandex', 'campaign', 6); // пауза $svc->freeze($tenant->id, 'yandex', 'campaign', 6, '3333.36'); // возобновление // Три события — три строки: заморозили, сняли, заморозили заново. $vse = AdWalletTransaction::where('tenant_id', $tenant->id) ->whereIn('type', [AdWalletTransaction::TYPE_FREEZE, AdWalletTransaction::TYPE_RELEASE]) ->orderBy('id')->get(); expect($vse->pluck('type')->all())->toBe([ AdWalletTransaction::TYPE_FREEZE, AdWalletTransaction::TYPE_RELEASE, AdWalletTransaction::TYPE_FREEZE, ]); });