Web app: new entities (Image, RenderedAsset, SharedImage, Token, DeviceImageHistory), enums, repositories, controllers, message handlers, migrations, tests, frontend upload/library/sticker UI, Vue components. Firmware: EPD background screen binaries + gen scripts, setup_bg header. Infra: ddev config, test bundle, gitignore coverage dir. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,157 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Tests\Unit\Service;
|
||||
|
||||
use App\Entity\Image;
|
||||
use App\Entity\Token;
|
||||
use App\Entity\User;
|
||||
use App\Enum\TokenType;
|
||||
use App\Repository\TokenRepository;
|
||||
use App\Service\TokenService;
|
||||
use Doctrine\ORM\EntityManagerInterface;
|
||||
use PHPUnit\Framework\MockObject\MockObject;
|
||||
use PHPUnit\Framework\TestCase;
|
||||
|
||||
class TokenServiceTest extends TestCase
|
||||
{
|
||||
private function makeService(): array
|
||||
{
|
||||
$repo = $this->createStub(TokenRepository::class);
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$service = new TokenService($repo, $em);
|
||||
return [$service, $repo, $em];
|
||||
}
|
||||
|
||||
private function makeServiceWithMockEm(): array
|
||||
{
|
||||
$repo = $this->createStub(TokenRepository::class);
|
||||
$em = $this->createMock(EntityManagerInterface::class);
|
||||
$service = new TokenService($repo, $em);
|
||||
return [$service, $repo, $em];
|
||||
}
|
||||
|
||||
private function makeImage(): Image
|
||||
{
|
||||
$user = new User();
|
||||
$image = new Image();
|
||||
$image->setUser($user)->setOriginalFilename('test.jpg')->setStoragePath('x');
|
||||
return $image;
|
||||
}
|
||||
|
||||
public function test_issue_returns_token_with_correct_type(): void
|
||||
{
|
||||
[$service, , $em] = $this->makeServiceWithMockEm();
|
||||
$em->expects($this->once())->method('persist');
|
||||
|
||||
$token = $service->issue(TokenType::ShareApprove, $this->makeImage(), null, 'a@b.com', 7);
|
||||
|
||||
$this->assertSame(TokenType::ShareApprove, $token->getType());
|
||||
}
|
||||
|
||||
public function test_issue_expiry_is_in_the_future(): void
|
||||
{
|
||||
[$service] = $this->makeService();
|
||||
|
||||
$token = $service->issue(TokenType::ShareApprove, $this->makeImage(), null, null, 7);
|
||||
|
||||
$this->assertGreaterThan(new \DateTimeImmutable(), $token->getExpiresAt());
|
||||
}
|
||||
|
||||
public function test_issue_calls_em_persist(): void
|
||||
{
|
||||
[$service, , $em] = $this->makeServiceWithMockEm();
|
||||
$em->expects($this->once())->method('persist')->with($this->isInstanceOf(Token::class));
|
||||
|
||||
$service->issue(TokenType::ShareApprove, $this->makeImage(), null, 'a@b.com', 7);
|
||||
}
|
||||
|
||||
public function test_consume_calls_token_consume(): void
|
||||
{
|
||||
$repo = $this->createStub(TokenRepository::class);
|
||||
$em = $this->createMock(EntityManagerInterface::class);
|
||||
$service = new TokenService($repo, $em);
|
||||
|
||||
/** @var Token&MockObject $token */
|
||||
$token = $this->createMock(Token::class);
|
||||
$token->expects($this->once())->method('consume');
|
||||
$em->expects($this->once())->method('flush');
|
||||
|
||||
$repo->method('findValidToken')->willReturn($token);
|
||||
|
||||
$service->consume('some-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
|
||||
public function test_consume_calls_em_flush(): void
|
||||
{
|
||||
$repo = $this->createStub(TokenRepository::class);
|
||||
$em = $this->createMock(EntityManagerInterface::class);
|
||||
$service = new TokenService($repo, $em);
|
||||
|
||||
$token = $this->createStub(Token::class);
|
||||
$em->expects($this->once())->method('flush');
|
||||
|
||||
$repo->method('findValidToken')->willReturn($token);
|
||||
|
||||
$service->consume('some-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
|
||||
public function test_consume_returns_the_token(): void
|
||||
{
|
||||
$repo = $this->createStub(TokenRepository::class);
|
||||
$em = $this->createStub(EntityManagerInterface::class);
|
||||
$service = new TokenService($repo, $em);
|
||||
|
||||
$token = $this->createStub(Token::class);
|
||||
$repo->method('findValidToken')->willReturn($token);
|
||||
|
||||
$result = $service->consume('some-uuid', TokenType::ShareApprove);
|
||||
|
||||
$this->assertSame($token, $result);
|
||||
}
|
||||
|
||||
public function test_consume_throws_when_token_not_found(): void
|
||||
{
|
||||
[$service, $repo] = $this->makeService();
|
||||
$repo->method('findValidToken')->willReturn(null);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
$service->consume('invalid-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
|
||||
/** T-05: expired token — repo already excludes it, returns null → RuntimeException */
|
||||
public function test_consume_throws_for_expired_token(): void
|
||||
{
|
||||
[$service, $repo] = $this->makeService();
|
||||
$repo->method('findValidToken')->willReturn(null);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
$service->consume('expired-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
|
||||
/** T-06: already-used token — repo excludes usedAt IS NOT NULL, returns null → RuntimeException */
|
||||
public function test_consume_throws_for_already_used_token(): void
|
||||
{
|
||||
[$service, $repo] = $this->makeService();
|
||||
$repo->method('findValidToken')->willReturn(null);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
$service->consume('used-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
|
||||
/** T-07: type mismatch — repo WHERE clause filters type, returns null → RuntimeException */
|
||||
public function test_consume_throws_for_type_mismatch(): void
|
||||
{
|
||||
[$service, $repo] = $this->makeService();
|
||||
$repo->method('findValidToken')->willReturn(null);
|
||||
|
||||
$this->expectException(\RuntimeException::class);
|
||||
|
||||
// UUID issued as ShareDecline but consumed as ShareApprove
|
||||
$service->consume('some-uuid', TokenType::ShareApprove);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user