Skip to content

Commit 7db0217

Browse files
authored
Merge pull request #76 from QuantStrategyLab/codex/google-voice-alert-naming
Rename crisis alert channel to Google Voice
2 parents 3c45c33 + 3ef4b13 commit 7db0217

9 files changed

Lines changed: 71 additions & 32 deletions

.github/workflows/sync-cloud-run-env.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,9 @@ jobs:
3434
IBKR_MIN_RESERVED_CASH_USD: ${{ vars.IBKR_MIN_RESERVED_CASH_USD }}
3535
IBKR_RESERVED_CASH_RATIO: ${{ vars.IBKR_RESERVED_CASH_RATIO }}
3636
IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD: ${{ vars.IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD }}
37+
CRISIS_ALERT_GOOGLE_VOICE_TO: ${{ vars.CRISIS_ALERT_GOOGLE_VOICE_TO }}
3738
CRISIS_ALERT_EMAIL_TO: ${{ vars.CRISIS_ALERT_EMAIL_TO }}
39+
CRISIS_ALERT_SMTP_FROM: ${{ vars.CRISIS_ALERT_SMTP_FROM }}
3840
CRISIS_ALERT_EMAIL_FROM: ${{ vars.CRISIS_ALERT_EMAIL_FROM }}
3941
CRISIS_ALERT_SMTP_HOST: ${{ vars.CRISIS_ALERT_SMTP_HOST }}
4042
CRISIS_ALERT_SMTP_PORT: ${{ vars.CRISIS_ALERT_SMTP_PORT }}
@@ -364,12 +366,24 @@ jobs:
364366
remove_env_vars+=("IBKR_RESERVED_CASH_RATIO")
365367
fi
366368
369+
if [ -n "${CRISIS_ALERT_GOOGLE_VOICE_TO:-}" ]; then
370+
env_pairs+=("CRISIS_ALERT_GOOGLE_VOICE_TO=${CRISIS_ALERT_GOOGLE_VOICE_TO}")
371+
else
372+
remove_env_vars+=("CRISIS_ALERT_GOOGLE_VOICE_TO")
373+
fi
374+
367375
if [ -n "${CRISIS_ALERT_EMAIL_TO:-}" ]; then
368376
env_pairs+=("CRISIS_ALERT_EMAIL_TO=${CRISIS_ALERT_EMAIL_TO}")
369377
else
370378
remove_env_vars+=("CRISIS_ALERT_EMAIL_TO")
371379
fi
372380
381+
if [ -n "${CRISIS_ALERT_SMTP_FROM:-}" ]; then
382+
env_pairs+=("CRISIS_ALERT_SMTP_FROM=${CRISIS_ALERT_SMTP_FROM}")
383+
else
384+
remove_env_vars+=("CRISIS_ALERT_SMTP_FROM")
385+
fi
386+
373387
if [ -n "${CRISIS_ALERT_EMAIL_FROM:-}" ]; then
374388
env_pairs+=("CRISIS_ALERT_EMAIL_FROM=${CRISIS_ALERT_EMAIL_FROM}")
375389
else

README.md

