89 lines
4.5 KiB
PHP
89 lines
4.5 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature;
|
|
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Support\Facades\DB;
|
|
use Illuminate\Support\Str;
|
|
use Tests\TestCase;
|
|
|
|
class SimulationFormDraftTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
public function test_anonymous_draft_is_encrypted_scoped_and_restorable(): void
|
|
{
|
|
$token = Str::random(48);
|
|
$key = (string) Str::uuid();
|
|
$payload = $this->payload();
|
|
$created = $this->postJson('/api/v1/simulation-drafts', $payload, [
|
|
'Idempotency-Key' => $key, 'X-Simulation-Token' => $token,
|
|
])->assertCreated()->assertJsonPath('data.version_no', 1);
|
|
$id = $created->json('data.id');
|
|
$stored = DB::table('simulation_form_drafts')->where('id', $id)->first();
|
|
$this->assertNotSame(json_encode($payload['personal_snapshot']), $stored->personal_snapshot);
|
|
$this->assertNotSame(json_encode($payload['economic_snapshot']), $stored->economic_snapshot);
|
|
|
|
$this->getJson('/api/v1/simulation-drafts/current', ['X-Simulation-Token' => $token])
|
|
->assertOk()->assertJsonPath('data.personal_snapshot.legal_name', 'Entidade de teste')
|
|
->assertJsonPath('data.economic_snapshot.base', '1200');
|
|
$this->getJson('/api/v1/simulation-drafts/'.$id, ['X-Simulation-Token' => Str::random(48)])->assertNotFound();
|
|
}
|
|
|
|
public function test_creation_is_idempotent_and_rejects_key_reuse_with_changed_data(): void
|
|
{
|
|
$token = Str::random(48);
|
|
$key = (string) Str::uuid();
|
|
$headers = ['Idempotency-Key' => $key, 'X-Simulation-Token' => $token];
|
|
$payload = $this->payload();
|
|
$first = $this->postJson('/api/v1/simulation-drafts', $payload, $headers)->assertCreated();
|
|
$id = $first->json('data.id');
|
|
$this->postJson('/api/v1/simulation-drafts', $payload, $headers)
|
|
->assertOk()->assertJsonPath('data.id', $id)->assertJsonPath('data.replayed', true);
|
|
$payload['economic_snapshot']['base'] = '1300';
|
|
$this->postJson('/api/v1/simulation-drafts', $payload, $headers)->assertConflict();
|
|
}
|
|
|
|
public function test_updates_require_a_current_etag_and_delete_is_reversible_until_expiry(): void
|
|
{
|
|
$token = Str::random(48);
|
|
$headers = ['Idempotency-Key' => (string) Str::uuid(), 'X-Simulation-Token' => $token];
|
|
$payload = $this->payload();
|
|
$created = $this->postJson('/api/v1/simulation-drafts', $payload, $headers)->assertCreated();
|
|
$id = $created->json('data.id');
|
|
$url = '/api/v1/simulation-drafts/'.$id;
|
|
$this->putJson($url, $payload, ['X-Simulation-Token' => $token])->assertStatus(428);
|
|
$this->putJson($url, $payload, ['X-Simulation-Token' => $token, 'If-Match' => '"sfd-'.$id.'-v99"'])->assertStatus(412);
|
|
$updated = $this->putJson($url, $payload, ['X-Simulation-Token' => $token, 'If-Match' => $created->headers->get('ETag')])
|
|
->assertOk()->assertJsonPath('data.version_no', 2);
|
|
$this->deleteJson($url, [], ['X-Simulation-Token' => $token, 'If-Match' => $updated->headers->get('ETag')])->assertNoContent();
|
|
$this->assertDatabaseHas('simulation_form_drafts', ['id' => $id]);
|
|
$this->assertDatabaseMissing('simulation_form_drafts', ['id' => $id, 'deleted_at' => null]);
|
|
}
|
|
|
|
public function test_server_rejects_invalid_rates_and_unlisted_snapshot_fields(): void
|
|
{
|
|
$token = Str::random(48);
|
|
$payload = $this->payload();
|
|
$payload['economic_snapshot']['rate'] = '101';
|
|
$this->postJson('/api/v1/simulation-drafts', $payload, [
|
|
'Idempotency-Key' => (string) Str::uuid(), 'X-Simulation-Token' => $token,
|
|
])->assertUnprocessable()->assertJsonValidationErrors('economic_snapshot.rate');
|
|
|
|
$payload = $this->payload();
|
|
$payload['economic_snapshot']['unapproved_field'] = 'not accepted';
|
|
$this->postJson('/api/v1/simulation-drafts', $payload, [
|
|
'Idempotency-Key' => (string) Str::uuid(), 'X-Simulation-Token' => $token,
|
|
])->assertUnprocessable()->assertJsonValidationErrors('economic_snapshot');
|
|
}
|
|
|
|
/** @return array{personal_snapshot: array<string,string>, economic_snapshot: array<string,string>} */
|
|
private function payload(): array
|
|
{
|
|
return [
|
|
'personal_snapshot' => ['legal_name' => 'Entidade de teste', 'email' => 'teste@example.com'],
|
|
'economic_snapshot' => ['purpose' => 'simulate_only', 'tax_calculation' => 'yes', 'base' => '1200', 'rate' => '8', 'reduction' => '0'],
|
|
];
|
|
}
|
|
}
|