167 lines
5.3 KiB
PHP
167 lines
5.3 KiB
PHP
<?php
|
|
|
|
$_SERVER['REQUEST_URI'] = $_SERVER['REQUEST_URI'] ?? '/';
|
|
|
|
app_require('routes/birdVoiceWebhooksRoute.php');
|
|
|
|
final class BirdVoiceWebhookRouteTestDouble extends \routes\birdVoiceWebhooksRoute
|
|
{
|
|
public array $departmentNames = [];
|
|
public array $rawState = [];
|
|
public array $ttls = [];
|
|
|
|
public function mapDepartments(array $ids): array
|
|
{
|
|
return $this->buildDepartmentOptionMap($ids);
|
|
}
|
|
|
|
public function departmentPrompt(array $map): string
|
|
{
|
|
return $this->buildDepartmentPromptText($map);
|
|
}
|
|
|
|
public function gatePrompt(string $name): string
|
|
{
|
|
return $this->buildGateTypePromptText($name);
|
|
}
|
|
|
|
public function selectDepartment(array $map, string $digit): ?int
|
|
{
|
|
return $this->resolveDepartmentIdByDigit($map, $digit);
|
|
}
|
|
|
|
public function selectGateType(string $digit): ?string
|
|
{
|
|
return $this->resolveGateTypeByDigit($digit);
|
|
}
|
|
|
|
public function storeState(string $callId, array $state): void
|
|
{
|
|
$this->saveIvrState($callId, $state);
|
|
}
|
|
|
|
public function loadState(string $callId): ?array
|
|
{
|
|
return $this->readIvrState($callId);
|
|
}
|
|
|
|
public function removeState(string $callId): void
|
|
{
|
|
$this->clearIvrState($callId);
|
|
}
|
|
|
|
public function stateForCaller(array $state, int $countryCode, int $phone): bool
|
|
{
|
|
return $this->isStateForCaller($state, $countryCode, $phone);
|
|
}
|
|
|
|
public function stateKey(string $callId): string
|
|
{
|
|
return $this->ivrStateKey($callId);
|
|
}
|
|
|
|
protected function getDepartmentNameById(int $departmentId): string
|
|
{
|
|
return $this->departmentNames[$departmentId] ?? ('Afdeling ' . $departmentId);
|
|
}
|
|
|
|
protected function writeIvrStateRaw(string $key, string $value, int $ttlSeconds): void
|
|
{
|
|
$this->rawState[$key] = $value;
|
|
$this->ttls[$key] = $ttlSeconds;
|
|
}
|
|
|
|
protected function readIvrStateRaw(string $key): ?string
|
|
{
|
|
return $this->rawState[$key] ?? null;
|
|
}
|
|
|
|
protected function deleteIvrStateRaw(string $key): void
|
|
{
|
|
unset($this->rawState[$key], $this->ttls[$key]);
|
|
}
|
|
}
|
|
|
|
it('builds deterministic department digit map and caps to 9 options', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
$route->departmentNames = [11 => 'Nord', 22 => 'Syd', 33 => 'Vest'];
|
|
|
|
$map = $route->mapDepartments([11, 22, 0, -5, 33, 44, 55, 66, 77, 88, 99]);
|
|
|
|
expect($map)->toHaveCount(9);
|
|
expect(array_keys($map))->toBe([1, 2, 3, 4, 5, 6, 7, 8, 9]);
|
|
expect($map['1']['department_id'])->toBe(11);
|
|
expect($map['2']['department_id'])->toBe(22);
|
|
expect($map['3']['department_id'])->toBe(33);
|
|
expect($map['1']['department_name'])->toBe('Nord');
|
|
expect($map['2']['department_name'])->toBe('Syd');
|
|
expect($map['3']['department_name'])->toBe('Vest');
|
|
});
|
|
|
|
it('resolves department ids by selected digit', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
$map = [
|
|
'1' => ['department_id' => 101, 'department_name' => 'A'],
|
|
'2' => ['department_id' => 202, 'department_name' => 'B'],
|
|
];
|
|
|
|
expect($route->selectDepartment($map, '1'))->toBe(101);
|
|
expect($route->selectDepartment($map, '2'))->toBe(202);
|
|
expect($route->selectDepartment($map, '9'))->toBeNull();
|
|
expect($route->selectDepartment($map, ''))->toBeNull();
|
|
});
|
|
|
|
it('resolves gate type digit to entrance or exit', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
|
|
expect($route->selectGateType('1'))->toBe('entrance');
|
|
expect($route->selectGateType('2'))->toBe('exit');
|
|
expect($route->selectGateType('0'))->toBeNull();
|
|
expect($route->selectGateType('x'))->toBeNull();
|
|
});
|
|
|
|
it('stores, loads and clears ivr state with ttl', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
$callId = 'abc-123';
|
|
$state = [
|
|
'stage' => 'department_select',
|
|
'department_options' => ['1' => ['department_id' => 5, 'department_name' => 'North']],
|
|
'country_code' => 45,
|
|
'phone' => 12345678,
|
|
];
|
|
|
|
$route->storeState($callId, $state);
|
|
|
|
$key = $route->stateKey($callId);
|
|
expect($route->loadState($callId))->toBe($state);
|
|
expect($route->ttls[$key] ?? null)->toBe(600);
|
|
|
|
$route->removeState($callId);
|
|
expect($route->loadState($callId))->toBeNull();
|
|
});
|
|
|
|
it('validates state caller fingerprint matching', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
$state = ['country_code' => 45, 'phone' => 12345678];
|
|
|
|
expect($route->stateForCaller($state, 45, 12345678))->toBeTrue();
|
|
expect($route->stateForCaller($state, 46, 12345678))->toBeFalse();
|
|
expect($route->stateForCaller($state, 45, 87654321))->toBeFalse();
|
|
});
|
|
|
|
it('builds department and gate prompts', function (): void {
|
|
$route = new BirdVoiceWebhookRouteTestDouble();
|
|
|
|
$prompt = $route->departmentPrompt([
|
|
'1' => ['department_id' => 1, 'department_name' => 'Nord'],
|
|
'2' => ['department_id' => 2, 'department_name' => 'Syd'],
|
|
]);
|
|
|
|
expect($prompt)->toContain('Tast 1 for Nord');
|
|
expect($prompt)->toContain('Tast 2 for Syd');
|
|
|
|
$gatePrompt = $route->gatePrompt('Nord');
|
|
expect($gatePrompt)->toContain('Du valgte Nord.');
|
|
expect($gatePrompt)->toContain('Tast 1 for indgang. Tast 2 for udgang.');
|
|
});
|