mirror of
https://github.com/Vateron-Media/XC_VM.git
synced 2026-10-26 19:03:17 +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
3.7 KiB
PHP
101 lines
3.7 KiB
PHP
<?php
|
|
|
|
use PHPUnit\Framework\TestCase;
|
|
use XcVm\Core\Util\CounterRateSampler;
|
|
|
|
/**
|
|
* CounterRateSampler — the per-second rate of a monotonic counter (nginx's
|
|
* total request count) across processes. The watchdog runs one pass per
|
|
* process and re-execs itself, so a previous reading held in a local variable
|
|
* was always null and servers.requests_per_second was 0 on every node. The
|
|
* sampler keeps the previous reading in a state file instead.
|
|
*/
|
|
final class CounterRateSamplerTest extends TestCase {
|
|
|
|
private string $rFile;
|
|
|
|
protected function setUp(): void {
|
|
$this->rFile = sys_get_temp_dir() . '/xcvm_rate_' . uniqid('', true) . '.json';
|
|
}
|
|
|
|
protected function tearDown(): void {
|
|
foreach (glob($this->rFile . '*') ?: [] as $rPath) {
|
|
@unlink($rPath);
|
|
}
|
|
}
|
|
|
|
public function testTheFirstSampleIsZeroAndStoresTheReading(): void {
|
|
$this->assertSame(0, (new CounterRateSampler($this->rFile))->sample(1000, 100));
|
|
$this->assertFileExists($this->rFile);
|
|
}
|
|
|
|
public function testRateIsTheCounterDeltaPerSecondAcrossInstances(): void {
|
|
(new CounterRateSampler($this->rFile))->sample(1000, 100);
|
|
|
|
// A fresh instance, as a re-exec'd watchdog pass would build.
|
|
$this->assertSame(100, (new CounterRateSampler($this->rFile))->sample(1500, 105));
|
|
$this->assertSame(33, (new CounterRateSampler($this->rFile))->sample(1600, 108));
|
|
}
|
|
|
|
public function testACounterThatWentDownIsZeroAndStillStored(): void {
|
|
$rSampler = new CounterRateSampler($this->rFile);
|
|
$rSampler->sample(5000, 100);
|
|
|
|
// nginx restarted: its counter starts over.
|
|
$this->assertSame(0, $rSampler->sample(40, 104));
|
|
// The next reading is measured from the stored post-restart value.
|
|
$this->assertSame(10, $rSampler->sample(80, 108));
|
|
}
|
|
|
|
public function testTheSameTimestampIsZero(): void {
|
|
$rSampler = new CounterRateSampler($this->rFile);
|
|
$rSampler->sample(1000, 100);
|
|
|
|
$this->assertSame(0, $rSampler->sample(1500, 100));
|
|
$this->assertSame(0, $rSampler->sample(2000, 99), 'a clock that stepped back');
|
|
}
|
|
|
|
public function testAReadingOlderThanMaxAgeIsZero(): void {
|
|
$rSampler = new CounterRateSampler($this->rFile, 120);
|
|
$rSampler->sample(1000, 100);
|
|
|
|
$this->assertSame(0, $rSampler->sample(100000, 221));
|
|
$this->assertSame(10, $rSampler->sample(101200, 341), '120 s is still in range');
|
|
}
|
|
|
|
public function testTheDefaultMaxAgeIs120Seconds(): void {
|
|
$rSampler = new CounterRateSampler($this->rFile);
|
|
$rSampler->sample(1000, 100);
|
|
$this->assertSame(0, $rSampler->sample(2210, 221));
|
|
$this->assertSame(10, $rSampler->sample(3410, 341), '120 s is in range by default');
|
|
}
|
|
|
|
public function testACorruptStateFileIsZeroAndOverwritten(): void {
|
|
file_put_contents($this->rFile, 'not json {');
|
|
$rSampler = new CounterRateSampler($this->rFile);
|
|
|
|
$this->assertSame(0, $rSampler->sample(1000, 100));
|
|
$this->assertSame(100, $rSampler->sample(1500, 105));
|
|
|
|
file_put_contents($this->rFile, '{"v":"x","t":null}');
|
|
$this->assertSame(0, $rSampler->sample(2000, 110));
|
|
}
|
|
|
|
public function testAnUnwritableStateFileIsZeroWithoutAWarning(): void {
|
|
$rSampler = new CounterRateSampler(sys_get_temp_dir() . '/xcvm_missing_' . uniqid('', true) . '/state.json');
|
|
|
|
$this->assertSame(0, $rSampler->sample(1000, 100));
|
|
$this->assertSame(0, $rSampler->sample(1500, 105));
|
|
}
|
|
|
|
public function testWatchdogSamplesNginxRequestsThroughTheStateFile(): void {
|
|
$rPath = MAIN_HOME . 'Cli/Commands/WatchdogCommand.php';
|
|
$this->assertFileExists($rPath);
|
|
$rSource = (string) file_get_contents($rPath);
|
|
|
|
$this->assertStringContainsString('SystemInfo::nginxRequestCount(', $rSource);
|
|
$this->assertStringContainsString('new CounterRateSampler(TMP_PATH . ', $rSource);
|
|
$this->assertStringNotContainsString('$rLastRequests', $rSource, 'a per-process previous reading is always null');
|
|
}
|
|
}
|