init here

This commit is contained in:
2025-11-26 21:32:41 +03:00
commit 33c97acade
91 changed files with 9155 additions and 0 deletions

0
autotest/__init__.py Normal file
View File

View File

View File

@@ -0,0 +1,25 @@
TEST_NAME = "users:create teacher"
def run(context) -> None:
payload = {
context.K_FIRST_NAME: context.teacher_first,
context.K_LAST_NAME: context.teacher_last,
context.K_MIDDLE_NAME: context.teacher_middle,
context.K_EDUCATION: context.teacher_education,
context.K_PASSWORD: context.teacher_password,
context.K_CONFIRM_PASSWORD: context.teacher_password,
}
status, body, _ = context.send_request("POST", "/api/users", body=payload)
context.expect(status == 201, f"expected 201, got {status}, body={body!r}")
user_id = context.query_single_value(
"SELECT id FROM users WHERE username = ?",
(context.teacher_username,),
)
context.teacher_user_id = user_id
teacher_id = context.query_single_value(
"SELECT id FROM teachers WHERE user_id = ?",
(user_id,),
)
context.expect(teacher_id > 0, "teacher id must be positive")
context.teacher_id = teacher_id

View File

@@ -0,0 +1,18 @@
TEST_NAME = "users:create mismatch"
def run(context) -> None:
payload = {
context.K_FIRST_NAME: "Bob",
context.K_LAST_NAME: "Doe",
context.K_MIDDLE_NAME: "Ray",
context.K_EDUCATION: "History",
context.K_PASSWORD: "OnePass",
context.K_CONFIRM_PASSWORD: "OtherPass",
}
status, body, _ = context.send_request("POST", "/api/users", body=payload)
context.expect(status == 400, f"expected 400, got {status}")
context.expect(
context.MESSAGE_PASSWORD_MISMATCH in body,
"expected mismatch message",
)

View File

@@ -0,0 +1,18 @@
TEST_NAME = "users:login success"
def run(context) -> None:
payload = {
context.K_USERNAME: context.teacher_username,
context.K_PASSWORD: context.teacher_password,
}
status, body, _ = context.send_request(
"POST",
"/api/users/login",
body=payload,
)
context.expect(status == 200, f"expected 200, got {status}")
context.expect(
body == context.MESSAGE_LOGIN_OK,
f"unexpected login body: {body!r}",
)

View File

@@ -0,0 +1,14 @@
TEST_NAME = "users:login failure"
def run(context) -> None:
payload = {
context.K_USERNAME: context.teacher_username,
context.K_PASSWORD: "WrongPass",
}
status, _, _ = context.send_request(
"POST",
"/api/users/login",
body=payload,
)
context.expect(status == 401, f"expected 401, got {status}")

View File

@@ -0,0 +1,16 @@
TEST_NAME = "users:change password"
def run(context) -> None:
payload = {context.K_NEW_PASSWORD: context.teacher_password_new}
status, body, _ = context.send_request(
"PUT",
"/api/users/password",
body=payload,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 204, f"expected 204, got {status}, body={body!r}")
context.teacher_password = context.teacher_password_new

View File

@@ -0,0 +1,18 @@
TEST_NAME = "users:login with new password"
def run(context) -> None:
payload = {
context.K_USERNAME: context.teacher_username,
context.K_PASSWORD: context.teacher_password,
}
status, body, _ = context.send_request(
"POST",
"/api/users/login",
body=payload,
)
context.expect(status == 200, f"expected 200, got {status}")
context.expect(
body == context.MESSAGE_LOGIN_OK,
f"unexpected login body: {body!r}",
)

View File

@@ -0,0 +1,9 @@
TEST_NAME = "teachers:auth required"
def run(context) -> None:
status, _, _ = context.send_request("GET", "/api/classes")
context.expect(status == 401, f"expected 401, got {status}")
payload = {context.K_CLASS_NUMBER: 5, context.K_CLASS_LETTER: "A"}
status, _, _ = context.send_request("POST", "/api/classes", body=payload)
context.expect(status == 401, f"expected 401, got {status}")

