Files
api/services/nginx/app/tests/Unit/Bird/BirdRequestValidationTest.php
T

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);
});