Files
portal-api/tests/Feature/Api/Client/InvoiceLookupTest.php

132 lines
4.7 KiB
PHP

<?php
declare(strict_types=1);
namespace Tests\Feature\Api\Client;
use App\Enums\Invoice\InvoiceStatus;
use App\Models\Customer;
use App\Models\Invoice;
use App\Models\InvoiceItem;
use App\Support\InvoiceBuyer;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Tests\TestCase;
class InvoiceLookupTest extends TestCase
{
use RefreshDatabase;
private Invoice $invoice;
protected function setUp(): void
{
parent::setUp();
$customer = Customer::factory()->create([
'fullname' => 'Nguyen Van A',
'address' => '1 Le Loi',
'invoice_company' => 'Cong ty ABC',
'tax_code' => '0312345678',
'invoice_address' => '99 Nguyen Hue',
]);
$this->invoice = Invoice::create([
'invoice_number' => 'INV-LOOKUP-1',
'customer_id' => $customer->id,
'invoice_type' => 1,
'sub_total' => 100_000,
'tax' => 10_000,
'total' => 110_000,
'status' => InvoiceStatus::PAID->value,
]);
InvoiceItem::create([
'invoice_id' => $this->invoice->id,
'type' => 'service',
'description' => 'VPS 1 thang',
'months' => 1,
'quantity' => 1,
'unit_price' => 100_000,
'total_price' => 100_000,
]);
}
private function lookup(string $uri, array $body)
{
return $this->withHeaders(['x-access-token' => $this->buildClientAccessToken()])->postJson($uri, $body);
}
public function test_lookup_code_is_generated_on_create(): void
{
$this->assertMatchesRegularExpression('/^[A-Z0-9]{10}$/', $this->invoice->lookup_code);
}
public function test_correct_code_returns_limited_payload(): void
{
$response = $this->lookup('/v1/invoice-lookup', [
'invoice_number' => 'INV-LOOKUP-1',
'lookup_code' => strtolower($this->invoice->lookup_code),
])->assertOk();
$response->assertJsonPath('data.total', 110_000)
->assertJsonPath('data.buyer.tax_code', '0312345678')
->assertJsonPath('data.buyer.company', 'Cong ty ABC');
$body = json_encode($response->json());
$this->assertStringNotContainsString('Nguyen Van A', $body);
$this->assertStringNotContainsString('99 Nguyen Hue', $body);
$this->assertStringNotContainsString($this->invoice->lookup_code, $body);
}
public function test_wrong_code_or_unknown_number_is_404(): void
{
$this->lookup('/v1/invoice-lookup', ['invoice_number' => 'INV-LOOKUP-1', 'lookup_code' => 'WRONGCODE1'])->assertNotFound();
$this->lookup('/v1/invoice-lookup', ['invoice_number' => 'INV-NOPE', 'lookup_code' => $this->invoice->lookup_code])->assertNotFound();
$this->lookup('/v1/invoice-lookup/pdf', ['invoice_number' => 'INV-LOOKUP-1', 'lookup_code' => 'WRONGCODE1'])->assertNotFound();
}
public function test_cancelled_invoice_is_not_found(): void
{
$this->invoice->update(['status' => InvoiceStatus::CANCELLED->value]);
$this->lookup('/v1/invoice-lookup', [
'invoice_number' => 'INV-LOOKUP-1',
'lookup_code' => $this->invoice->lookup_code,
])->assertNotFound();
}
public function test_pdf_is_returned_for_correct_code(): void
{
$response = $this->lookup('/v1/invoice-lookup/pdf', [
'invoice_number' => 'INV-LOOKUP-1',
'lookup_code' => $this->invoice->lookup_code,
])->assertOk();
$this->assertSame('application/pdf', $response->headers->get('Content-Type'));
$this->assertStringStartsWith('%PDF', $response->getContent());
}
public function test_requires_client_access_token(): void
{
$this->postJson('/v1/invoice-lookup', ['invoice_number' => 'INV-LOOKUP-1', 'lookup_code' => $this->invoice->lookup_code])
->assertForbidden();
}
public function test_throttles_repeated_guesses_for_one_invoice(): void
{
for ($i = 0; $i < 10; $i++) {
$this->lookup('/v1/invoice-lookup', ['invoice_number' => 'INV-LOOKUP-1', 'lookup_code' => 'GUESS' . $i])->assertNotFound();
}
$this->lookup('/v1/invoice-lookup', ['invoice_number' => 'INV-LOOKUP-1', 'lookup_code' => $this->invoice->lookup_code])
->assertStatus(429);
}
public function test_buyer_snapshot_freezes_buyer_after_vat_is_recorded(): void
{
$this->invoice->update(['buyer_snapshot' => InvoiceBuyer::fromCustomer($this->invoice->customer)]);
$this->invoice->customer->update(['tax_code' => '0999999999']);
$this->assertSame('0312345678', InvoiceBuyer::forInvoice($this->invoice->fresh())['tax_code']);
}
}