Add the Bird Control Plane gateway, signed webhook ingestion, policy-gated writes, fail-closed production auto-activation, and RSA-OAEP bootstrap credential flow.
59 lines
2.0 KiB
PHP
59 lines
2.0 KiB
PHP
<?php
|
|
|
|
app_require('modules/bird/classes/bird_flow_policy_evaluator.php');
|
|
|
|
use bird\classes\bird_flow_policy_evaluator;
|
|
|
|
it('returns only deterministic allowlisted Bird Flow actions for an exact rule match', function (): void {
|
|
$policy = json_encode([
|
|
'version' => 'v1',
|
|
'rules' => [[
|
|
'id' => 'vip-whatsapp',
|
|
'enabled' => true,
|
|
'when' => [
|
|
'platform' => 'whatsapp',
|
|
'contact.segment' => 'vip',
|
|
],
|
|
'actions' => [
|
|
['type' => 'tag', 'value' => 'vip'],
|
|
['type' => 'assign', 'value' => 'support-team'],
|
|
['type' => 'snooze', 'value' => 'PT15M'],
|
|
],
|
|
]],
|
|
], JSON_THROW_ON_ERROR);
|
|
|
|
$decision = bird_flow_policy_evaluator::evaluate($policy, [
|
|
'platform' => 'whatsapp',
|
|
'contact' => ['segment' => 'vip'],
|
|
]);
|
|
|
|
expect($decision)->toMatchArray([
|
|
'decision' => 'allow',
|
|
'reason' => 'matched_rule',
|
|
'policyVersion' => 'v1',
|
|
'ruleId' => 'vip-whatsapp',
|
|
])->and($decision['actions'])->toHaveCount(3);
|
|
});
|
|
|
|
it('fails closed for absent matches, invalid policy and any non-allowlisted action', function (): void {
|
|
$unsafePolicy = json_encode([
|
|
'version' => 'v1',
|
|
'rules' => [[
|
|
'id' => 'unsafe',
|
|
'enabled' => true,
|
|
'when' => ['platform' => 'email'],
|
|
'actions' => [['type' => 'send_message', 'value' => 'hello']],
|
|
]],
|
|
], JSON_THROW_ON_ERROR);
|
|
|
|
expect(bird_flow_policy_evaluator::evaluate('', ['platform' => 'email'])['decision'])->toBe('deny')
|
|
->and(bird_flow_policy_evaluator::evaluate($unsafePolicy, ['platform' => 'email']))
|
|
->toMatchArray([
|
|
'decision' => 'deny',
|
|
'reason' => 'invalid_rule_actions',
|
|
'actions' => [],
|
|
])
|
|
->and(bird_flow_policy_evaluator::evaluate($unsafePolicy, ['platform' => 'sms'])['decision'])
|
|
->toBe('deny');
|
|
});
|