From cf7cbb937cd47ad524ad1fb930f335c3c59835f0 Mon Sep 17 00:00:00 2001 From: Mohammad Sajad Pourajam Date: Wed, 6 May 2026 22:14:38 +0330 Subject: [PATCH] UPDATE --- .gitmodules | 20 ++++++++ Schemas | 1 + Tests/Dockerfile | 47 ++++++++++++++++++ Tests/config/apis.yaml | 37 ++++++++++++++ Tests/docker-compose.yaml | 24 +++++++++ Tests/logs/test.log | 36 ++++++++++++++ Tests/pytest.ini | 8 +++ Tests/requirements.txt | 5 ++ Tests/tests/test_authentication.py | 80 ++++++++++++++++++++++++++++++ Tests/utils/http_client.py | 21 ++++++++ Tests/utils/template.py | 13 +++++ Tests/utils/yaml_loader.py | 6 +++ 12 files changed, 298 insertions(+) create mode 100644 .gitmodules create mode 160000 Schemas create mode 100644 Tests/Dockerfile create mode 100644 Tests/config/apis.yaml create mode 100644 Tests/docker-compose.yaml create mode 100644 Tests/logs/test.log create mode 100644 Tests/pytest.ini create mode 100644 Tests/requirements.txt create mode 100644 Tests/tests/test_authentication.py create mode 100644 Tests/utils/http_client.py create mode 100644 Tests/utils/template.py create mode 100644 Tests/utils/yaml_loader.py diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..447789d --- /dev/null +++ b/.gitmodules @@ -0,0 +1,20 @@ +[submodule "Ai"] + path = Ai + url = ssh://git@git.crop-logic.ir:2222/sajad-dev/Ai.git + branch = develop +[submodule "SensorHub"] + path = SensorHub + url = ssh://git@git.crop-logic.ir:2222/sajad-dev/SensorHub.git + branch = develop +[submodule "Backend"] + path = Backend + url = ssh://git@git.crop-logic.ir:2222/sajad-dev/Backend.git + branch = develop +[submodule "Accsess"] + path = Accsess + url = ssh://git@git.crop-logic.ir:2222/sajad-dev/Accsess.git + branch = develop +[submodule "Schemas"] + path = Schemas + url = ssh://git@git.crop-logic.ir:2222/sajad-dev/Schemas.git + branch = develop diff --git a/Schemas b/Schemas new file mode 160000 index 0000000..993066a --- /dev/null +++ b/Schemas @@ -0,0 +1 @@ +Subproject commit 993066a19f4b2963fc6c289b7b22793e71fd1909 diff --git a/Tests/Dockerfile b/Tests/Dockerfile new file mode 100644 index 0000000..e8cddcd --- /dev/null +++ b/Tests/Dockerfile @@ -0,0 +1,47 @@ +FROM docker.iranserver.com/python:3.10 + +ENV PYTHONDONTWRITEBYTECODE=1 +ENV PYTHONUNBUFFERED=1 + +WORKDIR /app + +# ------------------------ +# APT MIRRORS (runflare) +# ------------------------ +RUN rm -f /etc/apt/sources.list /etc/apt/sources.list.d/* && \ +printf '%s\n' \ +'deb https://mirror-linux.runflare.com/debian/ bookworm main contrib non-free non-free-firmware' \ +'deb https://mirror-linux.runflare.com/debian/ bookworm-updates main contrib non-free non-free-firmware' \ +'deb https://mirror-linux.runflare.com/debian-security/ bookworm-security main contrib non-free non-free-firmware' \ +'' \ +> /etc/apt/sources.list + +RUN apt-get update && apt-get install -y --no-install-recommends \ + build-essential \ + curl \ + jq \ + && rm -rf /var/lib/apt/lists/* + +# ------------------------ +# Copy requirements +# ------------------------ +COPY requirements.txt . + +# ------------------------ +# PIP MIRROR +# ------------------------ +ENV PIP_INDEX_URL=https://mirror-pypi.runflare.com/simple +ENV PIP_TRUSTED_HOST=mirror-pypi.runflare.com + +RUN pip install --upgrade pip && \ + pip install --prefer-binary -r requirements.txt + +# ------------------------ +# Copy test code +# ------------------------ +COPY . . + +# Expose is optional for test container +# EXPOSE 8000 + +CMD bash -c "pytest tests -v --maxfail=1 | tee /logs/test.log" diff --git a/Tests/config/apis.yaml b/Tests/config/apis.yaml new file mode 100644 index 0000000..5ade919 --- /dev/null +++ b/Tests/config/apis.yaml @@ -0,0 +1,37 @@ +base_url: http://backend:8000/api/auth + +flows: + + register_login: + + register: + method: POST + path: /register/ + body: + username: "{random_username}" + email: "{random_username}@example.com" + phone_number: "09120000000" + password: "test123456" + first_name: "test" + last_name: "user" + + expected_status: 201 + expected_json: + msg: success + + login: + method: POST + path: /login/ + body: + identifier: "{random_username}" + password: "test123456" + + expected_status: 200 + + extract: + token: token + + store_redis: + key: "test_token:{random_username}" + ttl: 3600 + diff --git a/Tests/docker-compose.yaml b/Tests/docker-compose.yaml new file mode 100644 index 0000000..dc8238e --- /dev/null +++ b/Tests/docker-compose.yaml @@ -0,0 +1,24 @@ +services: + integration-tests: + image: integration-tests + build: . + container_name: integration_tests + restart: "no" + + environment: + REDIS_HOST: redis + REDIS_PORT: 6379 + + volumes: + - .:/app + - ./logs:/logs + + networks: + - crop_network + + tty: true + stdin_open: true + +networks: + crop_network: + external: true diff --git a/Tests/logs/test.log b/Tests/logs/test.log new file mode 100644 index 0000000..c40c6f5 --- /dev/null +++ b/Tests/logs/test.log @@ -0,0 +1,36 @@ +============================= test session starts ============================== +platform linux -- Python 3.10.20, pytest-9.0.3, pluggy-1.6.0 -- /usr/local/bin/python3.10 +cachedir: .pytest_cache +rootdir: /app +configfile: pytest.ini +collecting ... collected 0 items / 1 error + +==================================== ERRORS ==================================== +________________ ERROR collecting tests/test_authentication.py _________________ +ImportError while importing test module '/app/tests/test_authentication.py'. +Hint: make sure your test modules/packages have valid Python names. +Traceback: +/usr/local/lib/python3.10/site-packages/_pytest/python.py:507: in importtestmodule + mod = import_path( +/usr/local/lib/python3.10/site-packages/_pytest/pathlib.py:587: in import_path + importlib.import_module(module_name) +/usr/local/lib/python3.10/importlib/__init__.py:126: in import_module + return _bootstrap._gcd_import(name[level:], package, level) +:1050: in _gcd_import + ??? +:1027: in _find_and_load + ??? +:1006: in _find_and_load_unlocked + ??? +:688: in _load_unlocked + ??? +/usr/local/lib/python3.10/site-packages/_pytest/assertion/rewrite.py:197: in exec_module + exec(co, module.__dict__) +tests/test_authentication.py:5: in + from utils.yaml_loader import load_config +E ImportError: cannot import name 'load_config' from 'utils.yaml_loader' (/app/utils/yaml_loader.py) +=========================== short test summary info ============================ +ERROR tests/test_authentication.py +!!!!!!!!!!!!!!!!!!!!!!!!!! stopping after 1 failures !!!!!!!!!!!!!!!!!!!!!!!!!!! +!!!!!!!!!!!!!!!!!!!! Interrupted: 1 error during collection !!!!!!!!!!!!!!!!!!!! +=============================== 1 error in 0.52s =============================== diff --git a/Tests/pytest.ini b/Tests/pytest.ini new file mode 100644 index 0000000..0333abe --- /dev/null +++ b/Tests/pytest.ini @@ -0,0 +1,8 @@ +[pytest] +pythonpath = . +testpaths = tests +python_files = test_*.py +addopts = -v +markers = + ai: tests that use AI validation + slow: slow integration tests diff --git a/Tests/requirements.txt b/Tests/requirements.txt new file mode 100644 index 0000000..cd44c20 --- /dev/null +++ b/Tests/requirements.txt @@ -0,0 +1,5 @@ +pytest +requests +redis +python-dotenv +pyyaml diff --git a/Tests/tests/test_authentication.py b/Tests/tests/test_authentication.py new file mode 100644 index 0000000..a903e61 --- /dev/null +++ b/Tests/tests/test_authentication.py @@ -0,0 +1,80 @@ +import uuid +import redis + +from utils.http_client import http_request +from utils.yaml_loader import load_config +from utils.template import render + + +config = load_config() + +BASE_URL = config["base_url"] +flow = config["flows"]["register_login"] + +redis_client = redis.Redis( + host="redis", + port=6379, + decode_responses=True +) + + +def test_auth_flow(): + + context = {} + + context["random_username"] = f"user_{uuid.uuid4().hex[:6]}" + + # -------- register -------- + + register = flow["register"] + + body = render(register["body"], context) + + res = http_request( + register["method"], + BASE_URL + register["path"], + json=body + ) + + assert res["status_code"] == register["expected_status"] + + assert res["json"]["msg"] == register["expected_json"]["msg"] + + # -------- login -------- + + login = flow["login"] + + body = render(login["body"], context) + + res = http_request( + login["method"], + BASE_URL + login["path"], + json=body + ) + + assert res["status_code"] == login["expected_status"] + + token_field = login["extract"]["token"] + + token = res["json"][token_field] + + assert token is not None + + context["token"] = token + + # -------- redis store -------- + + redis_cfg = login["store_redis"] + + key = render(redis_cfg["key"], context) + + redis_client.set( + key, + token, + ex=redis_cfg["ttl"] + ) + + saved_token = redis_client.get(key) + + assert saved_token == token + diff --git a/Tests/utils/http_client.py b/Tests/utils/http_client.py new file mode 100644 index 0000000..b93b5f5 --- /dev/null +++ b/Tests/utils/http_client.py @@ -0,0 +1,21 @@ +import requests +import time + +def http_request(method, url, **kwargs): + start = time.time() + + response = requests.request(method, url, **kwargs) + + latency = time.time() - start + + try: + data = response.json() + except: + data = response.text + + return { + "status": response.status_code, + "data": data, + "latency": latency + } + diff --git a/Tests/utils/template.py b/Tests/utils/template.py new file mode 100644 index 0000000..00433fd --- /dev/null +++ b/Tests/utils/template.py @@ -0,0 +1,13 @@ +def render(obj, context): + + if isinstance(obj, dict): + return {k: render(v, context) for k, v in obj.items()} + + if isinstance(obj, list): + return [render(i, context) for i in obj] + + if isinstance(obj, str): + return obj.format(**context) + + return obj + diff --git a/Tests/utils/yaml_loader.py b/Tests/utils/yaml_loader.py new file mode 100644 index 0000000..6bd969c --- /dev/null +++ b/Tests/utils/yaml_loader.py @@ -0,0 +1,6 @@ +import yaml + +def load_apis(): + with open("apis.yaml", "r") as f: + return yaml.safe_load(f) +