File: /home/xedaptot/ai.naniguide.com/tests/Feature/CustomerSendingServerServiceTest.php
<?php
/**
* Feature test for the customer-side SendingServerService — read aggregator
* powering the rich-redesigned /rui/sending/servers page.
*/
use App\Model\Customer;
use App\Model\SendingServer;
use App\Services\Customer\SendingServerService;
uses(\Tests\TestCase::class);
beforeEach(function () {
$hostId = SendingServer::query()->whereNotNull('customer_id')->value('customer_id');
$this->customer = $hostId
? Customer::find($hostId)
: Customer::first();
if (! $this->customer) {
$this->markTestSkipped('No customer in DB — run DemoSeeder first');
}
$this->service = app(SendingServerService::class);
});
test('headerMeta returns 5-key shape with integers', function () {
$meta = $this->service->headerMeta($this->customer);
expect($meta)->toHaveKeys(['total', 'active', 'inactive', 'warmup', 'vendors']);
foreach (['total', 'active', 'inactive', 'warmup', 'vendors'] as $k) {
expect($meta[$k])->toBeInt();
}
expect($meta['active'] + $meta['inactive'])->toBe($meta['total']);
});
test('sidebarStats DTO has all five pre-shaped payloads', function () {
$dto = $this->service->sidebarStats($this->customer);
expect($dto)->toBeInstanceOf(\App\Dto\Refactor\SendingServer\SendingServerSidebarStatsDto::class);
expect($dto->headerMeta)->toHaveKeys(['total', 'active', 'inactive', 'warmup', 'vendors']);
expect($dto->vendorMix)->toHaveKeys(['slices', 'total']);
expect($dto->vendorMix['slices'])->toBeArray();
foreach ($dto->vendorMix['slices'] as $s) {
expect($s)->toHaveKeys(['key', 'name', 'count']);
expect($s['count'])->toBeInt();
}
expect($dto->throughput)->toHaveKeys(['total_per_hour', 'formatted', 'by_vendor']);
expect($dto->throughput['total_per_hour'])->toBeInt();
expect($dto->warmupStatus)->toHaveKeys(['enabled', 'disabled', 'started']);
foreach (['enabled', 'disabled', 'started'] as $k) {
expect($dto->warmupStatus[$k])->toBeInt();
}
expect($dto->coverage)->toHaveKeys(['used_vendor_keys', 'catalog']);
});
test('vendorMix slice counts sum to total', function () {
$dto = $this->service->sidebarStats($this->customer);
$sum = array_sum(array_column($dto->vendorMix['slices'], 'count'));
expect($sum)->toBe($dto->vendorMix['total']);
});
test('throughput by_vendor sum matches total_per_hour', function () {
$dto = $this->service->sidebarStats($this->customer);
if (empty($dto->throughput['by_vendor'])) {
$this->markTestSkipped('No throughput data');
}
$sum = array_sum(array_column($dto->throughput['by_vendor'], 'per_hour'));
expect($sum)->toBe($dto->throughput['total_per_hour']);
});
test('coverage catalog lists connected first', function () {
$dto = $this->service->sidebarStats($this->customer);
$sawDisconnected = false;
foreach ($dto->coverage['catalog'] as $row) {
if (! $row['connected']) { $sawDisconnected = true; continue; }
expect($sawDisconnected)->toBeFalse();
}
});
test('list honours status=active', function () {
$list = $this->service->list($this->customer, null, 'active');
foreach ($list->items() as $server) {
expect($server->status)->toBe(SendingServer::STATUS_ACTIVE);
}
});
test('list honours type filter', function () {
$first = SendingServer::where('customer_id', $this->customer->id)->first();
if (! $first) {
$this->markTestSkipped('No servers for this customer');
}
$list = $this->service->list($this->customer, null, null, $first->type);
foreach ($list->items() as $server) {
expect($server->type)->toBe($first->type);
}
});
test('list rejects unknown sort column', function () {
$list = $this->service->list($this->customer, null, null, null, 'pwned_column', 'asc');
expect($list)->toBeInstanceOf(\Illuminate\Contracts\Pagination\LengthAwarePaginator::class);
});
test('GET /rui/sending/servers renders 200 with rich-redesign markers', function () {
$user = $this->customer->user;
if (! $user) {
$this->markTestSkipped('No user attached to customer');
}
$resp = $this->actingAs($user)->get(route('refactor.sending.servers'));
if ($resp->status() === 302) {
$this->markTestSkipped('Customer plan blocks own sending server (entitlement gate)');
}
$resp->assertStatus(200);
$resp->assertSee('mc-banner mc-banner-open', false);
$resp->assertSee('mc-banner-meta', false);
$resp->assertSee('mc-page-grid', false);
$resp->assertSee('mc-page-rail', false);
$resp->assertSee('rui-servers-page-grid', false);
$resp->assertSee('rui-servers-rail-card', false);
$resp->assertSee('data-rui-servers-mix', false);
});
test('GET listing renders rich row partial', function () {
$user = $this->customer->user;
if (! $user) {
$this->markTestSkipped('No user attached to customer');
}
$resp = $this->actingAs($user)->get(route('refactor.sending.servers_listing'));
if ($resp->status() === 302) {
$this->markTestSkipped('Customer plan blocks own sending server');
}
$resp->assertStatus(200);
if (SendingServer::where('customer_id', $this->customer->id)->count() > 0) {
$resp->assertSee('rui-servers-list-name-cell', false);
}
});