137 lines
4.6 KiB
PHP
137 lines
4.6 KiB
PHP
<?php
|
|
|
|
app_require('modules/bird/helpers/bird_request_validator.php');
|
|
app_require('modules/bird/helpers/bird_request_schemas.php');
|
|
app_require('traits/bird_route_validation_t.php');
|
|
|
|
use bird\helpers\bird_request_schemas;
|
|
use bird\helpers\bird_request_validator;
|
|
use traits\bird_route_validation_t;
|
|
|
|
if (!class_exists('BirdRouteValidationHarness')) {
|
|
class BirdRouteValidationHarness
|
|
{
|
|
use bird_route_validation_t {
|
|
birdValidateSchema as public validateSchema;
|
|
}
|
|
|
|
public int $forwardCalls = 0;
|
|
|
|
public function validateThenForward(array $payload, array $schema): void
|
|
{
|
|
$this->validateSchema($payload, $schema);
|
|
$this->forwardCalls++;
|
|
}
|
|
}
|
|
}
|
|
|
|
beforeEach(function (): void {
|
|
global $response;
|
|
|
|
$response = new class {
|
|
public function error(mixed $data, int $status = null): void
|
|
{
|
|
$encoded = is_string($data) ? $data : json_encode($data);
|
|
throw new RuntimeException('HTTP_' . (string)$status . ':' . $encoded);
|
|
}
|
|
};
|
|
});
|
|
|
|
it('accepts valid payloads for create and nested call flow commands', function (): void {
|
|
$payload = [
|
|
'from' => '+4532330288',
|
|
'to' => '+4542331128',
|
|
'ringTimeout' => 20,
|
|
'notification' => ['url' => 'https://example.com/webhook'],
|
|
'callFlow' => [
|
|
[
|
|
'command' => 'say',
|
|
'conditions' => [
|
|
['variable' => 'keys', 'operator' => 'eq', 'value' => '1'],
|
|
],
|
|
'options' => ['text' => 'hello'],
|
|
],
|
|
],
|
|
];
|
|
|
|
$errors = bird_request_validator::validate($payload, bird_request_schemas::voiceCreateCallBody());
|
|
|
|
expect($errors)->toBe([]);
|
|
});
|
|
|
|
it('rejects unknown fields and invalid enum values in strict schemas', function (): void {
|
|
$unknownErrors = bird_request_validator::validate(
|
|
['to' => '+4542331128', 'unknownField' => 'x'],
|
|
bird_request_schemas::voiceCreateCallBody()
|
|
);
|
|
$enumErrors = bird_request_validator::validate(
|
|
['cause' => 'completed'],
|
|
bird_request_schemas::voiceHangupBody()
|
|
);
|
|
|
|
expect($unknownErrors)->not->toBe([]);
|
|
expect(implode(' | ', $unknownErrors))->toContain('$.unknownField is not allowed');
|
|
|
|
expect($enumErrors)->not->toBe([]);
|
|
expect(implode(' | ', $enumErrors))->toContain('$.cause must be one of: rejected, busy');
|
|
});
|
|
|
|
it('supports CSV normalization in schema validation for log filters', function (): void {
|
|
$validQuery = [
|
|
'channelId' => '2cbde4fd-8899-4f2d-95ea-2ab7cc6b8c97,5a6bd8c1-32b7-4a03-8b5a-b6a4a9d7a8b2',
|
|
'tag' => 'north,vip',
|
|
];
|
|
$invalidQuery = [
|
|
'channelId' => 'not-a-uuid',
|
|
];
|
|
|
|
$validErrors = bird_request_validator::validate($validQuery, bird_request_schemas::voiceCallsLogQuery());
|
|
$invalidErrors = bird_request_validator::validate($invalidQuery, bird_request_schemas::voiceCallsLogQuery());
|
|
|
|
expect($validErrors)->toBe([]);
|
|
expect($invalidErrors)->not->toBe([]);
|
|
expect(implode(' | ', $invalidErrors))->toContain('$.channelId[0] must be a UUID');
|
|
});
|
|
|
|
it('validates flash hangup payloads for both supported request shapes', function (): void {
|
|
$resultMode = [
|
|
'result' => 'verified',
|
|
'receivedCli' => '+4542331128',
|
|
];
|
|
$numbersMode = [
|
|
'from' => '+4532330288',
|
|
'to' => '+4542331128',
|
|
'result' => 'verified',
|
|
];
|
|
$invalid = [
|
|
'result' => 'ok',
|
|
];
|
|
|
|
$resultErrors = bird_request_validator::validate($resultMode, bird_request_schemas::flashHangupBody());
|
|
$numbersErrors = bird_request_validator::validate($numbersMode, bird_request_schemas::flashHangupBody());
|
|
$invalidErrors = bird_request_validator::validate($invalid, bird_request_schemas::flashHangupBody());
|
|
|
|
expect($resultErrors)->toBe([]);
|
|
expect($numbersErrors)->toBe([]);
|
|
expect($invalidErrors)->not->toBe([]);
|
|
expect(implode(' | ', $invalidErrors))->toContain('does not match any of the allowed schemas');
|
|
});
|
|
|
|
it('fails before forward step when strict validation fails and forwards when valid', function (): void {
|
|
$harness = new BirdRouteValidationHarness();
|
|
|
|
try {
|
|
$harness->validateThenForward(['unexpected' => true], bird_request_schemas::noBody());
|
|
$caught = null;
|
|
} catch (RuntimeException $e) {
|
|
$caught = $e;
|
|
}
|
|
|
|
expect($caught)->toBeInstanceOf(RuntimeException::class);
|
|
expect($caught?->getMessage())->toContain('HTTP_400');
|
|
expect($harness->forwardCalls)->toBe(0);
|
|
|
|
$harness->validateThenForward([], bird_request_schemas::noBody());
|
|
expect($harness->forwardCalls)->toBe(1);
|
|
});
|