feat: add OtpCodeFactory and comprehensive tests for AuthService and RegistrationService

This commit is contained in:
2026-09-21 05:06:46 +03:30
parent 9718fb8c7c
commit fd0d445b6e
8 changed files with 447 additions and 6 deletions
@@ -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'));
}
}