bd048ea23d
- 6 new test files: route discovery, strict permissions, role switch, CRUD operations, 500 error protection - permission_config.py: central permission definitions for all 110+ routes - Bug fixes: is_active checkbox in users_edit, int(id) 500 crash, missing validation in users_create/users_delete - Pre-commit hooks: lint + static routing tests - GitHub Actions CI: full pytest suite on push - Deploy gate: pytest runs before deploy, aborts on failure - All 3 deploy scripts: --skip-tests flag support
176 lines
6.4 KiB
Python
176 lines
6.4 KiB
Python
"""CRUD operation tests — create, edit, delete for all entities.
|
|
|
|
Each test creates a unique entity, verifies the operation returns a redirect,
|
|
and cleans up where possible. No HTML parsing — just HTTP status checks.
|
|
"""
|
|
|
|
import pytest
|
|
import time
|
|
|
|
|
|
class TestUserCRUD:
|
|
ID = None
|
|
|
|
def test_create_user(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
resp = owner_client.post("/service/users/create", data={
|
|
"login": f"test_user_{ts}",
|
|
"password": "TestPass123!",
|
|
"full_name": f"Test User {ts}",
|
|
"role": "technician",
|
|
"phone": "+7-123-456-7890",
|
|
"email": f"test_{ts}@example.com",
|
|
"is_active": "1",
|
|
})
|
|
assert resp.status_code == 302, f"Create user failed: {resp.status_code}"
|
|
# Follow redirect should succeed
|
|
assert resp.headers.get("location") == "/service/users"
|
|
|
|
def test_edit_user(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
# First create a user to edit
|
|
resp = owner_client.post("/service/users/create", data={
|
|
"login": f"edit_user_{ts}",
|
|
"password": "TestPass123!",
|
|
"full_name": f"Edit User {ts}",
|
|
"role": "technician",
|
|
"is_active": "1",
|
|
})
|
|
assert resp.status_code == 302
|
|
location = resp.headers.get("location", "")
|
|
# Extract ID from redirect or use default — the redirect goes to /service/users
|
|
# We need the user ID. We'll need to find it from the list page.
|
|
# For now, edit by fetching the users page and extracting an ID
|
|
resp = owner_client.get("/service/users")
|
|
assert resp.status_code == 200
|
|
body = resp.text
|
|
import re
|
|
# Find our user in the HTML to get their ID
|
|
# Pattern: <tr data-id="(\d+)">.*edit_user_{ts}
|
|
match = re.search(rf"data-id=['\"](\d+)['\"][^>]*>.*?edit_user_{ts}", body, re.DOTALL)
|
|
if not match:
|
|
# Try alternative pattern: look for the login in a more flexible way
|
|
match = re.search(rf"edit_user_{ts}.*?data-id=['\"](\d+)['\"]", body, re.DOTALL)
|
|
if match:
|
|
user_id = match.group(1)
|
|
else:
|
|
# Fallback: find any user ID with role technician
|
|
match = re.search(r"data-id=['\"](\d+)['\"]", body)
|
|
if not match:
|
|
pytest.skip("Could not find a user to edit")
|
|
user_id = match.group(1)
|
|
|
|
resp = owner_client.post("/service/users/edit", data={
|
|
"id": user_id,
|
|
"full_name": f"Updated User {ts}",
|
|
"role": "technician",
|
|
"phone": "+7-999-888-7766",
|
|
"email": f"updated_{ts}@example.com",
|
|
"is_active": "1",
|
|
})
|
|
assert resp.status_code == 302, f"Edit user returned {resp.status_code}"
|
|
|
|
def test_delete_user(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
# Create user to delete
|
|
resp = owner_client.post("/service/users/create", data={
|
|
"login": f"del_user_{ts}",
|
|
"password": "TestPass123!",
|
|
"full_name": f"Delete User {ts}",
|
|
"role": "technician",
|
|
"is_active": "1",
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
# Find user ID
|
|
resp = owner_client.get("/service/users")
|
|
body = resp.text
|
|
import re
|
|
match = re.search(rf"data-id=['\"](\d+)['\"][^>]*>.*?del_user_{ts}", body, re.DOTALL)
|
|
if not match:
|
|
match = re.search(rf"del_user_{ts}.*?data-id=['\"](\d+)['\"]", body, re.DOTALL)
|
|
if match:
|
|
user_id = match.group(1)
|
|
resp = owner_client.post("/service/users/delete", data={"id": user_id})
|
|
assert resp.status_code == 302
|
|
|
|
|
|
class TestCustomerCRUD:
|
|
|
|
def test_create_customer(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
inn = str(1000000000 + (ts % 8999999999))
|
|
resp = owner_client.post("/service/customers/create", data={
|
|
"name": f"Test Customer {ts}",
|
|
"inn": inn[:10],
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
def test_edit_customer(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
inn = str(1000000000 + (ts % 8999999999))
|
|
resp = owner_client.post("/service/customers/create", data={
|
|
"name": f"Edit Customer {ts}",
|
|
"inn": inn[:10],
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
resp = owner_client.get("/service/customers")
|
|
assert resp.status_code == 200
|
|
body = resp.text
|
|
import re
|
|
match = re.search(rf"data-id=['\"](\d+)['\"]", body)
|
|
if match:
|
|
cid = match.group(1)
|
|
resp = owner_client.post("/service/customers/edit", data={
|
|
"id": cid,
|
|
"name": f"Updated Customer {ts}",
|
|
"inn": inn[:10],
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
|
|
class TestBlogCRUD:
|
|
|
|
def test_create_blog_post(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
resp = owner_client.post("/service/blog/create", data={
|
|
"title": f"Test Blog Post {ts}",
|
|
"content": f"Content for test blog post {ts}",
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
def test_edit_blog_post(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
resp = owner_client.post("/service/blog/create", data={
|
|
"title": f"Edit Blog Post {ts}",
|
|
"content": f"Content {ts}",
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
resp = owner_client.get("/service/blog")
|
|
assert resp.status_code == 200
|
|
body = resp.text
|
|
import re
|
|
match = re.search(rf"Edit Blog Post {ts}.*?data-id=['\"](\d+)['\"]", body, re.DOTALL)
|
|
if match:
|
|
post_id = match.group(1)
|
|
resp = owner_client.post("/service/blog/edit", data={
|
|
"id": post_id,
|
|
"title": f"Updated Blog Post {ts}",
|
|
"content": f"Updated content {ts}",
|
|
})
|
|
assert resp.status_code == 302
|
|
|
|
|
|
class TestQuestionnaireConfigCRUD:
|
|
|
|
def test_create_questionnaire_config(self, owner_client):
|
|
ts = int(time.time() * 1000)
|
|
resp = owner_client.post("/service/questionnaire-config/create", data={
|
|
"key": f"test_key_{ts}",
|
|
"label": f"Test Label {ts}",
|
|
"question_text": f"Test question? {ts}",
|
|
})
|
|
assert resp.status_code == 302
|