Add tests for the Slack interactions endpoint

This commit is contained in:
Adam Moussa 2026-05-29 14:33:26 -04:00
parent 817e1f6a74
commit 63b6f11bf6

331
tests/test_interactions.py Normal file
View file

@ -0,0 +1,331 @@
"""Tests for the Slack interactions Lambda endpoint.
Covers: signature verification, drill_category modal, drill_site modal,
base64-encoded bodies, non-block_actions payloads, and missing-field no-ops.
"""
from __future__ import annotations
import base64
import json
from urllib.parse import urlencode
def _make_event(
payload_dict: dict,
*,
timestamp: str = "1234567890",
signature: str = "v0=fakesig",
base64_encoded: bool = False,
) -> dict:
"""Build a realistic API Gateway HTTP API event for Slack interactions."""
raw_payload = urlencode({"payload": json.dumps(payload_dict)})
body: str
if base64_encoded:
body = base64.b64encode(raw_payload.encode("utf-8")).decode("utf-8")
else:
body = raw_payload
return {
"headers": {
"x-slack-request-timestamp": timestamp,
"x-slack-signature": signature,
"content-type": "application/x-www-form-urlencoded",
},
"body": body,
"isBase64Encoded": base64_encoded,
}
def _block_actions_payload(
action_id: str,
value: str,
trigger_id: str = "trigger123",
) -> dict:
return {
"type": "block_actions",
"trigger_id": trigger_id,
"actions": [
{
"action_id": action_id,
"value": value,
}
],
}
# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------
def test_invalid_signature_returns_401_and_no_views_open(monkeypatch):
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: False)
views_open_calls = []
fake_client = type(
"C", (), {"views_open": lambda self, **kw: views_open_calls.append(kw)}
)
monkeypatch.setattr(slackio, "web_client", lambda: fake_client())
event = _make_event(
_block_actions_payload(
"drill_category:Report / Docs Needed", "Report / Docs Needed"
)
)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 401
assert "invalid" in resp["body"].lower()
assert views_open_calls == []
def test_drill_category_opens_modal_with_matching_wos(monkeypatch):
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
monkeypatch.setattr(
slackio, "get_dashboard_url", lambda: "https://grafana.example.com"
)
details = [
{"wo_number": "WO-001", "category": "Report / Docs Needed", "site": "ABQ5"},
{"wo_number": "WO-002", "category": "3rd Escalation", "site": "ACY9"},
{"wo_number": "WO-003", "category": "Report / Docs Needed", "site": "ABQ5"},
]
monkeypatch.setattr(slackio, "read_meta_json", lambda dt, name: details)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
event = _make_event(
_block_actions_payload(
"drill_category:Report / Docs Needed", "Report / Docs Needed"
)
)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert len(views_open_calls) == 1
call = views_open_calls[0]
assert call["trigger_id"] == "trigger123"
view = call["view"]
# Title must reflect the count (2 matching WOs)
assert "2" in view["title"]["text"]
assert "Report / Docs Needed" in view["title"]["text"]
def test_drill_site_filters_on_site_field(monkeypatch):
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
monkeypatch.setattr(
slackio, "get_dashboard_url", lambda: "https://grafana.example.com"
)
details = [
{"wo_number": "WO-001", "category": "Report / Docs Needed", "site": "ABQ5"},
{"wo_number": "WO-002", "category": "3rd Escalation", "site": "ACY9"},
{"wo_number": "WO-003", "category": "Awaiting Scheduling", "site": "ABQ5"},
]
monkeypatch.setattr(slackio, "read_meta_json", lambda dt, name: details)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
event = _make_event(_block_actions_payload("drill_site:ABQ5", "ABQ5"))
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert len(views_open_calls) == 1
view = views_open_calls[0]["view"]
# 2 WOs match site ABQ5
assert "2" in view["title"]["text"]
assert "ABQ5" in view["title"]["text"]
def test_base64_encoded_body_is_decoded(monkeypatch):
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
monkeypatch.setattr(
slackio, "get_dashboard_url", lambda: "https://grafana.example.com"
)
monkeypatch.setattr(
slackio,
"read_meta_json",
lambda dt, name: [
{"wo_number": "WO-010", "category": "3rd Escalation", "site": "ACY9"}
],
)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
event = _make_event(
_block_actions_payload("drill_category:3rd Escalation", "3rd Escalation"),
base64_encoded=True,
)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert len(views_open_calls) == 1
def test_non_block_actions_type_returns_200_no_views_open(monkeypatch):
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
# "view_submission" is a valid Slack payload type but not block_actions
event = _make_event({"type": "view_submission"})
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert views_open_calls == []
def test_unknown_action_kind_returns_200_no_views_open(monkeypatch):
"""action_id prefix not in _FILTER_FIELD → no-op."""
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
payload = {
"type": "block_actions",
"trigger_id": "tid",
"actions": [{"action_id": "open_dashboard", "value": "something"}],
}
event = _make_event(payload)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert views_open_calls == []
def test_missing_value_returns_200_no_views_open(monkeypatch):
"""action has a valid kind but missing value → no-op."""
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
payload = {
"type": "block_actions",
"trigger_id": "tid",
"actions": [
{"action_id": "drill_category:Report / Docs Needed"}
], # no value key
}
event = _make_event(payload)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert views_open_calls == []
def test_missing_trigger_id_returns_200_no_views_open(monkeypatch):
"""Valid action but no trigger_id → no-op."""
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
payload = {
"type": "block_actions",
# trigger_id absent
"actions": [
{
"action_id": "drill_category:Report / Docs Needed",
"value": "Report / Docs Needed",
}
],
}
event = _make_event(payload)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert views_open_calls == []
def test_empty_details_json_shows_zero_wos_in_modal(monkeypatch):
"""Details list is empty → views_open still called, modal title shows 0."""
import interactions
import slackio
monkeypatch.setattr(slackio, "verify_signature", lambda body, ts, sig: True)
monkeypatch.setattr(
slackio, "get_dashboard_url", lambda: "https://grafana.example.com"
)
monkeypatch.setattr(slackio, "read_meta_json", lambda dt, name: [])
views_open_calls = []
fake_client = type(
"C",
(),
{"views_open": lambda self, **kw: views_open_calls.append(kw)},
)()
monkeypatch.setattr(slackio, "web_client", lambda: fake_client)
event = _make_event(
_block_actions_payload("drill_category:3rd Escalation", "3rd Escalation")
)
resp = interactions.handler(event, None)
assert resp["statusCode"] == 200
assert len(views_open_calls) == 1
assert "(0)" in views_open_calls[0]["view"]["title"]["text"]