Lines changed: 16 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -112,9 +112,11 @@ For IBKR, keep `paper` as a single account-group entry. If you later add live ac
112112
| `TELEGRAM_TOKEN` | Yes | Telegram bot token. For Cloud Run, prefer a Secret Manager reference instead of a literal env var. |
113113
| `GLOBAL_TELEGRAM_CHAT_ID` | Yes | Telegram chat ID used by this service. |
114114
| `NOTIFY_LANG` | No | `en` (default) or `zh` |
115-
| `CRISIS_ALERT_EMAIL_TO` | No | Comma/semicolon/newline-separated recipients for escalated crisis-plugin email alerts. Email is skipped when unset. |
116-
| `CRISIS_ALERT_EMAIL_FROM` | No | Sender address for crisis-plugin email alerts. |
117-
| `CRISIS_ALERT_SMTP_HOST` | No | SMTP host for crisis-plugin email alerts. |
115+
| `CRISIS_ALERT_GOOGLE_VOICE_TO` | No | Comma/semicolon/newline-separated Google Voice SMS gateway recipients, usually ending in `@txt.voice.google.com`. |
116+
| `CRISIS_ALERT_EMAIL_TO` | No | Optional ordinary email recipients that receive the same escalated alert; also accepted as a legacy recipient list. |
117+
| `CRISIS_ALERT_SMTP_FROM` | No | SMTP sender address for Google Voice alerts. Falls back to `CRISIS_ALERT_EMAIL_FROM`. |
118+
| `CRISIS_ALERT_EMAIL_FROM` | No | Legacy SMTP sender alias; prefer `CRISIS_ALERT_SMTP_FROM`. |
119+
| `CRISIS_ALERT_SMTP_HOST` | No | SMTP host for Google Voice alerts. |
118120
| `CRISIS_ALERT_SMTP_PORT` | No | SMTP port; defaults to `587`. |
119121
| `CRISIS_ALERT_SMTP_USERNAME` | No | Optional SMTP username. |
120122
| `CRISIS_ALERT_SMTP_PASSWORD` | No | Optional SMTP password. For Cloud Run, prefer `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME` in env sync. |
@@ -215,8 +217,8 @@ Current behavior is fail-fast:
215217
- missing account-group config source → startup error
216218
- missing key fields in the selected group (`ib_gateway_instance_name`, `ib_gateway_mode`, `ib_client_id`) → startup error
217219

