159 lines
5.4 KiB
PHP
159 lines
5.4 KiB
PHP
<?php
|
|
|
|
namespace Tests\Feature\Services\Auth;
|
|
|
|
use App\Models\OtpCode;
|
|
use App\Services\Auth\OtpService;
|
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
|
use Illuminate\Http\Client\Request;
|
|
use Illuminate\Support\Carbon;
|
|
use Illuminate\Support\Facades\Http;
|
|
use Tests\TestCase;
|
|
|
|
class OtpServiceTest extends TestCase
|
|
{
|
|
use RefreshDatabase;
|
|
|
|
protected OtpService $service;
|
|
|
|
protected function setUp(): void
|
|
{
|
|
parent::setUp();
|
|
|
|
Http::fake();
|
|
$this->service = app(OtpService::class);
|
|
}
|
|
|
|
public function test_generate_code_returns_four_digit_numeric_string(): void
|
|
{
|
|
$code = $this->service->generateCode();
|
|
|
|
$this->assertMatchesRegularExpression('/^[1-9]\d{3}$/', $code);
|
|
}
|
|
|
|
public function test_send_for_phone_stores_code_with_expiry(): void
|
|
{
|
|
Carbon::setTestNow('2026-09-21 10:00:00');
|
|
|
|
$otpCode = $this->service->sendForPhone('09120000000');
|
|
|
|
$this->assertSame('09120000000', $otpCode->phone);
|
|
$this->assertMatchesRegularExpression('/^\d{4}$/', $otpCode->code);
|
|
$this->assertTrue($otpCode->expires_at->eq(Carbon::parse('2026-09-21 10:03:00')));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
}
|
|
|
|
public function test_send_for_phone_uses_custom_minutes(): void
|
|
{
|
|
Carbon::setTestNow('2026-09-21 10:00:00');
|
|
|
|
$otpCode = $this->service->sendForPhone('09120000000', 10);
|
|
|
|
$this->assertTrue($otpCode->expires_at->eq(Carbon::parse('2026-09-21 10:10:00')));
|
|
}
|
|
|
|
public function test_send_for_phone_replaces_existing_code_for_same_phone(): void
|
|
{
|
|
$existing = OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1111']);
|
|
|
|
$otpCode = $this->service->sendForPhone('09120000000');
|
|
|
|
$this->assertTrue($otpCode->is($existing));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
$this->assertDatabaseMissing('otp_codes', ['phone' => '09120000000', 'code' => '1111']);
|
|
}
|
|
|
|
public function test_send_for_phone_sends_verify_code_pattern_sms(): void
|
|
{
|
|
config(['services.iranpayamak.patterns.verify_code' => 'verify-pattern']);
|
|
|
|
$otpCode = $this->service->sendForPhone('09120000000');
|
|
|
|
Http::assertSent(function (Request $request) use ($otpCode) {
|
|
return $request['code'] === 'verify-pattern'
|
|
&& $request['recipient'] === '09120000000'
|
|
&& $request['attributes']['otp'] === $otpCode->code;
|
|
});
|
|
}
|
|
|
|
public function test_send_for_phone_still_stores_code_when_sms_fails(): void
|
|
{
|
|
Http::fake([config('services.iranpayamak.api_url') => Http::response('error', 500)]);
|
|
|
|
$otpCode = $this->service->sendForPhone('09120000000');
|
|
|
|
$this->assertDatabaseHas('otp_codes', ['id' => $otpCode->id, 'phone' => '09120000000']);
|
|
}
|
|
|
|
public function test_verify_code_returns_true_and_deletes_valid_code(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertTrue($this->service->verifyCode('09120000000', '1234'));
|
|
$this->assertDatabaseCount('otp_codes', 0);
|
|
}
|
|
|
|
public function test_verify_code_returns_false_for_wrong_code_and_keeps_record(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertFalse($this->service->verifyCode('09120000000', '0000'));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
}
|
|
|
|
public function test_verify_code_returns_false_for_other_phone(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertFalse($this->service->verifyCode('09130000000', '1234'));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
}
|
|
|
|
public function test_verify_code_returns_false_for_expired_code(): void
|
|
{
|
|
OtpCode::factory()->expired()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertFalse($this->service->verifyCode('09120000000', '1234'));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
}
|
|
|
|
public function test_verify_code_returns_false_when_code_expires_exactly_now(): void
|
|
{
|
|
Carbon::setTestNow('2026-09-21 10:00:00');
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234', 'expires_at' => now()]);
|
|
|
|
$this->assertFalse($this->service->verifyCode('09120000000', '1234'));
|
|
}
|
|
|
|
public function test_verify_code_cannot_be_reused(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->service->verifyCode('09120000000', '1234');
|
|
|
|
$this->assertFalse($this->service->verifyCode('09120000000', '1234'));
|
|
}
|
|
|
|
public function test_exists_valid_returns_true_without_deleting_code(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertTrue($this->service->existsValid('09120000000', '1234'));
|
|
$this->assertDatabaseCount('otp_codes', 1);
|
|
}
|
|
|
|
public function test_exists_valid_returns_false_for_wrong_code(): void
|
|
{
|
|
OtpCode::factory()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertFalse($this->service->existsValid('09120000000', '0000'));
|
|
}
|
|
|
|
public function test_exists_valid_returns_false_for_expired_code(): void
|
|
{
|
|
OtpCode::factory()->expired()->create(['phone' => '09120000000', 'code' => '1234']);
|
|
|
|
$this->assertFalse($this->service->existsValid('09120000000', '1234'));
|
|
}
|
|
}
|