182 lines
7.1 KiB
Python
182 lines
7.1 KiB
Python
from __future__ import annotations
|
||
|
||
import csv
|
||
import json
|
||
import os
|
||
import tempfile
|
||
import unittest
|
||
from pathlib import Path
|
||
from unittest.mock import patch
|
||
|
||
from common.security import TokenStore
|
||
from common import ApiError
|
||
from geo_alarm import CsvRegistrationProcessor, normalize_turkish_name
|
||
|
||
|
||
REGISTRATION_ONE = "f7587d9e-2481-4b4c-818d-c8d1946851b7"
|
||
REGISTRATION_TWO = "57d1e2ba-bf2d-4ef3-b078-523060b2000c"
|
||
ACCOUNT_ID = "2f04f1b5-1c0a-4c4e-b0a7-0ba6e9b7f2e1"
|
||
|
||
|
||
class FakeClient:
|
||
def __init__(self, env_file: Path) -> None:
|
||
self.token_store = TokenStore(env_file)
|
||
|
||
|
||
class FakeService:
|
||
def __init__(self, env_file: Path) -> None:
|
||
self.client = FakeClient(env_file)
|
||
self.payloads = []
|
||
|
||
def get_account_id(self):
|
||
return ACCOUNT_ID
|
||
|
||
def list_cities(self):
|
||
return [{"id": 6, "name": "Ankara"}, {"id": 34, "name": "İstanbul"}]
|
||
|
||
def list_districts(self, city_id):
|
||
return {
|
||
6: [{"id": 557, "name": "Çankaya"}],
|
||
34: [{"id": 347, "name": "Kadıköy"}],
|
||
}[city_id]
|
||
|
||
def create_registration(self, payload, idempotency_key):
|
||
self.payloads.append(payload)
|
||
registration_id = REGISTRATION_ONE if len(self.payloads) == 1 else REGISTRATION_TWO
|
||
warnings = ["test warning"] if len(self.payloads) == 1 else []
|
||
return 200, registration_id, warnings
|
||
|
||
|
||
class RecoveringService(FakeService):
|
||
def create_registration(self, payload, idempotency_key):
|
||
self.payloads.append(payload)
|
||
raise ApiError("duplicate", status_code=409)
|
||
|
||
def find_by_recipient_id(self, recipient_id):
|
||
return 200, REGISTRATION_ONE, []
|
||
|
||
|
||
class GeoAlarmTest(unittest.TestCase):
|
||
def test_turkish_exact_normalization(self) -> None:
|
||
self.assertEqual(normalize_turkish_name(" İSTANBUL "), "istanbul")
|
||
self.assertEqual(normalize_turkish_name("IĞDIR"), "ığdır")
|
||
self.assertEqual(normalize_turkish_name("ÇANKAYA"), "çankaya")
|
||
self.assertNotEqual(normalize_turkish_name("I"), normalize_turkish_name("İ"))
|
||
|
||
def test_csv_processing_and_webhook_transition(self) -> None:
|
||
with tempfile.TemporaryDirectory() as directory:
|
||
root = Path(directory)
|
||
env_file = root / ".env"
|
||
env_file.write_text("IKLIM_WEBHOOK_INITIALIZED=false\n", encoding="utf-8")
|
||
csv_file = root / "nowcast-geo-alarm-registrations.csv"
|
||
csv_file.write_text(
|
||
"city;district;recipientId\n"
|
||
" anKARA ; ÇANKAYA ;recipient-001\n"
|
||
"Ankara;Olmayan;recipient-invalid\n"
|
||
"İSTANBUL;KADIKÖY;recipient-002\n",
|
||
encoding="utf-8",
|
||
)
|
||
webhook_file = root / "webhook.json"
|
||
webhook_file.write_text(
|
||
json.dumps(
|
||
{
|
||
"url": "https://customer.example/webhook",
|
||
"httpMethod": "POST",
|
||
"contentType": "application/json",
|
||
"authentication": {
|
||
"selected": "BASIC",
|
||
"options": {
|
||
"BASIC": {"username": "user", "password": "pass"}
|
||
},
|
||
},
|
||
}
|
||
),
|
||
encoding="utf-8",
|
||
)
|
||
nowcast_filter_file = root / "nowcast-geo-alarm-filter.json"
|
||
nowcast_filter_file.write_text(
|
||
json.dumps({"precipitation": {"intensities": ["LIGHT"]}}),
|
||
encoding="utf-8",
|
||
)
|
||
service = FakeService(env_file)
|
||
|
||
with patch.dict(
|
||
os.environ, {"IKLIM_WEBHOOK_INITIALIZED": "false"}, clear=True
|
||
):
|
||
CsvRegistrationProcessor(
|
||
service,
|
||
csv_path=csv_file,
|
||
webhook_config_path=webhook_file,
|
||
nowcast_filter_config_path=nowcast_filter_file,
|
||
).run()
|
||
|
||
with csv_file.open("r", encoding="utf-8-sig", newline="") as handle:
|
||
rows = list(csv.DictReader(handle, delimiter=";"))
|
||
|
||
self.assertEqual(
|
||
list(rows[0]),
|
||
["city", "district", "recipientId", "status", "httpStatus", "registrationId"],
|
||
)
|
||
self.assertEqual(rows[0]["status"], "SUCCESS")
|
||
self.assertEqual(rows[0]["registrationId"], REGISTRATION_ONE)
|
||
self.assertEqual(rows[1]["status"], "FAILED")
|
||
self.assertEqual(rows[1]["httpStatus"], "NOT_SENT")
|
||
self.assertEqual(rows[2]["status"], "SUCCESS")
|
||
self.assertEqual(service.payloads[0]["webhook"]["url"], "https://customer.example/webhook")
|
||
self.assertNotIn("accountId", service.payloads[0]["webhook"])
|
||
self.assertEqual(service.payloads[1]["webhook"], {"accountId": ACCOUNT_ID})
|
||
self.assertIn("IKLIM_WEBHOOK_INITIALIZED=true", env_file.read_text())
|
||
|
||
def test_conflict_is_recovered_by_recipient_id(self) -> None:
|
||
with tempfile.TemporaryDirectory() as directory:
|
||
root = Path(directory)
|
||
env_file = root / ".env"
|
||
env_file.write_text("IKLIM_WEBHOOK_INITIALIZED=false\n", encoding="utf-8")
|
||
csv_file = root / "nowcast-geo-alarm-registrations.csv"
|
||
csv_file.write_text(
|
||
"city;district;recipientId\nAnkara;Çankaya;recipient-001\n",
|
||
encoding="utf-8",
|
||
)
|
||
webhook_file = root / "webhook.json"
|
||
webhook_file.write_text(
|
||
json.dumps(
|
||
{
|
||
"url": "https://customer.example/webhook",
|
||
"httpMethod": "POST",
|
||
"contentType": "application/json",
|
||
"authentication": {
|
||
"selected": "BASIC",
|
||
"options": {
|
||
"BASIC": {"username": "user", "password": "pass"}
|
||
},
|
||
},
|
||
}
|
||
),
|
||
encoding="utf-8",
|
||
)
|
||
nowcast_filter_file = root / "nowcast-geo-alarm-filter.json"
|
||
nowcast_filter_file.write_text(
|
||
json.dumps({"precipitation": {"intensities": ["LIGHT"]}}),
|
||
encoding="utf-8",
|
||
)
|
||
|
||
with patch.dict(
|
||
os.environ, {"IKLIM_WEBHOOK_INITIALIZED": "false"}, clear=True
|
||
):
|
||
CsvRegistrationProcessor(
|
||
RecoveringService(env_file),
|
||
csv_path=csv_file,
|
||
webhook_config_path=webhook_file,
|
||
nowcast_filter_config_path=nowcast_filter_file,
|
||
).run()
|
||
|
||
with csv_file.open("r", encoding="utf-8-sig", newline="") as handle:
|
||
row = next(csv.DictReader(handle, delimiter=";"))
|
||
self.assertEqual(row["status"], "SUCCESS")
|
||
self.assertEqual(row["httpStatus"], "HTTP_409")
|
||
self.assertEqual(row["registrationId"], REGISTRATION_ONE)
|
||
|
||
|
||
if __name__ == "__main__":
|
||
unittest.main()
|