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()