feat(s52): fail-fast NotifierConfig + build_notifier factory (egress gate threaded)

This commit is contained in:
Kjell Tore Guttormsen 2026-07-16 19:51:57 +02:00
commit 1267f6c3eb
2 changed files with 64 additions and 1 deletions

View file

@ -10,13 +10,19 @@ import inspect
import io
import json
import pytest
from pydantic import ValidationError
from portfolio_optimiser.notify import (
ConsoleNotifier,
FileNotifier,
Notifier,
NotifierConfig,
NotifyRefused,
WebhookNotifier,
_urllib_post,
_verdict_payload,
build_notifier,
)
from portfolio_optimiser.verdicts import ProposalFeatures, Verdict, capture_verdict, verdict_to_dict
@ -105,3 +111,21 @@ def test_webhook_default_post_is_urllib_post() -> None:
the single socket path and is never called by the offline suite."""
sig = inspect.signature(WebhookNotifier.__init__)
assert sig.parameters["post"].default is _urllib_post
def test_config_fail_fast() -> None:
"""Config is fail-fast (mirrors ``GoalContract``): a webhook config without a url refuses at
validation; an unknown type refuses at the factory; and the factory alone canNOT grant egress
``allow_egress`` is a code kwarg (default False ``NotifyRefused``), never a config field."""
with pytest.raises(ValidationError):
NotifierConfig(type="webhook") # url missing → model_validator raises
with pytest.raises(ValueError, match="unknown notifier type"):
build_notifier(NotifierConfig(type="smoke-signal"))
webhook_cfg = NotifierConfig(type="webhook", url="https://hooks.example.test/T0/B0/tok")
with pytest.raises(NotifyRefused):
build_notifier(webhook_cfg) # allow_egress defaults False → fail-closed
notifier = build_notifier(webhook_cfg, allow_egress=True)
assert isinstance(notifier, WebhookNotifier)