v7.0.0 release
This commit is contained in:
1 parent
0e7fa04b03
commit
c4ea9a6cd1
3582 files changed
+626189
-160151
No files matched your search
@@ -4,9 +4,17 @@ namespace Tests\Unit\Services;
|
||||
|
||||
use App\Contracts\Extension\ModuleManagerInterface;
|
||||
use App\Contracts\Extension\PluginManagerInterface;
|
||||
use App\Contracts\Extension\TemplateManagerInterface;
|
||||
use App\Contracts\Repositories\ActivityLogRepositoryInterface;
|
||||
use App\Contracts\Repositories\LanguagePackRepositoryInterface;
|
||||
use App\Contracts\Repositories\NotificationLogRepositoryInterface;
|
||||
use App\Contracts\Repositories\UserRepositoryInterface;
|
||||
use App\Enums\ActivityLogType;
|
||||
use App\Enums\NotificationLogStatus;
|
||||
use App\Models\ActivityLog;
|
||||
use App\Models\NotificationLog;
|
||||
use App\Services\DashboardService;
|
||||
use App\Services\LanguagePackService;
|
||||
use Illuminate\Database\Eloquent\Collection as EloquentCollection;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Mockery;
|
||||
@@ -29,6 +37,14 @@ class DashboardServiceTest extends TestCase
|
||||
|
||||
private $pluginManager;
|
||||
|
||||
private $notificationLogRepository;
|
||||
|
||||
private $templateManager;
|
||||
|
||||
private $languagePackRepository;
|
||||
|
||||
private $languagePackService;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
@@ -37,13 +53,24 @@ class DashboardServiceTest extends TestCase
|
||||
$this->activityLogRepository = Mockery::mock(ActivityLogRepositoryInterface::class);
|
||||
$this->moduleManager = Mockery::mock(ModuleManagerInterface::class);
|
||||
$this->pluginManager = Mockery::mock(PluginManagerInterface::class);
|
||||
$this->notificationLogRepository = Mockery::mock(NotificationLogRepositoryInterface::class);
|
||||
$this->templateManager = Mockery::mock(TemplateManagerInterface::class);
|
||||
$this->languagePackRepository = Mockery::mock(LanguagePackRepositoryInterface::class);
|
||||
$this->languagePackService = Mockery::mock(LanguagePackService::class);
|
||||
|
||||
$this->service = new DashboardService(
|
||||
$this->userRepository,
|
||||
$this->activityLogRepository,
|
||||
$this->moduleManager,
|
||||
$this->pluginManager
|
||||
$this->pluginManager,
|
||||
$this->notificationLogRepository,
|
||||
$this->templateManager,
|
||||
$this->languagePackRepository,
|
||||
$this->languagePackService
|
||||
);
|
||||
|
||||
// getStats() 가 항상 호출하는 템플릿/언어팩 의존성의 기본 mock (개별 테스트에서 재정의 가능).
|
||||
$this->mockTemplatesAndPacks();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -78,6 +105,32 @@ class DashboardServiceTest extends TestCase
|
||||
$this->pluginManager->shouldReceive('getActivePlugins')->andReturn($active);
|
||||
}
|
||||
|
||||
/**
|
||||
* 템플릿/언어팩 mock을 설정합니다.
|
||||
*
|
||||
* @param array $allTemplates 전체 템플릿 목록
|
||||
* @param array<string, bool> $activeByType 타입별 활성 여부 (admin/user)
|
||||
* @param int $activePacks 활성 언어팩 수
|
||||
* @param int $uninstalledBundledPacks 미설치 번들 언어팩 수
|
||||
*/
|
||||
private function mockTemplatesAndPacks(
|
||||
array $allTemplates = [],
|
||||
array $activeByType = ['admin' => false, 'user' => false],
|
||||
int $activePacks = 0,
|
||||
int $uninstalledBundledPacks = 0,
|
||||
): void {
|
||||
// byDefault() — setUp 의 기본 mock 을 개별 테스트가 재호출로 덮어쓸 수 있게 한다.
|
||||
$this->templateManager->shouldReceive('getAllTemplates')->andReturn($allTemplates)->byDefault();
|
||||
foreach (['admin', 'user'] as $type) {
|
||||
$this->templateManager->shouldReceive('getActiveTemplate')->with($type)
|
||||
->andReturn(($activeByType[$type] ?? false) ? ['identifier' => "tpl-{$type}"] : null)->byDefault();
|
||||
}
|
||||
$this->languagePackRepository->shouldReceive('getActivePacks')
|
||||
->andReturn(new EloquentCollection(array_fill(0, $activePacks, (object) [])))->byDefault();
|
||||
$this->languagePackService->shouldReceive('getUninstalledBundledPacks')
|
||||
->andReturn(collect(array_fill(0, $uninstalledBundledPacks, (object) [])))->byDefault();
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// getStats 테스트
|
||||
// ========================================================================
|
||||
@@ -99,9 +152,42 @@ class DashboardServiceTest extends TestCase
|
||||
$this->assertArrayHasKey('total_users', $stats);
|
||||
$this->assertArrayHasKey('installed_modules', $stats);
|
||||
$this->assertArrayHasKey('active_plugins', $stats);
|
||||
$this->assertArrayHasKey('installed_templates', $stats);
|
||||
$this->assertArrayHasKey('language_packs', $stats);
|
||||
$this->assertArrayHasKey('system_status', $stats);
|
||||
}
|
||||
|
||||
public function test_get_stats_returns_template_counts(): void
|
||||
{
|
||||
$this->mockUserStatistics();
|
||||
$this->mockModules();
|
||||
$this->mockPlugins();
|
||||
// 전체 템플릿 2개, admin/user 둘 다 활성 → 2/2
|
||||
$this->mockTemplatesAndPacks(
|
||||
allTemplates: [['identifier' => 'a'], ['identifier' => 'b']],
|
||||
activeByType: ['admin' => true, 'user' => true],
|
||||
);
|
||||
|
||||
$stats = $this->service->getStats();
|
||||
|
||||
$this->assertSame(2, $stats['installed_templates']['total']);
|
||||
$this->assertSame(2, $stats['installed_templates']['active']);
|
||||
}
|
||||
|
||||
public function test_get_stats_returns_language_pack_counts(): void
|
||||
{
|
||||
$this->mockUserStatistics();
|
||||
$this->mockModules();
|
||||
$this->mockPlugins();
|
||||
// 활성 14, 미설치 번들 4 → 활성 14 / 전체 18
|
||||
$this->mockTemplatesAndPacks(activePacks: 14, uninstalledBundledPacks: 4);
|
||||
|
||||
$stats = $this->service->getStats();
|
||||
|
||||
$this->assertSame(14, $stats['language_packs']['active']);
|
||||
$this->assertSame(18, $stats['language_packs']['total']);
|
||||
}
|
||||
|
||||
public function test_get_stats_calculates_change_percent_correctly_when_increased(): void
|
||||
{
|
||||
// 전체 100명, 이번달 20명 가입 → 전월 80명 대비 25% 증가
|
||||
@@ -191,6 +277,10 @@ class DashboardServiceTest extends TestCase
|
||||
$this->activityLogRepository,
|
||||
$this->moduleManager,
|
||||
$this->pluginManager,
|
||||
$this->notificationLogRepository,
|
||||
$this->templateManager,
|
||||
$this->languagePackRepository,
|
||||
$this->languagePackService,
|
||||
])->makePartial()->shouldAllowMockingProtectedMethods();
|
||||
|
||||
// CPU 조회 모킹 (실제 시스템 명령 호출 방지)
|
||||
@@ -261,6 +351,111 @@ class DashboardServiceTest extends TestCase
|
||||
$this->assertArrayHasKey('color', $resources['disk']);
|
||||
}
|
||||
|
||||
/**
|
||||
* probe 1개가 예외를 던지고 나머지는 정상값을 반환하는 부분 모킹 서비스를 생성합니다 (공개#40).
|
||||
*
|
||||
* @param string $throwingProbe 예외를 던질 probe 메서드명 (getCpuUsage|getMemoryUsage|getDiskUsage)
|
||||
* @return DashboardService 부분 모킹 서비스
|
||||
*/
|
||||
private function createPartiallyFailingResourceService(string $throwingProbe): DashboardService
|
||||
{
|
||||
$service = Mockery::mock(DashboardService::class, [
|
||||
$this->userRepository,
|
||||
$this->activityLogRepository,
|
||||
$this->moduleManager,
|
||||
$this->pluginManager,
|
||||
$this->notificationLogRepository,
|
||||
$this->templateManager,
|
||||
$this->languagePackRepository,
|
||||
$this->languagePackService,
|
||||
])->makePartial()->shouldAllowMockingProtectedMethods();
|
||||
|
||||
$normal = [
|
||||
'getCpuUsage' => ['percentage' => 45, 'color' => 'green'],
|
||||
'getMemoryUsage' => ['percentage' => 60, 'used' => '8.5 GB', 'total' => '16 GB', 'color' => 'blue'],
|
||||
'getDiskUsage' => ['percentage' => 75, 'used' => '300 GB', 'total' => '500 GB', 'color' => 'yellow'],
|
||||
];
|
||||
|
||||
foreach ($normal as $probe => $value) {
|
||||
if ($probe === $throwingProbe) {
|
||||
$service->shouldReceive($probe)->andThrow(new \ErrorException('open_basedir restriction in effect'));
|
||||
} else {
|
||||
$service->shouldReceive($probe)->andReturn($value);
|
||||
}
|
||||
}
|
||||
|
||||
return $service;
|
||||
}
|
||||
|
||||
public function test_get_system_resources_isolates_failing_cpu_probe(): void
|
||||
{
|
||||
$service = $this->createPartiallyFailingResourceService('getCpuUsage');
|
||||
|
||||
$resources = $service->getSystemResources();
|
||||
|
||||
// cpu 는 폴백, 나머지는 정상값 — 예외 비전파.
|
||||
$this->assertSame(0, $resources['cpu']['percentage']);
|
||||
$this->assertSame('gray', $resources['cpu']['color']);
|
||||
$this->assertSame(60, $resources['memory']['percentage']);
|
||||
$this->assertSame(75, $resources['disk']['percentage']);
|
||||
}
|
||||
|
||||
public function test_get_system_resources_isolates_failing_memory_probe(): void
|
||||
{
|
||||
$service = $this->createPartiallyFailingResourceService('getMemoryUsage');
|
||||
|
||||
$resources = $service->getSystemResources();
|
||||
|
||||
$this->assertSame(0, $resources['memory']['percentage']);
|
||||
$this->assertSame('gray', $resources['memory']['color']);
|
||||
$this->assertSame(__('common.unknown'), $resources['memory']['used']);
|
||||
$this->assertSame(__('common.unknown'), $resources['memory']['total']);
|
||||
$this->assertSame(45, $resources['cpu']['percentage']);
|
||||
$this->assertSame(75, $resources['disk']['percentage']);
|
||||
}
|
||||
|
||||
public function test_get_system_resources_isolates_failing_disk_probe(): void
|
||||
{
|
||||
$service = $this->createPartiallyFailingResourceService('getDiskUsage');
|
||||
|
||||
$resources = $service->getSystemResources();
|
||||
|
||||
$this->assertSame(0, $resources['disk']['percentage']);
|
||||
$this->assertSame('gray', $resources['disk']['color']);
|
||||
$this->assertSame(__('common.unknown'), $resources['disk']['used']);
|
||||
$this->assertSame(45, $resources['cpu']['percentage']);
|
||||
$this->assertSame(60, $resources['memory']['percentage']);
|
||||
}
|
||||
|
||||
public function test_get_system_resources_all_probes_fail_returns_fallback_structure(): void
|
||||
{
|
||||
$service = Mockery::mock(DashboardService::class, [
|
||||
$this->userRepository,
|
||||
$this->activityLogRepository,
|
||||
$this->moduleManager,
|
||||
$this->pluginManager,
|
||||
$this->notificationLogRepository,
|
||||
$this->templateManager,
|
||||
$this->languagePackRepository,
|
||||
$this->languagePackService,
|
||||
])->makePartial()->shouldAllowMockingProtectedMethods();
|
||||
|
||||
foreach (['getCpuUsage', 'getMemoryUsage', 'getDiskUsage'] as $probe) {
|
||||
$service->shouldReceive($probe)->andThrow(new \ErrorException('open_basedir'));
|
||||
}
|
||||
|
||||
$resources = $service->getSystemResources();
|
||||
|
||||
// 키 구조 유지 + 폴백값 — 예외 비전파.
|
||||
$this->assertArrayHasKey('cpu', $resources);
|
||||
$this->assertArrayHasKey('memory', $resources);
|
||||
$this->assertArrayHasKey('disk', $resources);
|
||||
$this->assertSame('gray', $resources['cpu']['color']);
|
||||
$this->assertSame('gray', $resources['memory']['color']);
|
||||
$this->assertSame('gray', $resources['disk']['color']);
|
||||
$this->assertSame(0, $resources['cpu']['percentage']);
|
||||
}
|
||||
|
||||
// ========================================================================
|
||||
// getRecentActivities 테스트
|
||||
// ========================================================================
|
||||
@@ -278,8 +473,8 @@ class DashboardServiceTest extends TestCase
|
||||
|
||||
public function test_get_recent_activities_formats_user_registration(): void
|
||||
{
|
||||
$mockLog = Mockery::mock(\App\Models\ActivityLog::class)->makePartial();
|
||||
$mockLog->shouldReceive('getAttribute')->with('log_type')->andReturn(\App\Enums\ActivityLogType::User);
|
||||
$mockLog = Mockery::mock(ActivityLog::class)->makePartial();
|
||||
$mockLog->shouldReceive('getAttribute')->with('log_type')->andReturn(ActivityLogType::User);
|
||||
$mockLog->shouldReceive('getAttribute')->with('action')->andReturn('register');
|
||||
$mockLog->shouldReceive('getAttribute')->with('localized_description')->andReturn('테스트 유저가 가입했습니다');
|
||||
$mockLog->shouldReceive('getAttribute')->with('actor_name')->andReturn('Test User');
|
||||
@@ -343,6 +538,36 @@ class DashboardServiceTest extends TestCase
|
||||
$this->assertIsArray($alerts);
|
||||
}
|
||||
|
||||
public function test_get_recent_notification_logs_maps_display_fields(): void
|
||||
{
|
||||
$log = new NotificationLog([
|
||||
'channel' => 'mail',
|
||||
'notification_type' => 'order_confirmed',
|
||||
'recipient_name' => '홍길동',
|
||||
'recipient_identifier' => 'test@example.com',
|
||||
'subject' => '주문이 확인되었습니다',
|
||||
'status' => NotificationLogStatus::Sent,
|
||||
'sent_at' => now(),
|
||||
]);
|
||||
$log->id = 1;
|
||||
$log->setRelation('recipientUser', null);
|
||||
|
||||
$this->notificationLogRepository
|
||||
->shouldReceive('getRecent')
|
||||
->once()
|
||||
->with(5)
|
||||
->andReturn(new EloquentCollection([$log]));
|
||||
|
||||
$result = $this->service->getRecentNotificationLogs(5);
|
||||
|
||||
$this->assertCount(1, $result);
|
||||
$this->assertSame(1, $result[0]['id']);
|
||||
$this->assertSame('order_confirmed', $result[0]['type']);
|
||||
$this->assertSame('mail', $result[0]['channel']);
|
||||
$this->assertSame('홍길동', $result[0]['recipient']);
|
||||
$this->assertSame('sent', $result[0]['status']);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
Mockery::close();
|
||||
|
||||
Reference in new issue
Block a user