242 lines
4.8 KiB
Python
242 lines
4.8 KiB
Python
import json
|
|
import os
|
|
import random
|
|
import time
|
|
import uuid
|
|
from collections import Counter
|
|
from concurrent.futures import ThreadPoolExecutor, as_completed
|
|
from copy import deepcopy
|
|
from urllib.error import HTTPError, URLError
|
|
from urllib.request import Request, urlopen
|
|
|
|
|
|
BASE_URL = os.getenv(
|
|
"BASE_URL",
|
|
"http://localhost:8000",
|
|
)
|
|
|
|
ENDPOINT = f"{BASE_URL}/api/v1/cdr/raw"
|
|
|
|
RUN_ID = os.getenv(
|
|
"RUN_ID",
|
|
uuid.uuid4().hex[:8],
|
|
)
|
|
|
|
|
|
def valid_payload(index: int) -> dict:
|
|
start = int(time.time())
|
|
|
|
return {
|
|
"raw_id": f"functional-{RUN_ID}-{index:04d}",
|
|
"src_ip": "10.240.12.1",
|
|
"device_id": "310410998877665",
|
|
"caller_num": "+14155552671",
|
|
"svc": "4G_DATA",
|
|
"up_bytes": 1024,
|
|
"down_bytes": 4096,
|
|
"rat": 6,
|
|
"lac": "4021",
|
|
"ci": "8812",
|
|
"epoch_start": start,
|
|
"epoch_end": start + 300,
|
|
}
|
|
|
|
|
|
def send_json(payload: dict) -> int | str:
|
|
body = json.dumps(
|
|
payload
|
|
).encode("utf-8")
|
|
|
|
return send_raw(body)
|
|
|
|
|
|
def send_raw(body: bytes) -> int | str:
|
|
request = Request(
|
|
ENDPOINT,
|
|
data=body,
|
|
method="POST",
|
|
headers={
|
|
"Content-Type": "application/json",
|
|
},
|
|
)
|
|
|
|
try:
|
|
with urlopen(
|
|
request,
|
|
timeout=10,
|
|
) as response:
|
|
return response.status
|
|
|
|
except HTTPError as exc:
|
|
return exc.code
|
|
|
|
except URLError as exc:
|
|
return f"network_error:{exc.reason}"
|
|
|
|
|
|
def main() -> None:
|
|
requests: list[
|
|
tuple[str, dict | bytes]
|
|
] = []
|
|
|
|
valid_records = [
|
|
valid_payload(index)
|
|
for index in range(1000)
|
|
]
|
|
|
|
# 1000 valid CDRs
|
|
for payload in valid_records:
|
|
requests.append(
|
|
("valid", payload)
|
|
)
|
|
|
|
# 20 missing caller_num
|
|
for index in range(20):
|
|
payload = valid_payload(
|
|
2000 + index
|
|
)
|
|
|
|
payload.pop(
|
|
"caller_num"
|
|
)
|
|
|
|
requests.append(
|
|
("missing_caller_num", payload)
|
|
)
|
|
|
|
# 10 malformed JSON requests
|
|
for _ in range(10):
|
|
requests.append(
|
|
(
|
|
"invalid_json",
|
|
b'{"raw_id": invalid}',
|
|
)
|
|
)
|
|
|
|
# 10 invalid RAT values
|
|
for index in range(10):
|
|
payload = valid_payload(
|
|
3000 + index
|
|
)
|
|
|
|
payload["rat"] = 99
|
|
|
|
requests.append(
|
|
("invalid_rat", payload)
|
|
)
|
|
|
|
# 10 invalid epochs
|
|
for index in range(10):
|
|
payload = valid_payload(
|
|
4000 + index
|
|
)
|
|
|
|
payload["epoch_start"] = (
|
|
payload["epoch_end"] + 100
|
|
)
|
|
|
|
requests.append(
|
|
("invalid_epoch", payload)
|
|
)
|
|
|
|
# 20 duplicates of already accepted valid records.
|
|
for payload in valid_records[:20]:
|
|
requests.append(
|
|
(
|
|
"duplicate",
|
|
deepcopy(payload),
|
|
)
|
|
)
|
|
|
|
random.shuffle(
|
|
requests
|
|
)
|
|
|
|
results = Counter()
|
|
|
|
started_at = time.perf_counter()
|
|
|
|
with ThreadPoolExecutor(
|
|
max_workers=50
|
|
) as executor:
|
|
futures = []
|
|
|
|
for request_type, payload in requests:
|
|
if isinstance(
|
|
payload,
|
|
bytes,
|
|
):
|
|
future = executor.submit(
|
|
send_raw,
|
|
payload,
|
|
)
|
|
else:
|
|
future = executor.submit(
|
|
send_json,
|
|
payload,
|
|
)
|
|
|
|
futures.append(
|
|
(
|
|
request_type,
|
|
future,
|
|
)
|
|
)
|
|
|
|
for request_type, future in futures:
|
|
status = future.result()
|
|
|
|
results[
|
|
(
|
|
request_type,
|
|
status,
|
|
)
|
|
] += 1
|
|
|
|
elapsed = (
|
|
time.perf_counter()
|
|
- started_at
|
|
)
|
|
|
|
print()
|
|
print(
|
|
f"Run ID: {RUN_ID}"
|
|
)
|
|
|
|
print(
|
|
f"Total requests: {len(requests)}"
|
|
)
|
|
|
|
print(
|
|
f"Elapsed: {elapsed:.2f}s"
|
|
)
|
|
|
|
print()
|
|
|
|
for (
|
|
request_type,
|
|
status,
|
|
), count in sorted(
|
|
results.items(),
|
|
key=lambda item: str(
|
|
item[0]
|
|
),
|
|
):
|
|
print(
|
|
f"{request_type:24} "
|
|
f"status={status!s:8} "
|
|
f"count={count}"
|
|
)
|
|
|
|
print()
|
|
print("Expected:")
|
|
print(" valid → 1000 x 202")
|
|
print(" duplicate → 20 x 202")
|
|
print(" missing_caller → 20 x 400")
|
|
print(" invalid_json → 10 x 400")
|
|
print(" invalid_rat → 10 x 400")
|
|
print(" invalid_epoch → 10 x 400")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main() |