139 lines
4.2 KiB
PHP
139 lines
4.2 KiB
PHP
<?php
|
|
|
|
app_require('classes/department_wash_count_service.php');
|
|
|
|
use classes\department_wash_count_service;
|
|
|
|
final class DepartmentWashCountFakeResult
|
|
{
|
|
public int $num_rows;
|
|
private int $cursor = 0;
|
|
|
|
public function __construct(private readonly array $rows)
|
|
{
|
|
$this->num_rows = count($rows);
|
|
}
|
|
|
|
public function fetch_assoc(): ?array
|
|
{
|
|
if (!array_key_exists($this->cursor, $this->rows)) {
|
|
return null;
|
|
}
|
|
|
|
return $this->rows[$this->cursor++];
|
|
}
|
|
}
|
|
|
|
final class DepartmentWashCountFakeDb
|
|
{
|
|
public array $queries = [];
|
|
|
|
public function escape_string(string $value): string
|
|
{
|
|
return addslashes($value);
|
|
}
|
|
|
|
public function getDatabase(): string
|
|
{
|
|
return 'test_db';
|
|
}
|
|
|
|
public function query(string $sql): DepartmentWashCountFakeResult
|
|
{
|
|
$this->queries[] = $sql;
|
|
|
|
if (str_contains($sql, 'information_schema.COLUMNS') || str_contains($sql, 'information_schema.STATISTICS')) {
|
|
return new DepartmentWashCountFakeResult([['c' => 1, 'DATA_TYPE' => 'text']]);
|
|
}
|
|
|
|
if (str_contains($sql, 'COUNT(DISTINCT o.id) AS quantity')) {
|
|
return new DepartmentWashCountFakeResult([[
|
|
'quantity' => 3,
|
|
'products' => 5,
|
|
'earnings' => 250,
|
|
]]);
|
|
}
|
|
|
|
return new DepartmentWashCountFakeResult([[
|
|
'department_id' => 7,
|
|
'hour_bucket' => '2026-04-01 12:00:00',
|
|
'wash_count' => 2,
|
|
]]);
|
|
}
|
|
}
|
|
|
|
function department_wash_count_find_query(array $queries, string $needle): string
|
|
{
|
|
foreach ($queries as $query) {
|
|
if (str_contains($query, $needle)) {
|
|
return $query;
|
|
}
|
|
}
|
|
|
|
return '';
|
|
}
|
|
|
|
it('builds wash counts from order washes and completed self-serve sessions with linked-order dedupe', function (): void {
|
|
$hadDb = array_key_exists('db', $GLOBALS);
|
|
$previousDb = $GLOBALS['db'] ?? null;
|
|
$fakeDb = new DepartmentWashCountFakeDb();
|
|
$GLOBALS['db'] = $fakeDb;
|
|
|
|
try {
|
|
$service = new department_wash_count_service();
|
|
|
|
$rows = $service->countByHourForDepartments('2026-04-01 00:00:00', '2026-04-01 23:59:59', [7]);
|
|
|
|
expect($rows)->toBe([[
|
|
'department_id' => 7,
|
|
'hour_bucket' => '2026-04-01 12:00:00',
|
|
'wash_count' => 2,
|
|
]]);
|
|
|
|
$query = department_wash_count_find_query($fakeDb->queries, 'FROM selfserve_wash_sessions s');
|
|
expect($query)->toContain('FROM selfserve_wash_sessions s')
|
|
->and($query)->toContain("UPPER(TRIM(s.status)) = 'COMPLETED'")
|
|
->and($query)->toContain('s.completed_at IS NOT NULL')
|
|
->and($query)->toContain('COALESCE(linked_o.created_at, s.completed_at)')
|
|
->and($query)->toContain("CONCAT('order:', linked_o.id)")
|
|
->and($query)->toContain('GROUP BY dedupe_key, department_id');
|
|
} finally {
|
|
if ($hadDb) {
|
|
$GLOBALS['db'] = $previousDb;
|
|
} else {
|
|
unset($GLOBALS['db']);
|
|
}
|
|
}
|
|
});
|
|
|
|
it('keeps transaction totals order-based while using expanded wash counts', function (): void {
|
|
$hadDb = array_key_exists('db', $GLOBALS);
|
|
$previousDb = $GLOBALS['db'] ?? null;
|
|
$fakeDb = new DepartmentWashCountFakeDb();
|
|
$GLOBALS['db'] = $fakeDb;
|
|
|
|
try {
|
|
$service = new department_wash_count_service();
|
|
|
|
$summary = $service->transactionSummary('2026-04-01 00:00:00', '2026-04-01 23:59:59', [7]);
|
|
|
|
expect($summary)->toBe([
|
|
'quantity' => 3,
|
|
'products' => 5,
|
|
'earnings' => 250,
|
|
'washes' => 2,
|
|
]);
|
|
$summaryQuery = department_wash_count_find_query($fakeDb->queries, 'COUNT(DISTINCT o.id) AS quantity');
|
|
$countQuery = department_wash_count_find_query($fakeDb->queries, 'FROM selfserve_wash_sessions s');
|
|
expect($summaryQuery)->toContain('COUNT(DISTINCT o.id) AS quantity')
|
|
->and($summaryQuery)->not->toContain('selfserve_wash_sessions')
|
|
->and($countQuery)->toContain('selfserve_wash_sessions');
|
|
} finally {
|
|
if ($hadDb) {
|
|
$GLOBALS['db'] = $previousDb;
|
|
} else {
|
|
unset($GLOBALS['db']);
|
|
}
|
|
}
|
|
});
|