""" Unit Tests — Webhook Server """ import hashlib import hmac import base64 import pytest from fastapi.testclient import TestClient from unittest.mock import AsyncMock, patch # ─── Signature Helper ────────────────────────────────────────────────────────── def make_signature(body: bytes, secret: str) -> str: h = hmac.new(secret.encode(), body, hashlib.sha256).digest() return base64.b64encode(h).decode() # ─── Fixture: mock settings ─────────────────────────────────────────────────── @pytest.fixture(autouse=True) def mock_settings(monkeypatch): monkeypatch.setenv("LINE_CHANNEL_SECRET", "test_secret") monkeypatch.setenv("LINE_CHANNEL_ACCESS_TOKEN", "test_token") monkeypatch.setenv("HF_API_TOKEN", "hf_test") monkeypatch.setenv("DATABASE_URL", "postgresql://u:p@localhost/test") monkeypatch.setenv("REDIS_URL", "redis://localhost:6379") # ─── Tests ──────────────────────────────────────────────────────────────────── class TestSignatureVerification: def test_valid_signature(self): from app.main import verify_line_signature body = b'{"test": "data"}' sig = make_signature(body, "test_secret") assert verify_line_signature(body, sig) is True def test_invalid_signature(self): from app.main import verify_line_signature body = b'{"test": "data"}' assert verify_line_signature(body, "wrong_sig") is False def test_empty_signature(self): from app.main import verify_line_signature assert verify_line_signature(b"body", "") is False class TestPayloadParsing: def test_parse_text_message(self): from app.models import LineWebhookPayload raw = """{ "destination": "U123", "events": [{ "type": "message", "timestamp": 1700000000000, "replyToken": "token123", "source": {"type": "user", "userId": "Uabc"}, "message": {"id": "m1", "type": "text", "text": "สวัสดีครับ"} }] }""" payload = LineWebhookPayload.model_validate_json(raw) assert len(payload.events) == 1 assert payload.events[0].message.text == "สวัสดีครับ" def test_skip_non_text_event(self): from app.models import LineWebhookPayload raw = """{ "destination": "U123", "events": [{ "type": "follow", "timestamp": 1700000000000, "source": {"type": "user", "userId": "Uabc"} }] }""" payload = LineWebhookPayload.model_validate_json(raw) assert payload.events[0].type == "follow" assert payload.events[0].message is None class TestSentimentResult: def test_label_mapping_positive(self): from app.models import SentimentResult r = SentimentResult( label="positive", score=0.91, raw_scores={"positive": 0.91, "neutral": 0.07, "negative": 0.02}, ) assert r.label == "positive" assert r.score == 0.91 def test_fallback_is_neutral(self): from app.sentiment import _fallback_result r = _fallback_result() assert r.label == "neutral" assert r.score == 0.0