218-
When `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal strategy-cycle Telegram message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends an independent crisis email when the `CRISIS_ALERT_*` SMTP settings are complete.
219-
Email alert results are written into the runtime report. Duplicate suppression uses stable plugin alert keys and stores markers under `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` when set, otherwise `EXECUTION_REPORT_GCS_URI`, with a local `/tmp` marker fallback.
220+
When `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` includes the `crisis_response_shadow` plugin, the normal strategy-cycle Telegram message still includes the compact plugin line. If the plugin signal escalates beyond `no_action` (for example `canonical_route=true_crisis`, `suggested_action=defend`/`blocked`, or `would_trade_if_enabled=true`), the service also sends an independent crisis Google Voice notification when the `CRISIS_ALERT_*` SMTP settings are complete.
221+
Google Voice alert results are written into the runtime report. Duplicate suppression uses stable plugin alert keys and stores markers under `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` when set, otherwise `EXECUTION_REPORT_GCS_URI`, with a local `/tmp` marker fallback.
220222

221223
### GitHub-managed Cloud Run env sync
222224

@@ -233,7 +235,7 @@ Recommended setup:
233235
- `ACCOUNT_GROUP` (recommended: `paper`)
234236
- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`
235237
- Optional: `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON`, `IBKR_MIN_RESERVED_CASH_USD`, `IBKR_RESERVED_CASH_RATIO`, `IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD`
236-
- Optional crisis email alerts: `CRISIS_ALERT_EMAIL_TO`, `CRISIS_ALERT_EMAIL_FROM`, `CRISIS_ALERT_SMTP_HOST`, `CRISIS_ALERT_SMTP_PORT`, `CRISIS_ALERT_SMTP_USERNAME`, `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`, `CRISIS_ALERT_SMTP_STARTTLS`, `CRISIS_ALERT_SMTP_SSL`
238+
- Optional crisis Google Voice alerts: `CRISIS_ALERT_GOOGLE_VOICE_TO`, `CRISIS_ALERT_EMAIL_TO`, `CRISIS_ALERT_SMTP_FROM`, `CRISIS_ALERT_EMAIL_FROM`, `CRISIS_ALERT_SMTP_HOST`, `CRISIS_ALERT_SMTP_PORT`, `CRISIS_ALERT_SMTP_USERNAME`, `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`, `CRISIS_ALERT_SMTP_STARTTLS`, `CRISIS_ALERT_SMTP_SSL`
237239
- `GLOBAL_TELEGRAM_CHAT_ID`
238240
- `NOTIFY_LANG`
239241
- **Repository Secrets**
@@ -364,9 +366,11 @@ IBKR 账户
364366
| `TELEGRAM_TOKEN` || Telegram 机器人 Token。Cloud Run 上更推荐走 Secret Manager 引用,不要直接写成明文 env。 |
365367
| `GLOBAL_TELEGRAM_CHAT_ID` || 这个服务使用的 Telegram Chat ID。 |
366368
| `NOTIFY_LANG` || `en`(默认)或 `zh` |
367-
| `CRISIS_ALERT_EMAIL_TO` || 危机插件升级邮件收件人,支持逗号、分号或换行分隔。不配置则跳过邮件。 |
368-
| `CRISIS_ALERT_EMAIL_FROM` || 危机插件升级邮件发件人。 |
369-
| `CRISIS_ALERT_SMTP_HOST` || 危机插件升级邮件的 SMTP host。 |
369+
| `CRISIS_ALERT_GOOGLE_VOICE_TO` || Google Voice 短信网关收件人,通常以 `@txt.voice.google.com` 结尾,支持逗号、分号或换行分隔。 |
370+
| `CRISIS_ALERT_EMAIL_TO` || 可选普通邮件收件人,会收到同一份升级告警;也作为旧版收件人配置兼容。 |
371+
| `CRISIS_ALERT_SMTP_FROM` || Google Voice 告警的 SMTP 发件人;未设置时回退到 `CRISIS_ALERT_EMAIL_FROM`|
372+
| `CRISIS_ALERT_EMAIL_FROM` || 旧版 SMTP 发件人别名;优先使用 `CRISIS_ALERT_SMTP_FROM`|
373+
| `CRISIS_ALERT_SMTP_HOST` || Google Voice 告警的 SMTP host。 |
370374
| `CRISIS_ALERT_SMTP_PORT` || SMTP 端口,默认 `587`|
371375
| `CRISIS_ALERT_SMTP_USERNAME` || 可选 SMTP 用户名。 |
372376
| `CRISIS_ALERT_SMTP_PASSWORD` || 可选 SMTP 密码。Cloud Run env sync 建议配置 `CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME`|
@@ -433,8 +437,8 @@ IB_GATEWAY_IP_MODE=internal
433437
- 没有账号组配置来源 → 启动直接报错
434438
- 选中的账号组缺少关键字段(`ib_gateway_instance_name``ib_gateway_mode``ib_client_id`)→ 启动直接报错
435439

