whisper-money/tests/Feature/Console/BackfillTransactionDescript...

147 lines
5.6 KiB
PHP

<?php
use App\Models\AutomationRule;
use App\Models\Transaction;
use App\Models\User;
use App\Services\AutomationRuleService;
use function Pest\Laravel\artisan;
function taggedTransaction(array $attributes = []): Transaction
{
return Transaction::factory()->create([
'description' => '/TXT/D|BAR CONO',
'original_description' => null,
'description_iv' => null,
...$attributes,
]);
}
test('reformats already-imported descriptions that still carry the remittance tag', function () {
$transaction = taggedTransaction();
artisan('banking:backfill-descriptions')
->expectsOutputToContain('1 transaction(s) and 0 automation rule(s) reformatted.')
->assertSuccessful();
$transaction->refresh();
expect($transaction->description)->toBe('BAR CONO');
expect($transaction->original_description)->toBe('/TXT/D|BAR CONO');
});
test('leaves untagged descriptions alone', function () {
$transaction = taggedTransaction(['description' => 'Cafe de la Plaza']);
artisan('banking:backfill-descriptions')->assertSuccessful();
expect($transaction->refresh()->description)->toBe('Cafe de la Plaza');
});
test('skips transactions the server cannot read', function () {
$transaction = taggedTransaction(['description_iv' => 'abcdefghijklmnop']);
artisan('banking:backfill-descriptions')->assertSuccessful();
expect($transaction->refresh()->description)->toBe('/TXT/D|BAR CONO');
});
test('keeps an original description that was already stored', function () {
$transaction = taggedTransaction(['original_description' => '/TXT/D|BAR CONO 27/07/26|20260803']);
artisan('banking:backfill-descriptions')->assertSuccessful();
expect($transaction->refresh()->original_description)->toBe('/TXT/D|BAR CONO 27/07/26|20260803');
});
test('rewrites the automation rules that match on the raw tag', function () {
$rule = AutomationRule::factory()->for(User::factory())->create([
'rules_json' => ['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]],
]);
artisan('banking:backfill-descriptions')
->expectsOutputToContain('0 transaction(s) and 1 automation rule(s) reformatted.')
->assertSuccessful();
expect($rule->refresh()->rules_json)->toBe(['in' => ['BAR CONO', ['var' => 'description']]]);
});
test('rewrites the tag in rules stored as a JSON string', function () {
$rule = AutomationRule::factory()->for(User::factory())->create([
'rules_json' => json_encode(['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]]),
]);
artisan('banking:backfill-descriptions')->assertSuccessful();
expect(json_decode($rule->refresh()->rules_json, true))
->toBe(['in' => ['BAR CONO', ['var' => 'description']]]);
});
test('a rule matching on the raw tag still matches its transaction afterwards', function () {
$user = User::factory()->create();
$transaction = taggedTransaction([
'user_id' => $user->id,
'description' => '/TXT/D|RECIBO VISA CLASICA',
]);
$rule = AutomationRule::factory()->for($user)->create([
'rules_json' => ['in' => ['/TXT/D|RECIBO VISA CLASICA', ['var' => 'description']]],
]);
$service = app(AutomationRuleService::class);
expect($service->ruleMatches($rule, $transaction))->toBeTrue();
artisan('banking:backfill-descriptions')->assertSuccessful();
expect($service->ruleMatches($rule->refresh(), $transaction->refresh()))->toBeTrue();
});
test('leaves untagged automation rules untouched', function () {
$rule = AutomationRule::factory()->for(User::factory())->create([
'rules_json' => ['in' => ['grocery', ['var' => 'description']]],
]);
artisan('banking:backfill-descriptions')->assertSuccessful();
expect($rule->refresh()->rules_json)->toBe(['in' => ['grocery', ['var' => 'description']]]);
});
test('does not save anything on a dry run', function () {
$transaction = taggedTransaction();
$rule = AutomationRule::factory()->for(User::factory())->create([
'rules_json' => ['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]],
]);
artisan('banking:backfill-descriptions', ['--dry-run' => true])
->expectsOutputToContain('1 transaction(s) and 1 automation rule(s) would be reformatted.')
->assertSuccessful();
expect($transaction->refresh()->description)->toBe('/TXT/D|BAR CONO');
expect($rule->refresh()->rules_json)->toBe(['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]]);
});
test('only touches the given user when one is provided', function () {
$user = User::factory()->create();
$mine = taggedTransaction(['user_id' => $user->id]);
$theirs = taggedTransaction();
$myRule = AutomationRule::factory()->for($user)->create([
'rules_json' => ['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]],
]);
$theirRule = AutomationRule::factory()->for(User::factory())->create([
'rules_json' => ['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]],
]);
artisan('banking:backfill-descriptions', ['--user' => $user->email])->assertSuccessful();
expect($mine->refresh()->description)->toBe('BAR CONO');
expect($theirs->refresh()->description)->toBe('/TXT/D|BAR CONO');
expect($myRule->refresh()->rules_json)->toBe(['in' => ['BAR CONO', ['var' => 'description']]]);
expect($theirRule->refresh()->rules_json)->toBe(['in' => ['/TXT/D|BAR CONO', ['var' => 'description']]]);
});
test('fails when the given user does not exist', function () {
artisan('banking:backfill-descriptions', ['--user' => 'nobody@example.com'])
->expectsOutputToContain("User with email 'nobody@example.com' not found.")
->assertFailed();
});