View File

@@ -0,0 +1,31 @@
import json
TEST_NAME = "teachers:create class"
def run(context) -> None:
payload = {context.K_CLASS_NUMBER: 5, context.K_CLASS_LETTER: "A"}
status, body, _ = context.send_request(
"POST",
"/api/classes",
body=payload,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}, body={body!r}")
data = json.loads(body)
context.class_id = int(data[context.K_IDENTIFIER])
context.expect(
data[context.K_CLASS_NUMBER] == payload[context.K_CLASS_NUMBER],
"class number mismatch",
)
context.expect(
data[context.K_CLASS_LETTER] == payload[context.K_CLASS_LETTER],
"class letter mismatch",
)
context.expect(
int(data[context.K_CREATOR]) > 0,
"creator id must be positive",
)

View File

@@ -0,0 +1,24 @@
import json
TEST_NAME = "teachers:list classes"
def run(context) -> None:
status, body, _ = context.send_request(
"GET",
"/api/classes",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200, got {status}")
data = json.loads(body)
entries = data.get(context.K_CLASSES, [])
context.expect(
any(
int(item[context.K_IDENTIFIER]) == context.class_id
for item in entries
),
"class not listed",
)

View File

@@ -0,0 +1,19 @@
import json
TEST_NAME = "teachers:create extra class"
def run(context) -> None:
payload = {context.K_CLASS_NUMBER: 6, context.K_CLASS_LETTER: "B"}
status, body, _ = context.send_request(
"POST",
"/api/classes",
body=payload,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}")
data = json.loads(body)
context.class_two_id = int(data[context.K_IDENTIFIER])

View File

@@ -0,0 +1,27 @@
TEST_NAME = "teachers:delete extra class"
def run(context) -> None:
path = f"/api/classes/{context.class_two_id}"
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 204, f"expected 204, got {status}")
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 404,
f"expected 404 on repeated delete, got {status}",
)
context.class_two_id = None

View File

@@ -0,0 +1,33 @@
TEST_NAME = "students:create student"
def run(context) -> None:
status, _, _ = context.send_request("POST", "/api/students", body={})
context.expect(status == 401, f"expected 401, got {status}")
payload = {
context.K_FIRST_NAME: "Charlie",
context.K_LAST_NAME: "Stone",
context.K_MIDDLE_NAME: "Lee",
context.K_PASSWORD: "Student42",
context.K_CONFIRM_PASSWORD: "Student42",
context.K_SNILS: "00011122233",
context.K_PASSPORT: "AB1234567",
}
status, body, _ = context.send_request(
"POST",
"/api/students",
body=payload,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}, body={body!r}")
context.student_one_id = context.query_single_value(
"SELECT id FROM students WHERE snils = ?",
(payload[context.K_SNILS],),
)
context.student_one_username = (
f"{payload[context.K_FIRST_NAME]}.{payload[context.K_LAST_NAME]}"
)
context.student_one_password = payload[context.K_PASSWORD]

View File

@@ -0,0 +1,31 @@
TEST_NAME = "students:create second student"
def run(context) -> None:
payload = {
context.K_FIRST_NAME: "Daisy",
context.K_LAST_NAME: "King",
context.K_MIDDLE_NAME: "May",
context.K_PASSWORD: "Student84",
context.K_CONFIRM_PASSWORD: "Student84",
context.K_SNILS: "99988877766",
context.K_PASSPORT: "CD7654321",
}
status, body, _ = context.send_request(
"POST",
"/api/students",
body=payload,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}, body={body!r}")
context.student_two_id = context.query_single_value(
"SELECT id FROM students WHERE snils = ?",
(payload[context.K_SNILS],),
)
context.student_two_username = (
f"{payload[context.K_FIRST_NAME]}.{payload[context.K_LAST_NAME]}"
)
context.student_two_password = payload[context.K_PASSWORD]

View File

