feat: add OtpCodeFactory and comprehensive tests for AuthService and RegistrationService
This commit is contained in:
@@ -0,0 +1,158 @@
|
||||
<?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'));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user