mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-25 11:03:14 +01:00
Review follow-up to the bandwidth/rps/producers fix. The nginx stub_status parse moves into SystemInfo::nginxRequestCount(), so the requests field the watchdog samples is tested against a real status body and an unreachable one (no count, rps 0). The getStats() test now pins the whole array_replace statement (a `+` would leave the bandwidth keys at 0), and the sampler test checks that 120 s is in range by default. No behaviour change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQHeypQpfAXTdCysmi7b6a
101 lines
4.0 KiB
PHP
101 lines
4.0 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Core\Util\SystemInfo;
|
|
|
|
/**
|
|
* SystemInfo::aggregateNetwork — the bandwidth figures of the watchdog
|
|
* heartbeat and of servers_stats. The per-second rates were summed over every
|
|
* selected interface, but the lifetime totals were assigned, so only the last
|
|
* interface was kept, and the received total read tx_bytes. Driven against a
|
|
* fixture /sys/class/net tree.
|
|
*/
|
|
final class SystemInfoNetworkTest extends TestCase {
|
|
|
|
private string $rRoot;
|
|
|
|
protected function setUp(): void {
|
|
$this->rRoot = sys_get_temp_dir() . '/xcvm_sysnet_' . uniqid('', true) . '/';
|
|
$this->iface('eth0', 1000, 10, '1000');
|
|
$this->iface('eth1', 2000, 20, '10000');
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
foreach (glob($this->rRoot . '*/statistics/*') ?: [] as $rFile) {
|
|
unlink($rFile);
|
|
}
|
|
foreach (glob($this->rRoot . '*/speed') ?: [] as $rFile) {
|
|
unlink($rFile);
|
|
}
|
|
foreach (glob($this->rRoot . '*/statistics') ?: [] as $rDir) {
|
|
rmdir($rDir);
|
|
}
|
|
foreach (glob($this->rRoot . '*') ?: [] as $rDir) {
|
|
rmdir($rDir);
|
|
}
|
|
@rmdir($this->rRoot);
|
|
}
|
|
|
|
private function iface(string $rName, ?int $rTx, ?int $rRx, ?string $rSpeed): void {
|
|
mkdir($this->rRoot . $rName . '/statistics', 0755, true);
|
|
if ($rTx !== null) {
|
|
file_put_contents($this->rRoot . $rName . '/statistics/tx_bytes', $rTx . "\n");
|
|
}
|
|
if ($rRx !== null) {
|
|
file_put_contents($this->rRoot . $rName . '/statistics/rx_bytes', $rRx . "\n");
|
|
}
|
|
if ($rSpeed !== null) {
|
|
file_put_contents($this->rRoot . $rName . '/speed', $rSpeed . "\n");
|
|
}
|
|
}
|
|
|
|
private static function rates(int $rIn, int $rOut): array {
|
|
return ['in_bytes' => $rIn, 'in_packets' => 0, 'in_errors' => 0, 'out_bytes' => $rOut, 'out_packets' => 0, 'out_errors' => 0];
|
|
}
|
|
|
|
public function testTotalsAreSummedOverTheSelectedInterfaces(): void {
|
|
$rNetwork = SystemInfo::aggregateNetwork(['eth0' => self::rates(1, 2), 'eth1' => self::rates(3, 4)], $this->rRoot);
|
|
|
|
$this->assertSame(6, $rNetwork['bytes_sent']);
|
|
$this->assertSame(4, $rNetwork['bytes_received']);
|
|
$this->assertSame(3000, $rNetwork['bytes_sent_total']);
|
|
$this->assertSame(30, $rNetwork['bytes_received_total'], 'received is rx_bytes, not tx_bytes');
|
|
}
|
|
|
|
public function testNetworkSpeedIsTheFirstPositiveSpeed(): void {
|
|
$rNetwork = SystemInfo::aggregateNetwork(['eth0' => self::rates(1, 2), 'eth1' => self::rates(3, 4)], $this->rRoot);
|
|
$this->assertSame(1000, $rNetwork['network_speed']);
|
|
|
|
// A down or virtual interface reports -1 (or nothing); the next one counts.
|
|
$this->iface('veth0', 5, 5, '-1');
|
|
$rNetwork = SystemInfo::aggregateNetwork(['veth0' => self::rates(0, 0), 'eth1' => self::rates(0, 0)], $this->rRoot);
|
|
$this->assertSame(10000, $rNetwork['network_speed']);
|
|
}
|
|
|
|
public function testReturnsTheHeartbeatKeysInOrder(): void {
|
|
$this->assertSame(
|
|
['bytes_sent', 'bytes_sent_total', 'bytes_received', 'bytes_received_total', 'network_speed'],
|
|
array_keys(SystemInfo::aggregateNetwork([], $this->rRoot))
|
|
);
|
|
$this->assertSame([0, 0, 0, 0, 0], array_values(SystemInfo::aggregateNetwork([], $this->rRoot)));
|
|
}
|
|
|
|
public function testAMissingStatisticsFileCountsAsZero(): void {
|
|
$this->iface('eth2', null, 7, null);
|
|
$rNetwork = SystemInfo::aggregateNetwork(['eth2' => self::rates(1, 1), 'gone0' => self::rates(1, 1)], $this->rRoot);
|
|
|
|
$this->assertSame(0, $rNetwork['bytes_sent_total']);
|
|
$this->assertSame(7, $rNetwork['bytes_received_total']);
|
|
$this->assertSame(0, $rNetwork['network_speed']);
|
|
$this->assertSame(2, $rNetwork['bytes_sent']);
|
|
}
|
|
|
|
public function testGetStatsTakesItsBandwidthFromAggregateNetwork(): void {
|
|
$rSource = (string) file_get_contents(MAIN_HOME . 'Core/Util/SystemInfo.php');
|
|
|
|
// array_replace, not `+`: the keys already exist (as 0) and must be overwritten in place.
|
|
$this->assertStringContainsString("\$rJSON = array_replace(\$rJSON, self::aggregateNetwork(\$rJSON['network_info']));", $rSource);
|
|
$this->assertStringNotContainsString("\$rJSON['bytes_sent_total'] = (", $rSource, 'the total must not be assigned per interface');
|
|
}
|
|
}
|