toBeEmpty() ->and($verdict['log'])->toHaveCount(1) ->and($verdict['log'][0])->toContain('invalid notification payload'); // the function must still reach sql_save() afterwards instead of fataling. expect($verdict['sql_save'])->toHaveCount(1); }); test('a per-type entry that decodes to a non-array is logged and skipped, not dispatched', function () use ($probe) { $verdict = cacti_test_isolated_probe($probe, [json_encode([ 'email' => 'attacker-controlled-string-not-an-array', ])]); expect($verdict['mailer_calls'])->toBeEmpty() ->and($verdict['log'])->toHaveCount(1) ->and($verdict['log'][0])->toContain("invalid notification data for type 'email'"); expect($verdict['sql_save'])->toHaveCount(1); }); test('a well-formed email notification still dispatches to mailer() with the right recipients', function () use ($probe) { $verdict = cacti_test_isolated_probe($probe, [json_encode([ 'email' => [ 'to_email' => 'ops@example.com', 'cc_email' => 'lead@example.com', ], ])]); expect($verdict['log'])->toBeEmpty() ->and($verdict['mailer_calls'])->toHaveCount(1) ->and($verdict['mailer_calls'][0]['to'])->toBe('ops@example.com') ->and($verdict['mailer_calls'][0]['cc'])->toBe('lead@example.com'); expect($verdict['sql_save'])->toHaveCount(1); }); test('mixed valid and malformed entries in the same payload are handled independently', function () use ($probe) { $verdict = cacti_test_isolated_probe($probe, [json_encode([ 'email' => ['to_email' => 'ops@example.com'], 'unknown' => 'not-an-array', ])]); expect($verdict['mailer_calls'])->toHaveCount(1) ->and($verdict['log'])->toHaveCount(1) ->and($verdict['log'][0])->toContain("invalid notification data for type 'unknown'"); });