101 lines
2.9 KiB
PHP
101 lines
2.9 KiB
PHP
<?php
|
||
/**
|
||
* File: tests/unit/Core/TOTPServiceTest.php
|
||
*
|
||
* Author: Funky Waddle
|
||
* Date: 2026-09-12
|
||
*
|
||
* Purpose:
|
||
* Unit tests for the TOTPService.
|
||
*/
|
||
declare(strict_types=1);
|
||
namespace Tests\unit\Core;
|
||
|
||
use SiteWeaver\Core\Services\TOTPService;
|
||
use OTPHP\TOTP;
|
||
use Psr\Clock\ClockInterface;
|
||
use PHPUnit\Framework\TestCase;
|
||
|
||
final class TOTPServiceTest extends TestCase
|
||
{
|
||
private ?ClockInterface $clock;
|
||
|
||
protected function setUp(): void
|
||
{
|
||
$this->clock = new class implements ClockInterface {
|
||
public function now(): \DateTimeImmutable
|
||
{
|
||
return new \DateTimeImmutable();
|
||
}
|
||
};
|
||
}
|
||
|
||
protected function tearDown(): void
|
||
{
|
||
$this->clock = null;
|
||
}
|
||
public function testGenerateAndVerify(): void
|
||
{
|
||
$totpService = new TOTPService();
|
||
$secret = $totpService->generateSecret();
|
||
|
||
// Generate a current OTP using the secret and clock
|
||
$otp = TOTP::create($secret, clock: $this->clock)->now();
|
||
|
||
$this->assertTrue($totpService->verify($secret, $otp));
|
||
}
|
||
|
||
public function testGenerateSecretUniqueness(): void
|
||
{
|
||
$totpService = new TOTPService();
|
||
$secret1 = $totpService->generateSecret();
|
||
$secret2 = $totpService->generateSecret();
|
||
|
||
$this->assertNotEquals($secret1, $secret2);
|
||
$this->assertNotEmpty($secret1);
|
||
$this->assertNotEmpty($secret2);
|
||
}
|
||
|
||
public function testVerifyWithWrongOTP(): void
|
||
{
|
||
$totpService = new TOTPService();
|
||
$secret = $totpService->generateSecret();
|
||
|
||
// correct OTP
|
||
$otpCorrect = TOTP::create($secret, clock: $this->clock)->now();
|
||
$this->assertTrue($totpService->verify($secret, $otpCorrect));
|
||
|
||
// wrong OTP
|
||
$wrongOtp = '000000';
|
||
$this->assertFalse($totpService->verify($secret, $wrongOtp));
|
||
}
|
||
|
||
public function testGetProvisioningUri(): void
|
||
{
|
||
$totpService = new TOTPService();
|
||
$secret = $totpService->generateSecret();
|
||
|
||
// Use a known account name and issuer
|
||
$accountName = 'alice@example.com';
|
||
$issuer = 'SiteWeaverCMS';
|
||
|
||
$uri = $totpService->getProvisioningUri($accountName, $issuer);
|
||
|
||
// The URI should start with otpauth://totp/
|
||
$this->assertStringStartsWith('otpauth://totp/', $uri);
|
||
|
||
// It should contain the issuer and account name (URL‑encoded)
|
||
$expectedIssuer = rawurlencode($issuer);
|
||
$expectedAccount = rawurlencode($accountName);
|
||
$this->assertStringContainsString($expectedIssuer, $uri);
|
||
$this->assertStringContainsString($expectedAccount, $uri);
|
||
|
||
// Compare with the library's own provisioning URI
|
||
$libUri = TOTP::create($secret, clock: $this->clock)
|
||
->withLabel($accountName)
|
||
->withIssuer($issuer)
|
||
->getProvisioningUri();
|
||
$this->assertEquals($libUri, $uri);
|
||
}
|
||
}
|