mirror of
https://github.com/zvx-echo6/meshai.git
synced 2026-08-26 17:31:34 +00:00
- Move all application source (meshai/, dashboard-frontend/, tests/, config/, docs/, Dockerfile, etc.) into work/ directory - Add Node.js multi-stage build to Dockerfile for frontend compilation; remove compiled static assets from git tracking - Fix satpass missing time windows: consolidation was splitting wire on newline and only putting line 1 in event.title, dropping the time window line that the composer uses for precomposed broadcasts - Fix satpass burst flooding: stagger consolidation timers (+60s per pending pass) so Central batch publishes don't blast the mesh - Update CI workflow build context to work/ - Anchor lib/ and data/ gitignore patterns to repo root to prevent false matches on nested directories - Add dashboard-frontend/node_modules/ to .dockerignore Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
63 lines
2.3 KiB
Python
63 lines
2.3 KiB
Python
"""Phase 2.16.1: lock in notification-rule coercion in the config loader path.
|
|
|
|
Regression guard for the bug where the generic nested-dataclass handler in
|
|
_dict_to_dataclass shadowed the explicit 'notifications' branch, leaving
|
|
cfg.notifications.rules as raw dicts (which crashed Dispatcher._matching_rules
|
|
on rule.enabled). config_loader.load_config uses this same _dict_to_dataclass.
|
|
"""
|
|
|
|
from meshai.config import Config, NotificationRuleConfig, _dict_to_dataclass
|
|
|
|
|
|
def test_multifile_load_coerces_notification_rules():
|
|
"""notifications.rules dicts are coerced to NotificationRuleConfig."""
|
|
data = {
|
|
"notifications": {
|
|
"enabled": True,
|
|
"rules": [
|
|
{
|
|
"name": "Test Rule",
|
|
"enabled": True,
|
|
"trigger_type": "condition",
|
|
"categories": ["earthquake_event"],
|
|
"min_severity": "routine",
|
|
"delivery_type": "mesh_broadcast",
|
|
},
|
|
{
|
|
"name": "Second Rule",
|
|
"enabled": False,
|
|
"trigger_type": "condition",
|
|
"categories": ["wildfire_incident"],
|
|
"delivery_type": "email",
|
|
},
|
|
],
|
|
}
|
|
}
|
|
cfg = _dict_to_dataclass(Config, data)
|
|
rules = cfg.notifications.rules
|
|
assert len(rules) == 2
|
|
# Coerced to the dataclass, NOT left as dicts.
|
|
assert all(isinstance(r, NotificationRuleConfig) for r in rules)
|
|
# Attribute access (what Dispatcher._matching_rules needs) works.
|
|
assert rules[0].enabled is True
|
|
assert rules[0].name == "Test Rule"
|
|
assert rules[1].enabled is False
|
|
|
|
|
|
def test_rules_attribute_access_does_not_raise():
|
|
"""Dispatcher-style attribute access on every rule succeeds."""
|
|
data = {
|
|
"notifications": {
|
|
"rules": [
|
|
{"name": "R", "enabled": True, "trigger_type": "condition",
|
|
"categories": ["earthquake_event"], "min_severity": "immediate"},
|
|
]
|
|
}
|
|
}
|
|
cfg = _dict_to_dataclass(Config, data)
|
|
for r in cfg.notifications.rules:
|
|
# These are the accesses Dispatcher._matching_rules performs.
|
|
_ = r.enabled
|
|
_ = r.trigger_type
|
|
_ = r.categories
|
|
_ = r.min_severity
|