@@ -0,0 +1,32 @@
TEST_NAME = "teachers:add student to class"
def run(context) -> None:
path = f"/api/classes/{context.class_id}/students/{context.student_two_id}"
status, _, _ = context.send_request("POST", path)
context.expect(status == 401, f"expected 401, got {status}")
status, _, _ = context.send_request(
"POST",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}")
invalid_path = (
f"/api/classes/{context.class_id + 999}/students/"
f"{context.student_two_id}"
)
status, _, _ = context.send_request(
"POST",
invalid_path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 403,
f"expected 403 for foreign class, got {status}",
)

View File

@@ -0,0 +1,31 @@
import json
TEST_NAME = "students:list"
def run(context) -> None:
status, _, _ = context.send_request("GET", "/api/students")
context.expect(status == 401, f"expected 401, got {status}")
status, body, _ = context.send_request(
"GET",
"/api/students",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200, got {status}")
data = json.loads(body)
students = data.get("ученики", [])
ids = {int(item[context.K_IDENTIFIER]) for item in students}
context.expect(
context.student_one_id in ids,
"first student missing from list",
)
context.expect(
context.student_two_id in ids,
"second student missing from list",
)

View File

@@ -0,0 +1,48 @@
import json
TEST_NAME = "students:get one"
def run(context) -> None:
path = f"/api/students/{context.student_one_id}"
status, _, _ = context.send_request("GET", path)
context.expect(status == 401, f"expected 401, got {status}")
status, body, _ = context.send_request(
"GET",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200, got {status}")
data = json.loads(body)
context.expect(
int(data[context.K_IDENTIFIER]) == context.student_one_id,
"unexpected student id",
)
context.expect(
data.get(context.K_FIRST_NAME),
"missing student first name",
)
context.expect(
data.get(context.K_LAST_NAME),
"missing student last name",
)
context.expect(
data.get(context.K_USERNAME),
"missing student username",
)
missing_path = f"/api/students/{context.student_two_id + 999}"
status, _, _ = context.send_request(
"GET",
missing_path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 404, f"expected 404, got {status}")

View File

@@ -0,0 +1,39 @@
TEST_NAME = "students:delete"
def run(context) -> None:
path = f"/api/students/{context.student_one_id}"
status, _, _ = context.send_request("DELETE", path)
context.expect(status == 401, f"expected 401, got {status}")
status, _, _ = context.send_request(
"DELETE",
f"/api/students/{context.student_one_id + 999}",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 404, f"expected 404, got {status}")
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 204, f"expected 204, got {status}")
status, _, _ = context.send_request(
"GET",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 404, f"expected 404 after delete, got {status}")
context.student_one_id = None

View File

@@ -0,0 +1,86 @@
import json
TEST_NAME = "lessons:create"
def run(context) -> None:
path = f"/api/classes/{context.class_id}/lessons"
payload_primary = {
"дата": "2025-09-01",
"название": "Алгебра",
"домашнее задание": "Упражнения 1-3",
}
status, _, _ = context.send_request("POST", path, body=payload_primary)
context.expect(status == 401, f"expected 401, got {status}")
status, _, _ = context.send_request(
"POST",
f"/api/classes/{context.class_id + 999}/lessons",
body=payload_primary,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 403,
f"expected 403 for foreign class, got {status}",
)
status, body, _ = context.send_request(
"POST",
path,
body=payload_primary,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 201, f"expected 201, got {status}, body={body!r}")
data = json.loads(body)
context.lesson_first_id = int(data[context.K_IDENTIFIER])
context.lesson_first_date = payload_primary["дата"]
context.lesson_first_title = payload_primary["название"]
context.expect(
int(data["идентификатор класса"]) == context.class_id,
"lesson class mismatch",
)
context.expect(
data["название"] == payload_primary["название"],
"lesson title mismatch",
)
context.expect(
data["дата"] == payload_primary["дата"],
"lesson date mismatch",
)
payload_secondary = {
"дата урока": "2025-09-02",
"тема": "Геометрия",
"домашка": "Читать параграф 4",
}
status, body, _ = context.send_request(
"POST",
path,
body=payload_secondary,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 201,
f"expected 201 for second lesson, got {status}",
)
data = json.loads(body)
context.lesson_second_id = int(data[context.K_IDENTIFIER])
context.lesson_second_date = payload_secondary["дата урока"]
context.lesson_second_title = payload_secondary["тема"]
context.expect(
data["домашнее задание"] == payload_secondary["домашка"],
"secondary homework mismatch",
)

View File

@@ -0,0 +1,80 @@
import json
TEST_NAME = "lessons:list"
def run(context) -> None:
base_path = f"/api/classes/{context.class_id}/lessons"
status, _, _ = context.send_request("GET", base_path)
context.expect(status == 401, f"expected 401, got {status}")
status, body, _ = context.send_request(
"GET",
base_path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200, got {status}")
data = json.loads(body)
lessons = data.get("уроки", [])
context.expect(len(lessons) >= 2, "expected at least two lessons")
def find_lesson(entry_id: int):
for item in lessons:
if int(item[context.K_IDENTIFIER]) == entry_id:
return item
return None
first_entry = find_lesson(context.lesson_first_id)
context.expect(first_entry is not None, "first lesson missing")
context.expect(
first_entry.get("название") == context.lesson_first_title,
"first lesson title mismatch",
)
filter_path = (
f"/api/classes/{context.class_id}/lessons/date/"
f"{context.lesson_first_date}"
)
status, body, _ = context.send_request(
"GET",
filter_path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200 for filter, got {status}")
filtered = json.loads(body).get("уроки", [])
context.expect(
len(filtered) == 1,
f"expected single lesson in filter, got {len(filtered)}",
)
context.expect(
int(filtered[0][context.K_IDENTIFIER]) == context.lesson_first_id,
"filter returned unexpected lesson",
)
student_headers = context.make_auth(
context.student_two_username,
context.student_two_password,
)
status, body, _ = context.send_request(
"GET",
base_path,
headers=student_headers,
)
context.expect(status == 200, f"expected 200 for student, got {status}")
student_view = json.loads(body).get("уроки", [])
context.expect(
any(
int(item[context.K_IDENTIFIER]) == context.lesson_second_id
for item in student_view
),
"student view missing lesson",
)

View File

@@ -0,0 +1,81 @@
import json
TEST_NAME = "lessons:delete"
def run(context) -> None:
path = f"/api/classes/{context.class_id}/lessons/{context.lesson_first_id}"
status, _, _ = context.send_request("DELETE", path)
context.expect(status == 401, f"expected 401, got {status}")
status, _, _ = context.send_request(
"DELETE",
f"/api/classes/{context.class_id + 999}/lessons/"
f"{context.lesson_first_id}",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 403,
f"expected 403 for foreign class, got {status}",
)
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 204, f"expected 204, got {status}")
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 404, f"expected 404 after delete, got {status}")
filter_path = (
f"/api/classes/{context.class_id}/lessons/date/"
f"{context.lesson_first_date}"
)
status, body, _ = context.send_request(
"GET",
filter_path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200 for filter, got {status}")
filtered = json.loads(body).get("уроки", [])
context.expect(len(filtered) == 0, "filter should be empty after delete")
status, body, _ = context.send_request(
"GET",
f"/api/classes/{context.class_id}/lessons",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 200, f"expected 200 for list, got {status}")
remaining = json.loads(body).get("уроки", [])
context.expect(
any(
int(item[context.K_IDENTIFIER]) == context.lesson_second_id
for item in remaining
),
"second lesson missing after delete",
)
context.lesson_first_id = None

View File

@@ -0,0 +1,55 @@
TEST_NAME = "teachers:remove student from class"
def run(context) -> None:
path = f"/api/classes/{context.class_id}/students/{context.student_two_id}"
status, _, _ = context.send_request("DELETE", path)
context.expect(status == 401, f"expected 401, got {status}")
status, _, _ = context.send_request(
"DELETE",
f"/api/classes/{context.class_id + 999}/students/"
f"{context.student_two_id}",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 403,
f"expected 403 for foreign class, got {status}",
)
status, _, _ = context.send_request(
"DELETE",
f"/api/classes/{context.class_id}/students/"
f"{context.student_two_id + 999}",
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(
status == 404,
f"expected 404 for foreign student, got {status}",
)
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 204, f"expected 204, got {status}")
status, _, _ = context.send_request(
"DELETE",
path,
headers=context.make_auth(
context.teacher_username,
context.teacher_password,
),
)
context.expect(status == 404, f"expected 404 after removal, got {status}")

255
autotest/main.py Normal file
View File

@@ -0,0 +1,255 @@
#!/usr/bin/env python3
import http.client
import importlib
import json
import os
import socket
import sqlite3
import subprocess
import sys
import time
import argparse
from pathlib import Path
import traceback
ROOT = Path(__file__).resolve().parents[1]
DB_PATH = ROOT / "var" / "srab.db"
SERVER_PATH = os.environ.get("SERVER_PATH", ROOT / "build" / "target.exe")
HOST = "127.0.0.1"
PORT = 1337
if str(ROOT) not in sys.path:
sys.path.insert(0, str(ROOT))
K_FIRST_NAME = "имя"
K_LAST_NAME = "фамилия"
K_MIDDLE_NAME = "отчество"
K_EDUCATION = "образование"
K_PASSWORD = "пароль"
K_CONFIRM_PASSWORD = "повтор пароля"
K_NEW_PASSWORD = "новый пароль"
K_SNILS = "снилс"
K_PASSPORT = "паспорт"
K_CLASS_NUMBER = "номер"
K_CLASS_LETTER = "буква"
K_CLASSES = "классы"
K_IDENTIFIER = "идентификатор"
K_CREATOR = "создатель"
K_USERNAME = "имя пользователя"
MESSAGE_PASSWORD_MISMATCH = "Пароли не совпадают."
MESSAGE_LOGIN_OK = "Рады видеть вас снова :3"
CASE_PACKAGE = "autotest.cases"
CASE_MODULES = [
"case_01_users_create_teacher",
"case_02_users_create_mismatch",
"case_03_users_login_success",
"case_04_users_login_failure",
"case_05_users_change_password",
"case_06_users_login_with_new_password",
"case_07_teachers_auth_required",
"case_08_teachers_create_class",
"case_09_teachers_list_classes",
"case_10_teachers_create_extra_class",
"case_11_teachers_delete_extra_class",
"case_12_students_create_student",
"case_13_students_create_second_student",
"case_14_teachers_add_student_to_class",
"case_15_students_list",
"case_16_students_get",
"case_17_students_delete",
"case_18_lessons_teacher_add",
"case_19_lessons_list",
"case_20_lessons_teacher_delete",
"case_21_teachers_remove_student_from_class",
]
class TestContext:
def __init__(self) -> None:
self.teacher_first = "Alice"
self.teacher_last = "Smith"
self.teacher_middle = "Ann"
self.teacher_education = "Math"
self.teacher_password_initial = "Secret42"
self.teacher_password_new = "Secret99"
self.teacher_password = self.teacher_password_initial
self.teacher_username = f"{self.teacher_first}.{self.teacher_last}"
self.class_id = None
self.class_two_id = None
self.student_one_id = None
self.student_two_id = None
self.teacher_id = None
self.teacher_user_id = None
self.lesson_first_id = None
self.lesson_first_date = None
self.lesson_first_title = None
self.lesson_second_id = None
self.lesson_second_date = None
self.lesson_second_title = None
self.student_one_username = None
self.student_one_password = None
self.student_two_username = None
self.student_two_password = None
self.K_FIRST_NAME = K_FIRST_NAME
self.K_LAST_NAME = K_LAST_NAME
self.K_MIDDLE_NAME = K_MIDDLE_NAME
self.K_EDUCATION = K_EDUCATION
self.K_PASSWORD = K_PASSWORD
self.K_CONFIRM_PASSWORD = K_CONFIRM_PASSWORD
self.K_NEW_PASSWORD = K_NEW_PASSWORD
self.K_SNILS = K_SNILS
self.K_PASSPORT = K_PASSPORT
self.K_CLASS_NUMBER = K_CLASS_NUMBER
self.K_CLASS_LETTER = K_CLASS_LETTER
self.K_CLASSES = K_CLASSES
self.K_IDENTIFIER = K_IDENTIFIER
self.K_CREATOR = K_CREATOR
self.K_USERNAME = K_USERNAME
self.MESSAGE_PASSWORD_MISMATCH = MESSAGE_PASSWORD_MISMATCH
self.MESSAGE_LOGIN_OK = MESSAGE_LOGIN_OK
def expect(self, condition: bool, message: str) -> None:
expect(condition, message)
def send_request(self, method: str, path: str, *, body=None, headers=None):
return send_request(method, path, body=body, headers=headers)
def query_single_value(self, sql: str, params) -> int:
return query_single_value(sql, params)
def make_auth(self, username: str, password: str) -> dict:
return make_auth(username, password)
def remove_database() -> None:
if DB_PATH.exists():
DB_PATH.unlink()
def start_server(silent) -> subprocess.Popen:
out = subprocess.DEVNULL if silent else None
return subprocess.Popen(
[str(SERVER_PATH)],
cwd=str(ROOT),
stdout=out,
stderr=out,
)
def wait_for_server(timeout: float = 10.0) -> None:
deadline = time.time() + timeout
while time.time() < deadline:
try:
with socket.create_connection((HOST, PORT), timeout=0.25):
return
except OSError:
time.sleep(0.05)
raise RuntimeError("server did not accept connections")
def stop_server(proc: subprocess.Popen) -> None:
if proc.poll() is not None:
return
proc.terminate()
try:
proc.wait(timeout=2)
except subprocess.TimeoutExpired:
proc.kill()
proc.wait(timeout=2)
def expect(condition: bool, message: str) -> None:
if not condition:
raise AssertionError(message)
def make_auth(username: str, password: str) -> dict:
return {"Authorization": f"Basic {username} {password}"}
def send_request(method: str, path: str, *, body=None, headers=None):
conn = http.client.HTTPConnection(HOST, PORT, timeout=5)
try:
data = None
hdrs = {} if headers is None else dict(headers)
if body is not None:
if isinstance(body, (dict, list)):
data = json.dumps(body, ensure_ascii=False).encode("utf-8")
hdrs.setdefault(
"Content-Type",
"application/json; charset=utf-8",
)
elif isinstance(body, str):
data = body.encode("utf-8")
else:
data = body
conn.request(method, path, body=data, headers=hdrs)
response = conn.getresponse()
payload = response.read()
text = payload.decode("utf-8") if payload else ""
return response.status, text, dict(response.getheaders())
finally:
conn.close()
def query_single_value(sql: str, params) -> int:
with sqlite3.connect(DB_PATH) as conn:
conn.row_factory = sqlite3.Row
row = conn.execute(sql, params).fetchone()
expect(row is not None, f"query returned no rows: {sql!r} {params!r}")
return int(row["id"])
def load_cases():
modules = []
for name in CASE_MODULES:
module = importlib.import_module(f"{CASE_PACKAGE}.{name}")
modules.append(module)
return modules
def run_tests() -> None:
context = TestContext()
for module in load_cases():
step_name = getattr(module, "TEST_NAME", module.__name__)
print(f"[step] {step_name}")
try:
module.run(context)
except Exception:
print(f"Test '{step_name}' run failed")
print(traceback.format_exc())
sys.exit(1)
print("[OK] All steps passed")
def main() -> None:
parser = argparse.ArgumentParser(description="SRAB automatic test suit.")
parser.add_argument(
"-s",
"--suppress",
default=False,
action="store_true",
help="Suppress server output"
)
args = parser.parse_args()
remove_database()
server = start_server(args.suppress)
try:
wait_for_server()
run_tests()
finally:
stop_server(server)
if __name__ == "__main__":
main()