436-
如果 `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis``suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,只要 `CRISIS_ALERT_*` SMTP 配置完整,服务还会额外发一封独立危机邮件
437-
邮件告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。
440+
如果 `IBKR_STRATEGY_PLUGIN_MOUNTS_JSON` 挂载了 `crisis_response_shadow` 插件,常规策略周期 Telegram 仍会包含插件摘要行。当插件信号升级到非 `no_action`(例如 `canonical_route=true_crisis``suggested_action=defend`/`blocked`,或 `would_trade_if_enabled=true`)时,只要 `CRISIS_ALERT_*` SMTP 配置完整,服务还会额外发一封独立 Google Voice 危机通知
441+
Google Voice 告警结果会写入 runtime report。重复发送抑制使用稳定的插件告警 key;如配置了 `STRATEGY_PLUGIN_ALERT_STATE_GCS_URI` 则写入该前缀,否则复用 `EXECUTION_REPORT_GCS_URI`,并有本地 `/tmp` marker fallback。
438442

439443
### GitHub 统一管理 Cloud Run 环境变量
440444

@@ -451,7 +455,7 @@ IB_GATEWAY_IP_MODE=internal
451455
- `ACCOUNT_GROUP`(建议设为 `paper`
452456
- `IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME`
453457
- 可选:`IBKR_STRATEGY_PLUGIN_MOUNTS_JSON``IBKR_MIN_RESERVED_CASH_USD``IBKR_RESERVED_CASH_RATIO``IBKR_SAFE_HAVEN_CASH_SUBSTITUTE_THRESHOLD_USD`
454-
- 可选危机插件邮件告警:`CRISIS_ALERT_EMAIL_TO``CRISIS_ALERT_EMAIL_FROM``CRISIS_ALERT_SMTP_HOST``CRISIS_ALERT_SMTP_PORT``CRISIS_ALERT_SMTP_USERNAME``CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME``CRISIS_ALERT_SMTP_STARTTLS``CRISIS_ALERT_SMTP_SSL`
458+
- 可选危机插件 Google Voice 告警:`CRISIS_ALERT_GOOGLE_VOICE_TO``CRISIS_ALERT_EMAIL_TO``CRISIS_ALERT_SMTP_FROM``CRISIS_ALERT_EMAIL_FROM``CRISIS_ALERT_SMTP_HOST``CRISIS_ALERT_SMTP_PORT``CRISIS_ALERT_SMTP_USERNAME``CRISIS_ALERT_SMTP_PASSWORD_SECRET_NAME``CRISIS_ALERT_SMTP_STARTTLS``CRISIS_ALERT_SMTP_SSL`
455459
- `GLOBAL_TELEGRAM_CHAT_ID`
456460
- `NOTIFY_LANG`
457461
- **仓库级 Secrets**

main.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@
2525
from decision_mapper import map_strategy_decision
2626
from entrypoints.cloud_run import is_market_open_today
2727
from notifications.telegram import build_strategy_display_name, build_translator, send_telegram_message
28-
from quant_platform_kit.notifications.strategy_plugin_email import (
29-
StrategyPluginEmailAlertMarkerStore,
30-
build_strategy_plugin_alert_context_label as build_email_alert_context_label,
31-
publish_strategy_plugin_email_alerts,
28+
from quant_platform_kit.notifications.strategy_plugin_google_voice import (
29+
StrategyPluginGoogleVoiceAlertMarkerStore,
30+
build_strategy_plugin_alert_context_label as build_google_voice_alert_context_label,
31+
publish_strategy_plugin_google_voice_alerts,
3232
)
3333
from quant_platform_kit.common.runtime_assembly import build_runtime_assembly
3434
from quant_platform_kit.common.runtime_reports import (
@@ -478,15 +478,15 @@ def build_strategy_plugin_alert_messages(signals):
478478

479479

480480
def build_strategy_plugin_alert_store():
481-
return StrategyPluginEmailAlertMarkerStore(
481+
return StrategyPluginGoogleVoiceAlertMarkerStore(
482482
local_dir=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_DIR") or "/tmp/quant_strategy_plugin_alerts",
483483
gcs_prefix_uri=os.getenv("STRATEGY_PLUGIN_ALERT_STATE_GCS_URI") or os.getenv("EXECUTION_REPORT_GCS_URI"),
484484
gcp_project_id=PROJECT_ID,
485485
)
486486

487487

488488
def build_strategy_plugin_alert_context_label() -> str:
489-
return build_email_alert_context_label(
489+
return build_google_voice_alert_context_label(
490490
platform_id="ibkr",
491491
strategy_profile=STRATEGY_PROFILE,
492492
account_scope=ACCOUNT_GROUP,
@@ -495,23 +495,23 @@ def build_strategy_plugin_alert_context_label() -> str:
495495
)
496496

497497

498-
def attach_strategy_plugin_alert_email_result(report, result) -> None:
499-
report.setdefault("summary", {})["strategy_plugin_alert_email_sent_count"] = result.sent_count
498+
def attach_strategy_plugin_alert_google_voice_result(report, result) -> None:
499+
report.setdefault("summary", {})["strategy_plugin_alert_google_voice_sent_count"] = result.sent_count
500500
report.setdefault("diagnostics", {}).update(result.to_report_fields())
501501

502502

503503
def publish_strategy_plugin_alerts(signals, *, report=None):
504-
result = publish_strategy_plugin_email_alerts(
504+
result = publish_strategy_plugin_google_voice_alerts(
505505
signals,
506-
email_settings=RUNTIME_SETTINGS,
506+
google_voice_settings=RUNTIME_SETTINGS,
507507
translator=t,
508508
strategy_label=STRATEGY_PROFILE,
509509
context_label=build_strategy_plugin_alert_context_label(),
510510
alert_store=build_strategy_plugin_alert_store(),
511511
log_message=print,
512512
)
513513
if report is not None:
514-
attach_strategy_plugin_alert_email_result(report, result)
514+
attach_strategy_plugin_alert_google_voice_result(report, result)
515515
return result
516516

517517

requirements.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
flask
22
gunicorn
3-
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@ba67541711228f5a72a294def0e5cc24cc5479f3
4-
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@305f2cc0748ec08d001deabc3add6c4eff7fe7ba
3+
quant-platform-kit @ git+https://github.com/QuantStrategyLab/QuantPlatformKit.git@8ed13d9122f52c35425b0802d22467bb6664dcd3
4+
us-equity-strategies @ git+https://github.com/QuantStrategyLab/UsEquityStrategies.git@e89ea43181f687d3454636b4b2d99ab7771546f4
55
pandas
66
numpy
77
requests

runtime_config_support.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@ class PlatformRuntimeSettings:
7272
tg_chat_id: str | None = None
7373
notify_lang: str = "en"
7474
strategy_plugin_mounts_json: str | None = None
75+
crisis_alert_google_voice_to: tuple[str, ...] = ()
76+
crisis_alert_smtp_from: str | None = None
7577
crisis_alert_email_to: tuple[str, ...] = ()
7678
crisis_alert_email_from: str | None = None
7779
crisis_alert_smtp_host: str | None = None
@@ -188,6 +190,11 @@ def load_platform_runtime_settings(
188190
os.getenv("IBKR_STRATEGY_PLUGIN_MOUNTS_JSON")
189191
or os.getenv("STRATEGY_PLUGIN_MOUNTS_JSON")
190192
),
193+
crisis_alert_google_voice_to=split_env_list(os.getenv("CRISIS_ALERT_GOOGLE_VOICE_TO")),
194+
crisis_alert_smtp_from=first_non_empty(
195+
os.getenv("CRISIS_ALERT_SMTP_FROM"),
196+
os.getenv("CRISIS_ALERT_EMAIL_FROM"),
197+
),
191198
crisis_alert_email_to=split_env_list(os.getenv("CRISIS_ALERT_EMAIL_TO")),
192199
crisis_alert_email_from=first_non_empty(os.getenv("CRISIS_ALERT_EMAIL_FROM")),
193200
crisis_alert_smtp_host=first_non_empty(os.getenv("CRISIS_ALERT_SMTP_HOST")),

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,9 @@ def load_strategy_module(**env_overrides):
3030
),
3131
"IB_ACCOUNT_GROUP_CONFIG_SECRET_NAME": None,
3232
"GLOBAL_TELEGRAM_CHAT_ID": None,
33+
"CRISIS_ALERT_GOOGLE_VOICE_TO": None,
3334
"CRISIS_ALERT_EMAIL_TO": None,
35+
"CRISIS_ALERT_SMTP_FROM": None,
3436
"CRISIS_ALERT_EMAIL_FROM": None,
3537
"CRISIS_ALERT_SMTP_HOST": None,
3638
"CRISIS_ALERT_SMTP_PORT": None,

tests/test_request_handling.py

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -54,15 +54,15 @@ def fake_publish(signals, **kwargs):
5454
return types.SimpleNamespace(
5555
sent_count=1,
5656
to_report_fields=lambda: {
57-
"strategy_plugin_alert_email_attempted_count": 1,
58-
"strategy_plugin_alert_email_sent_count": 1,
59-
"strategy_plugin_alert_email_skipped_count": 0,
60-
"strategy_plugin_alert_email_failed_count": 0,
61-
"strategy_plugin_alert_email_deliveries": [],
57+
"strategy_plugin_alert_google_voice_attempted_count": 1,
58+
"strategy_plugin_alert_google_voice_sent_count": 1,
59+
"strategy_plugin_alert_google_voice_skipped_count": 0,
60+
"strategy_plugin_alert_google_voice_failed_count": 0,
61+
"strategy_plugin_alert_google_voice_deliveries": [],
6262
},
6363
)
6464

65-
monkeypatch.setattr(strategy_module, "publish_strategy_plugin_email_alerts", fake_publish)
65+
monkeypatch.setattr(strategy_module, "publish_strategy_plugin_google_voice_alerts", fake_publish)
6666
monkeypatch.setattr(strategy_module, "run_strategy_core", lambda **_kwargs: "OK - executed")
6767

6868
with strategy_module.app.test_request_context("/", method="POST"):

tests/test_runtime_config_support.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,6 +135,8 @@ def test_load_platform_runtime_settings_uses_minimal_group_config(monkeypatch):
135135
assert settings.tg_token is None
136136
assert settings.tg_chat_id is None
137137
assert settings.strategy_plugin_mounts_json is None
138+
assert settings.crisis_alert_google_voice_to == ()
139+
assert settings.crisis_alert_smtp_from is None
138140
assert settings.crisis_alert_email_to == ()
139141
assert settings.crisis_alert_email_from is None
140142
assert settings.crisis_alert_smtp_host is None
@@ -208,11 +210,13 @@ def test_load_platform_runtime_settings_supports_explicit_group_config_values(mo
208210
assert settings.notify_lang == "zh"
209211

210212

211-
def test_load_platform_runtime_settings_reads_crisis_alert_email_config(monkeypatch):
213+
def test_load_platform_runtime_settings_reads_crisis_alert_google_voice_config(monkeypatch):
212214
monkeypatch.setenv("RUNTIME_TARGET_JSON", runtime_target_json(SAMPLE_STRATEGY_PROFILE))
213215
monkeypatch.setenv("ACCOUNT_GROUP", "paper")
214216
monkeypatch.setenv("IB_ACCOUNT_GROUP_CONFIG_JSON", MINIMAL_GROUP_JSON)
217+
monkeypatch.setenv("CRISIS_ALERT_GOOGLE_VOICE_TO", "gateway@txt.voice.google.com")
215218
monkeypatch.setenv("CRISIS_ALERT_EMAIL_TO", "risk@example.com;ops@example.com,risk@example.com")
219+
monkeypatch.setenv("CRISIS_ALERT_SMTP_FROM", "smtp-from@example.com")
216220
monkeypatch.setenv("CRISIS_ALERT_EMAIL_FROM", "bot@example.com")
217221
monkeypatch.setenv("CRISIS_ALERT_SMTP_HOST", "smtp.example.com")
218222
monkeypatch.setenv("CRISIS_ALERT_SMTP_PORT", "465")
@@ -223,6 +227,8 @@ def test_load_platform_runtime_settings_reads_crisis_alert_email_config(monkeypa
223227

224228
settings = load_platform_runtime_settings(project_id_resolver=lambda: "project-1")
225229

230+
assert settings.crisis_alert_google_voice_to == ("gateway@txt.voice.google.com",)
231+
assert settings.crisis_alert_smtp_from == "smtp-from@example.com"
226232
assert settings.crisis_alert_email_to == ("risk@example.com", "ops@example.com")
227233
assert settings.crisis_alert_email_from == "bot@example.com"
228234
assert settings.crisis_alert_smtp_host == "smtp.example.com"

0 commit comments

Comments
 (0)