feat: add cdr validation and normalization domain
This commit is contained in:
77
tests/unit/test_normalization.py
Normal file
77
tests/unit/test_normalization.py
Normal file
@@ -0,0 +1,77 @@
|
||||
from core.domain.normalization import generate_cdr_id, normalize_cdr, normalize_rat
|
||||
from core.domain.validation import parse_raw_cdr
|
||||
|
||||
|
||||
def test_normalize_valid_cdr() -> None:
|
||||
payload = {
|
||||
"raw_id": "raw-us-east-9988221",
|
||||
"src_ip": "10.240.12.1",
|
||||
"device_id": "310410998877665",
|
||||
"caller_num": "0019875551234",
|
||||
"svc": "4G_DATA",
|
||||
"up_bytes": 1048576,
|
||||
"down_bytes": 4194304,
|
||||
"rat": 6,
|
||||
"lac": "4021",
|
||||
"ci": "8812",
|
||||
"epoch_start": 1726070000,
|
||||
"epoch_end": 1726070300,
|
||||
}
|
||||
|
||||
raw = parse_raw_cdr(payload)
|
||||
|
||||
normalized = normalize_cdr(raw)
|
||||
|
||||
assert normalized.raw_id == "raw-us-east-9988221"
|
||||
assert normalized.imsi == "310410998877665"
|
||||
assert normalized.msisdn == "+19875551234"
|
||||
|
||||
assert normalized.service_type == "4G_DATA"
|
||||
assert normalized.radio_access_type == "LTE_4G"
|
||||
|
||||
assert normalized.cell_id == "4021_8812"
|
||||
|
||||
assert normalized.bytes_uploaded == 1048576
|
||||
assert normalized.bytes_downloaded == 4194304
|
||||
assert normalized.total_bytes == 5242880
|
||||
|
||||
assert normalized.duration_seconds == 300
|
||||
|
||||
assert str(normalized.source_ip) == "10.240.12.1"
|
||||
|
||||
def test_cdr_id_is_deterministic() -> None:
|
||||
first = generate_cdr_id("raw-123")
|
||||
second = generate_cdr_id("raw-123")
|
||||
|
||||
assert first == second
|
||||
|
||||
def test_different_raw_ids_generate_different_cdr_ids() -> None:
|
||||
first = generate_cdr_id("raw-123")
|
||||
second = generate_cdr_id("raw-456")
|
||||
|
||||
assert first != second
|
||||
|
||||
def test_rat_mapping() -> None:
|
||||
assert normalize_rat(1) == "3G"
|
||||
assert normalize_rat(6) == "LTE_4G"
|
||||
assert normalize_rat(9) == "5G_NR"
|
||||
|
||||
def test_missing_source_ip_defaults_to_zero_address() -> None:
|
||||
payload = {
|
||||
"raw_id": "raw-001",
|
||||
"device_id": "310410998877665",
|
||||
"caller_num": "0019875551234",
|
||||
"svc": "4G_DATA",
|
||||
"up_bytes": 100,
|
||||
"down_bytes": 200,
|
||||
"rat": 6,
|
||||
"lac": "4021",
|
||||
"ci": "8812",
|
||||
"epoch_start": 1726070000,
|
||||
"epoch_end": 1726070300,
|
||||
}
|
||||
|
||||
raw = parse_raw_cdr(payload)
|
||||
normalized = normalize_cdr(raw)
|
||||
|
||||
assert str(normalized.source_ip) == "0.0.0.0"
|
||||
71
tests/unit/test_validation.py
Normal file
71
tests/unit/test_validation.py
Normal file
@@ -0,0 +1,71 @@
|
||||
import pytest
|
||||
|
||||
from core.domain.errors import CDRProcessingError, FailureReason
|
||||
from core.domain.validation import parse_raw_cdr
|
||||
|
||||
|
||||
def valid_payload() -> dict:
|
||||
return {
|
||||
"raw_id": "raw-001",
|
||||
"src_ip": "10.240.12.1",
|
||||
"device_id": "310410998877665",
|
||||
"caller_num": "0019875551234",
|
||||
"svc": "4G_DATA",
|
||||
"up_bytes": 1048576,
|
||||
"down_bytes": 4194304,
|
||||
"rat": 6,
|
||||
"lac": "4021",
|
||||
"ci": "8812",
|
||||
"epoch_start": 1726070000,
|
||||
"epoch_end": 1726070300,
|
||||
}
|
||||
|
||||
def test_missing_required_field() -> None:
|
||||
payload = valid_payload()
|
||||
del payload["caller_num"]
|
||||
|
||||
with pytest.raises(CDRProcessingError) as exc_info:
|
||||
parse_raw_cdr(payload)
|
||||
|
||||
error = exc_info.value
|
||||
|
||||
assert error.reason == FailureReason.MISSING_REQUIRED_FIELD
|
||||
assert error.failed_field == "caller_num"
|
||||
|
||||
def test_invalid_rat() -> None:
|
||||
payload = valid_payload()
|
||||
payload["rat"] = 99
|
||||
|
||||
with pytest.raises(CDRProcessingError) as exc_info:
|
||||
parse_raw_cdr(payload)
|
||||
|
||||
error = exc_info.value
|
||||
|
||||
assert error.reason == FailureReason.INVALID_RAT_VALUE
|
||||
assert error.failed_field == "rat"
|
||||
|
||||
def test_epoch_start_after_end_is_invalid() -> None:
|
||||
payload = valid_payload()
|
||||
|
||||
payload["epoch_start"] = 200
|
||||
payload["epoch_end"] = 100
|
||||
|
||||
with pytest.raises(CDRProcessingError) as exc_info:
|
||||
parse_raw_cdr(payload)
|
||||
|
||||
error = exc_info.value
|
||||
|
||||
assert error.reason == FailureReason.INVALID_EPOCH
|
||||
|
||||
def test_negative_uploaded_bytes_is_invalid() -> None:
|
||||
payload = valid_payload()
|
||||
|
||||
payload["up_bytes"] = -1
|
||||
|
||||
with pytest.raises(CDRProcessingError) as exc_info:
|
||||
parse_raw_cdr(payload)
|
||||
|
||||
error = exc_info.value
|
||||
|
||||
assert error.reason == FailureReason.NORMALIZATION_ERROR
|
||||
assert error.failed_field == "up_bytes"
|
||||
Reference in New Issue
Block a user