2 TestFeatchAlmostReady
This commit is contained in:
parent
76f51b6935
commit
2b6f327397
1
KVSTest/__init__.py
Normal file
1
KVSTest/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
11
KVSTest/features/FirstBahave.feature
Normal file
11
KVSTest/features/FirstBahave.feature
Normal file
@ -0,0 +1,11 @@
|
||||
Feature: Place info (REST/GraphQL/WebSocket)
|
||||
|
||||
|
||||
Scenario: Authorize as employer
|
||||
When get access token
|
||||
Then access token is valid
|
||||
|
||||
Scenario: Get place info
|
||||
When get place info
|
||||
Then place info is valid for query data
|
||||
|
||||
18
KVSTest/features/PlaceInfoKVS.feature
Normal file
18
KVSTest/features/PlaceInfoKVS.feature
Normal file
@ -0,0 +1,18 @@
|
||||
Feature: KVS GraphQL (place + members)
|
||||
|
||||
Background: Authorize as employer
|
||||
When get access token
|
||||
Then access token is valid
|
||||
|
||||
Scenario: Get place info (dynamic place, no hardcode)
|
||||
When create place for kvs
|
||||
And query place members for created kvs place
|
||||
Then kvs place members response has correct shape for created place
|
||||
|
||||
Scenario: Add user to place and verify member appears
|
||||
When create place for kvs
|
||||
And create user for kvs
|
||||
And add user to kvs place
|
||||
Then addUserToPlace response is valid
|
||||
When query place members for created kvs place
|
||||
Then added member is present in place members results
|
||||
15
KVSTest/features/SubscriptionKVS.feature
Normal file
15
KVSTest/features/SubscriptionKVS.feature
Normal file
@ -0,0 +1,15 @@
|
||||
Feature: KVS GraphQL subscription
|
||||
|
||||
Background: Authorize as employer
|
||||
When get access token
|
||||
Then access token is valid
|
||||
|
||||
Scenario: Create subscription, check invoices, delete subscription
|
||||
When create service for kvs subscription
|
||||
And create plan for kvs subscription
|
||||
And create subscription for kvs
|
||||
Then subscription response is valid
|
||||
When query pending invoices for subscription place
|
||||
Then invoices response is valid and references subscription
|
||||
When delete created subscription
|
||||
Then delete subscription response is successful
|
||||
1
KVSTest/features/__init__.py
Normal file
1
KVSTest/features/__init__.py
Normal file
@ -0,0 +1 @@
|
||||
|
||||
29
KVSTest/features/environment.py
Normal file
29
KVSTest/features/environment.py
Normal file
@ -0,0 +1,29 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import traceback
|
||||
from typing import Any, Callable
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
|
||||
|
||||
def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||
# Стек очистки: функции вызываются в обратном порядке (LIFO).
|
||||
context._cleanup_fns: list[Callable[[], None]] = [] # type: ignore[attr-defined]
|
||||
|
||||
|
||||
def after_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||
cleanup_fns: list[Callable[[], None]] = getattr(context, "_cleanup_fns", [])
|
||||
while cleanup_fns:
|
||||
fn = cleanup_fns.pop()
|
||||
try:
|
||||
with allure.step(f"Cleanup: {getattr(fn, '__name__', 'cleanup')}"):
|
||||
fn()
|
||||
except Exception:
|
||||
# Cleanup не должен скрывать причину падения сценария, но полезно приложить traceback в Allure.
|
||||
allure.attach(
|
||||
traceback.format_exc(),
|
||||
name="Cleanup error",
|
||||
attachment_type=AttachmentType.TEXT,
|
||||
)
|
||||
|
||||
49
KVSTest/features/steps/kvs_add_user_to_place_steps.py
Normal file
49
KVSTest/features/steps/kvs_add_user_to_place_steps.py
Normal file
@ -0,0 +1,49 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
from behave import then, when
|
||||
|
||||
from KVSTest.testdata.kvs_test_data import KVSTestData
|
||||
|
||||
@when("add user to kvs place") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_add_user_to_place(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
place_id = getattr(context, "kvs_place_id", None) or td.place_id
|
||||
account_id = getattr(context, "kvs_account_id", None) or td.account_id
|
||||
assert isinstance(place_id, str) and place_id, "Нет kvs_place_id (place не создан)."
|
||||
assert isinstance(account_id, str) and account_id, "Нет kvs_account_id (user не создан)."
|
||||
|
||||
resp = td.add_user_to_place(account_id=account_id, place_id=place_id)
|
||||
context.kvs_add_user_to_place_response = resp
|
||||
|
||||
@then("addUserToPlace response is valid") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_add_user_to_place_response(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
place_id = getattr(context, "kvs_place_id", None) or td.place_id
|
||||
assert isinstance(place_id, str) and place_id, "Нет kvs_place_id."
|
||||
|
||||
resp = getattr(context, "kvs_add_user_to_place_response", None)
|
||||
assert isinstance(resp, dict), f"Ответ GraphQL не dict: {resp!r}"
|
||||
assert "data" in resp, f"В ответе нет data: {resp!r}"
|
||||
payload = resp.get("data", {}).get("addUserToPlace")
|
||||
assert isinstance(payload, dict), f"data.addUserToPlace не объект (ожидали JSONObject c полями place_id/member_id): {payload!r}"
|
||||
assert payload.get("place_id") == place_id, f"place_id не совпал: {payload!r}"
|
||||
member_id = payload.get("member_id")
|
||||
assert isinstance(member_id, str) and member_id, f"member_id пустой/не строка: {payload!r}"
|
||||
context.kvs_member_id = member_id
|
||||
|
||||
|
||||
@then("added member is present in place members results") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_member_present(context) -> None:
|
||||
member_id = getattr(context, "kvs_member_id", None)
|
||||
assert isinstance(member_id, str) and member_id, "Нет kvs_member_id (невалидный addUserToPlace ответ)."
|
||||
|
||||
resp = getattr(context, "kvs_place_members_response", None)
|
||||
assert isinstance(resp, dict), f"Ответ GraphQL не dict: {resp!r}"
|
||||
results = resp.get("data", {}).get("place", {}).get("results", [])
|
||||
assert isinstance(results, list) and results, f"data.place.results пустой/не list: {results!r}"
|
||||
members = results[0].get("members") if isinstance(results[0], dict) else None
|
||||
assert isinstance(members, list), f"members не list: {members!r}"
|
||||
ids = [m.get("id") for m in members if isinstance(m, dict)]
|
||||
assert member_id in ids, f"Не нашли member_id={member_id!r} в members ids={ids!r}"
|
||||
|
||||
34
KVSTest/features/steps/kvs_place_info_steps.py
Normal file
34
KVSTest/features/steps/kvs_place_info_steps.py
Normal file
@ -0,0 +1,34 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import then, when
|
||||
|
||||
from KVSTest.testdata.kvs_test_data import KVSTestData
|
||||
|
||||
@when("query place members for created kvs place") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_query_place_members(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
place_id = getattr(context, "kvs_place_id", None) or td.place_id
|
||||
assert isinstance(place_id, str) and place_id, "Нет kvs_place_id (place не создан)."
|
||||
context.kvs_place_members_response = td.query_place_members(place_id=place_id)
|
||||
|
||||
@then("kvs place members response has correct shape for created place") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_place_members_shape(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
place_id = getattr(context, "kvs_place_id", None) or td.place_id
|
||||
assert isinstance(place_id, str) and place_id, "Нет kvs_place_id."
|
||||
|
||||
resp = getattr(context, "kvs_place_members_response", None)
|
||||
assert isinstance(resp, dict), f"Ответ GraphQL не dict: {resp!r}"
|
||||
assert "data" in resp, f"В ответе нет data: {resp!r}"
|
||||
place = resp.get("data", {}).get("place")
|
||||
assert isinstance(place, dict), f"data.place не объект: {place!r}"
|
||||
results = place.get("results")
|
||||
assert isinstance(results, list) and results, f"data.place.results пустой/не list: {results!r}"
|
||||
first = results[0]
|
||||
assert isinstance(first, dict), f"results[0] не объект: {first!r}"
|
||||
assert first.get("id") == place_id, f"Ожидали place.id={place_id!r}, получили: {first.get('id')!r}"
|
||||
members = first.get("members")
|
||||
assert isinstance(members, list), f"members не list: {members!r}"
|
||||
|
||||
88
KVSTest/features/steps/kvs_subscription_steps.py
Normal file
88
KVSTest/features/steps/kvs_subscription_steps.py
Normal file
@ -0,0 +1,88 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import then, when
|
||||
|
||||
from KVSTest.testdata.subscription_test_data import SubscriptionTestData
|
||||
|
||||
|
||||
@when("create service for kvs subscription") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_service(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
context.kvs_service_id = td.ensure_service()
|
||||
|
||||
|
||||
@when("create plan for kvs subscription") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_plan(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
context.kvs_plan_id = td.ensure_plan()
|
||||
|
||||
|
||||
@when("create subscription for kvs") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_subscription(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
context.kvs_subscription_id = td.create_subscription()
|
||||
|
||||
|
||||
@then("subscription response is valid") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_subscription_valid(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
sub_id = getattr(context, "kvs_subscription_id", None) or td.subscription_id
|
||||
assert isinstance(sub_id, str) and sub_id, "Нет subscription_id."
|
||||
# create_subscription already asserts response shape; here we validate cached ids coherence
|
||||
assert td.subscription_id == sub_id
|
||||
assert isinstance(td.plan_id, str) and td.plan_id, "Нет plan_id."
|
||||
assert isinstance(td.service_id, str) and td.service_id, "Нет service_id."
|
||||
|
||||
|
||||
@when("query pending invoices for subscription place") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_query_invoices(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
context.kvs_invoices_response = td.query_invoices()
|
||||
|
||||
|
||||
@then("invoices response is valid and references subscription") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_invoices(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
sub_id = td.subscription_id
|
||||
assert isinstance(sub_id, str) and sub_id, "Нет subscription_id."
|
||||
|
||||
resp = getattr(context, "kvs_invoices_response", None)
|
||||
assert isinstance(resp, dict) and "data" in resp, f"Некорректный invoices ответ: {resp!r}"
|
||||
invoices = resp.get("data", {}).get("invoices")
|
||||
assert isinstance(invoices, list), f"data.invoices не list: {invoices!r}"
|
||||
# допускаем, что инвойсы могут создаваться асинхронно — но если они пришли, должны быть валидной формы
|
||||
for inv in invoices:
|
||||
assert isinstance(inv, dict), f"invoice элемент не dict: {inv!r}"
|
||||
assert isinstance(inv.get("id"), str) and inv.get("id"), f"invoice.id пустой: {inv!r}"
|
||||
assert inv.get("status") in {"pending", "PENDING", "Pending"}, f"invoice.status неожиданный: {inv!r}"
|
||||
# Если стенд возвращает subscriptions — проверим, что где-то упоминается наша подписка
|
||||
referenced = False
|
||||
for inv in invoices:
|
||||
subs = inv.get("subscriptions")
|
||||
if isinstance(subs, list) and sub_id in subs:
|
||||
referenced = True
|
||||
break
|
||||
if isinstance(subs, str) and sub_id in subs:
|
||||
referenced = True
|
||||
break
|
||||
assert referenced or invoices == [], (
|
||||
"Invoices вернулись, но не содержат ссылку на созданную подписку. "
|
||||
"Если генерация invoice асинхронная — можно добавить ожидание/ретраи."
|
||||
)
|
||||
|
||||
|
||||
@when("delete created subscription") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_delete_subscription(context) -> None:
|
||||
td = SubscriptionTestData.from_behave_context(context)
|
||||
context.kvs_delete_subscription_response = td.delete_subscription_now()
|
||||
|
||||
|
||||
@then("delete subscription response is successful") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_delete_subscription(context) -> None:
|
||||
resp = getattr(context, "kvs_delete_subscription_response", None)
|
||||
assert isinstance(resp, dict) and "data" in resp, f"Некорректный deleteSubscription ответ: {resp!r}"
|
||||
value = resp.get("data", {}).get("deleteSubscription")
|
||||
assert value in (True, "true", "True", 1, "1", None) or isinstance(value, dict), f"deleteSubscription вернул неожиданное: {value!r}"
|
||||
|
||||
23
KVSTest/features/steps/kvs_testdata_steps.py
Normal file
23
KVSTest/features/steps/kvs_testdata_steps.py
Normal file
@ -0,0 +1,23 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import when
|
||||
|
||||
from KVSTest.testdata.kvs_test_data import KVSTestData
|
||||
|
||||
|
||||
@when("create place for kvs") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_place_for_kvs(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
td.ensure_place()
|
||||
context.kvs_place_id = td.place_id
|
||||
|
||||
|
||||
@when("create user for kvs") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_user_for_kvs(context) -> None:
|
||||
td = KVSTestData.from_behave_context(context)
|
||||
td.create_user()
|
||||
context.kvs_account_id = td.account_id
|
||||
context.kvs_username = td.username
|
||||
|
||||
40
KVSTest/features/steps/placeInfokvs_Steps.py
Normal file
40
KVSTest/features/steps/placeInfokvs_Steps.py
Normal file
@ -0,0 +1,40 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
import os
|
||||
from typing import Any, Final
|
||||
from behave import given, when, then
|
||||
from worklib.QueryData import kvs_query_data, kvs_query_data_place_id
|
||||
from worklib import admin_data
|
||||
from worklib.auth_as_employer import get_access_token
|
||||
from worklib.findplaceinfo.find_place_data import fetch_place_members
|
||||
|
||||
expected_result = {
|
||||
"members": [
|
||||
{
|
||||
"id": "bb368ee9-c15f-40ef-acb0-c466df47d096",
|
||||
"parent_id": None,
|
||||
"user": {
|
||||
"username": "+79999956657"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@when("get place info kvs") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_get_place_info(context):
|
||||
token = getattr(context, "access_token", None) or admin_data.get_or_create_user("tester").access_token
|
||||
data = fetch_place_members(access_token=token, query=kvs_query_data()["query"], variables=kvs_query_data_place_id()["variables"])
|
||||
context.place_info = data
|
||||
|
||||
|
||||
@then("place info is valid") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_place_info_valid(context):
|
||||
data = getattr(context, "place_info", None)
|
||||
assert isinstance(data, dict), "Ответ GraphQL не dict"
|
||||
assert "data" in data or "place" in str(data), f"Не похоже на успешный GraphQL ответ: {data}"
|
||||
assert data["data"]["place"]["results"][0]["members"][0]["id"] == expected_result["members"][0]["id"]
|
||||
assert data["data"]["place"]["results"][0]["members"][0]["parent_id"] == expected_result["members"][0]["parent_id"]
|
||||
assert data["data"]["place"]["results"][0]["members"][0]["user"]["username"] == expected_result["members"][0]["user"]["username"]
|
||||
31
KVSTest/features/steps/place_steps.py
Normal file
31
KVSTest/features/steps/place_steps.py
Normal file
@ -0,0 +1,31 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
import os
|
||||
from typing import Any, Final
|
||||
from behave import given, when, then
|
||||
from worklib.QueryData import query_data, query_data_place_id_variables
|
||||
from worklib import admin_data
|
||||
from worklib.auth_as_employer import get_access_token
|
||||
from worklib.findplaceinfo.find_place_data import fetch_place_members
|
||||
|
||||
|
||||
|
||||
# pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
|
||||
|
||||
@when("get access token") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_get_access_token(context):
|
||||
if not getattr(context, "access_token", None):
|
||||
token = admin_data.get_access_token_from_env()
|
||||
context.access_token = token
|
||||
admin_data.get_or_create_user("tester").access_token = token
|
||||
|
||||
|
||||
@then("access token is valid") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_token_is_valid(context):
|
||||
token = getattr(context, "access_token", None)
|
||||
assert isinstance(token, str) and token.strip(), f"access_token пустой/не строка: {token}"
|
||||
|
||||
|
||||
|
||||
26
KVSTest/features/steps/place_steps_info.py
Normal file
26
KVSTest/features/steps/place_steps_info.py
Normal file
@ -0,0 +1,26 @@
|
||||
from behave import given, when, then
|
||||
from typing import Final
|
||||
from worklib import admin_data
|
||||
from worklib.findplaceinfo.find_place_data import fetch_place_members
|
||||
from worklib.QueryData import query_data, query_data_place_id_variables
|
||||
# pyright: ignore[reportGeneralTypeIssues]
|
||||
|
||||
_EXPECTED_RESULT: Final[dict[str, str]] = {
|
||||
"id": "682b071a163ac2a0995355be",
|
||||
"place_type": "street",
|
||||
"name": "ул. Мебельная",
|
||||
}
|
||||
@when("get place info") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_get_place_info(context):
|
||||
token = getattr(context, "access_token", None) or admin_data.get_or_create_user("tester").access_token
|
||||
data = fetch_place_members(access_token=token, query=query_data()["query"], variables=query_data_place_id_variables()["variables"])
|
||||
context.place_info = data
|
||||
|
||||
@then("place info is valid for query data") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_place_info_valid(context):
|
||||
data = getattr(context, "place_info", None)
|
||||
assert isinstance(data, dict), "Ответ GraphQL не dict"
|
||||
assert "data" in data or "place" in str(data), f"Не похоже на успешный GraphQL ответ: {data}"
|
||||
assert data["data"]["place"]["results"][0]["id"] == _EXPECTED_RESULT["id"]
|
||||
assert data["data"]["place"]["results"][0]["place_type"] == _EXPECTED_RESULT["place_type"]
|
||||
assert data["data"]["place"]["results"][0]["name"] == _EXPECTED_RESULT["name"]
|
||||
2
KVSTest/testdata/__init__.py
vendored
Normal file
2
KVSTest/testdata/__init__.py
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
"""Test data builders for KVSTest (behave)."""
|
||||
|
||||
201
KVSTest/testdata/kvs_test_data.py
vendored
Normal file
201
KVSTest/testdata/kvs_test_data.py
vendored
Normal file
@ -0,0 +1,201 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
|
||||
from worklib import admin_data
|
||||
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
||||
from KVSTest.testdata.query_data import add_user_to_place_mutation, kvs_place_members_query
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
def _exec_or_fail(*, op_name: str, token: str, query: str, variables: dict[str, Any] | None = None, company_id: str) -> dict[str, Any]:
|
||||
try:
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
variables=variables,
|
||||
company_id=company_id,
|
||||
access_token=token,
|
||||
)
|
||||
except PermissionError as e:
|
||||
allure.attach(str(e), name=f"Forbidden: {op_name}", attachment_type=AttachmentType.TEXT)
|
||||
raise AssertionError(f"Forbidden на операции: {op_name}") from e
|
||||
except RuntimeError as e:
|
||||
if "Forbidden" in str(e):
|
||||
allure.attach(str(e), name=f"Forbidden (runtime): {op_name}", attachment_type=AttachmentType.TEXT)
|
||||
raise AssertionError(f"Forbidden на операции: {op_name}") from e
|
||||
raise
|
||||
|
||||
|
||||
@dataclass
|
||||
class KVSTestData:
|
||||
"""
|
||||
Хранилище/фабрика тестовых данных для KVS GraphQL.
|
||||
|
||||
- Создаёт сущности (place/user) по мере необходимости
|
||||
- Кеширует id в полях
|
||||
- Регистрирует cleanup в behave context (если передан)
|
||||
"""
|
||||
|
||||
company_id: str = DEFAULT_COMPANY_ID
|
||||
parent_place_id: str = "6915dc03462d5aea0adc8cbd"
|
||||
default_user_first_name: str = "kvstest1"
|
||||
default_user_last_name: str = "kvstest2"
|
||||
|
||||
access_token: Optional[str] = None
|
||||
place_id: Optional[str] = None
|
||||
account_id: Optional[str] = None
|
||||
username: Optional[str] = None
|
||||
|
||||
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
||||
_last_add_user_to_place_response: dict[str, Any] | None = None
|
||||
|
||||
@classmethod
|
||||
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "KVSTestData":
|
||||
td: KVSTestData | None = getattr(context, "kvs_test_data", None)
|
||||
if isinstance(td, cls):
|
||||
if not td.access_token and getattr(context, "access_token", None):
|
||||
td.access_token = context.access_token
|
||||
if not td._cleanup_fns:
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
return td
|
||||
|
||||
td = cls(company_id=company_id)
|
||||
td.access_token = getattr(context, "access_token", None) or None
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
setattr(context, "kvs_test_data", td)
|
||||
return td
|
||||
|
||||
def _register_cleanup(self, fn: Callable[[], None]) -> None:
|
||||
if self._cleanup_fns is not None:
|
||||
self._cleanup_fns.append(fn)
|
||||
|
||||
def ensure_token(self) -> str:
|
||||
if self.access_token:
|
||||
return self.access_token
|
||||
token = admin_data.get_or_create_user("tester").access_token
|
||||
assert token, "Нет access_token (ни в context, ни в admin_data.get_or_create_user('tester'))."
|
||||
self.access_token = token
|
||||
return token
|
||||
|
||||
def ensure_place(self) -> str:
|
||||
if self.place_id:
|
||||
return self.place_id
|
||||
token = self.ensure_token()
|
||||
mutation = """
|
||||
mutation ($place_type: PlaceType!, $names: [String!]!, $parent_id: String, $address_id: String) {
|
||||
createPlaceMultiple(
|
||||
dto: {place_type: $place_type, names: $names, parent_id: $parent_id, address_id: $address_id}
|
||||
) {
|
||||
id
|
||||
__typename
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
suffix = str(int(time.time()))
|
||||
variables = {"names": [f"kvs-test-{suffix}"], "parent_id": self.parent_place_id, "place_type": "flat"}
|
||||
with allure.step("GraphQL: createPlaceMultiple (KVS)"):
|
||||
resp = _exec_or_fail(op_name="createPlaceMultiple(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createPlaceMultiple response", resp)
|
||||
created = resp.get("data", {}).get("createPlaceMultiple")
|
||||
if isinstance(created, list):
|
||||
assert created, f"createPlaceMultiple вернул пустой список. Ответ: {resp}"
|
||||
created0 = created[0]
|
||||
else:
|
||||
created0 = created
|
||||
assert isinstance(created0, dict), f"createPlaceMultiple вернул не объект: {created0!r}. Ответ: {resp}"
|
||||
place_id = created0.get("id")
|
||||
assert place_id, f"createPlaceMultiple не вернул id. Ответ: {resp}"
|
||||
self.place_id = place_id
|
||||
|
||||
def _cleanup_delete_place() -> None:
|
||||
delete_mutation = """mutation deleteplace($id: String!) { deletePlace(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deletePlace(mutation)", token=token, query=delete_mutation, variables={"id": place_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_place)
|
||||
return place_id
|
||||
|
||||
def create_user(self) -> str:
|
||||
if self.account_id:
|
||||
return self.account_id
|
||||
token = self.ensure_token()
|
||||
username = f"+7999{random.randint(1000000, 9999999)}"
|
||||
mutation = """
|
||||
mutation createUser($input: CreateAccountDTO!) {
|
||||
createUser(dto: $input)
|
||||
}
|
||||
""".strip()
|
||||
variables = {
|
||||
"input": {
|
||||
"username": username,
|
||||
"first_name": self.default_user_first_name,
|
||||
"last_name": self.default_user_last_name,
|
||||
"is_demo": True,
|
||||
"password": "",
|
||||
}
|
||||
}
|
||||
with allure.step("GraphQL: createUser (KVS)"):
|
||||
resp = _exec_or_fail(op_name="createUser(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createUser response", resp)
|
||||
created = resp.get("data", {}).get("createUser")
|
||||
if isinstance(created, dict):
|
||||
account_id = created.get("id")
|
||||
returned_username = created.get("username")
|
||||
else:
|
||||
account_id = created
|
||||
returned_username = None
|
||||
assert isinstance(account_id, str) and account_id, f"createUser не вернул id. Ответ: {resp}"
|
||||
self.account_id = account_id
|
||||
self.username = returned_username or username
|
||||
|
||||
def _cleanup_delete_user() -> None:
|
||||
delete_mutation = """
|
||||
mutation deleteUser($account_id: String!) { deleteUser(account_id: $account_id) }
|
||||
""".strip()
|
||||
_exec_or_fail(
|
||||
op_name="deleteUser(mutation)",
|
||||
token=token,
|
||||
query=delete_mutation,
|
||||
variables={"account_id": account_id},
|
||||
company_id=self.company_id,
|
||||
)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_user)
|
||||
return account_id
|
||||
|
||||
def add_user_to_place(self, *, account_id: str | None = None, place_id: str | None = None) -> dict[str, Any]:
|
||||
token = self.ensure_token()
|
||||
aid = account_id or self.create_user()
|
||||
pid = place_id or self.ensure_place()
|
||||
mutation = add_user_to_place_mutation()
|
||||
variables = {"account_id": aid, "place_id": pid}
|
||||
with allure.step("GraphQL: addUserToPlace (KVS)"):
|
||||
resp = _exec_or_fail(op_name="addUserToPlace(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("addUserToPlace response", resp)
|
||||
self._last_add_user_to_place_response = resp
|
||||
return resp
|
||||
|
||||
def query_place_members(self, *, place_id: str | None = None) -> dict[str, Any]:
|
||||
token = self.ensure_token()
|
||||
pid = place_id or self.ensure_place()
|
||||
query = kvs_place_members_query()
|
||||
variables = {"id": pid}
|
||||
with allure.step("GraphQL: place members (KVS)"):
|
||||
resp = _exec_or_fail(op_name="place(query)", token=token, query=query, variables=variables, company_id=self.company_id)
|
||||
_attach_json("place members response", resp)
|
||||
return resp
|
||||
|
||||
27
KVSTest/testdata/query_data.py
vendored
Normal file
27
KVSTest/testdata/query_data.py
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
from __future__ import annotations
|
||||
|
||||
|
||||
def kvs_place_members_query() -> str:
|
||||
return """
|
||||
query placeMembers($id: String!) {
|
||||
place(id: $id) {
|
||||
results {
|
||||
id
|
||||
members {
|
||||
id
|
||||
parent_id
|
||||
user { id username }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
|
||||
|
||||
def add_user_to_place_mutation() -> str:
|
||||
return """
|
||||
mutation addUserToPlace($account_id: String!, $place_id: String!) {
|
||||
addUserToPlace(account_id: $account_id, place_id: $place_id)
|
||||
}
|
||||
""".strip()
|
||||
|
||||
265
KVSTest/testdata/subscription_test_data.py
vendored
Normal file
265
KVSTest/testdata/subscription_test_data.py
vendored
Normal file
@ -0,0 +1,265 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
|
||||
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
||||
from KVSTest.testdata.kvs_test_data import KVSTestData
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
def _exec_or_fail(*, op_name: str, token: str, query: str, variables: dict[str, Any] | None = None, company_id: str) -> dict[str, Any]:
|
||||
try:
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
variables=variables,
|
||||
company_id=company_id,
|
||||
access_token=token,
|
||||
)
|
||||
except PermissionError as e:
|
||||
allure.attach(str(e), name=f"Forbidden: {op_name}", attachment_type=AttachmentType.TEXT)
|
||||
raise AssertionError(f"Forbidden на операции: {op_name}") from e
|
||||
except RuntimeError as e:
|
||||
# На части стендов Forbidden иногда приходит как GraphQL error со статусом 500.
|
||||
if "Forbidden" in str(e):
|
||||
allure.attach(str(e), name=f"Forbidden (runtime): {op_name}", attachment_type=AttachmentType.TEXT)
|
||||
raise AssertionError(
|
||||
f"Forbidden на операции: {op_name}. Проверьте, что пользователь из env "
|
||||
"AUTH_USERNAME/AUTH_PASSWORD имеет права на subscription операции."
|
||||
) from e
|
||||
raise
|
||||
|
||||
|
||||
@dataclass
|
||||
class SubscriptionTestData:
|
||||
"""
|
||||
Данные для теста подписок:
|
||||
service -> plan -> subscription -> invoices -> delete subscription
|
||||
"""
|
||||
|
||||
company_id: str = DEFAULT_COMPANY_ID
|
||||
kvs: KVSTestData | None = None
|
||||
|
||||
service_id: Optional[str] = None
|
||||
plan_id: Optional[str] = None
|
||||
subscription_id: Optional[str] = None
|
||||
|
||||
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
||||
|
||||
@classmethod
|
||||
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "SubscriptionTestData":
|
||||
td: SubscriptionTestData | None = getattr(context, "kvs_subscription_test_data", None)
|
||||
if isinstance(td, cls):
|
||||
if not td._cleanup_fns:
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
if not td.kvs:
|
||||
td.kvs = KVSTestData.from_behave_context(context, company_id=company_id)
|
||||
return td
|
||||
|
||||
td = cls(company_id=company_id)
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
td.kvs = KVSTestData.from_behave_context(context, company_id=company_id)
|
||||
setattr(context, "kvs_subscription_test_data", td)
|
||||
return td
|
||||
|
||||
def _register_cleanup(self, fn: Callable[[], None]) -> None:
|
||||
if self._cleanup_fns is not None:
|
||||
self._cleanup_fns.append(fn)
|
||||
|
||||
def ensure_service(self) -> str:
|
||||
if self.service_id:
|
||||
return self.service_id
|
||||
assert self.kvs is not None
|
||||
token = self.kvs.ensure_token()
|
||||
mutation = """
|
||||
mutation createservice($title: String!, $type: String!) {
|
||||
createService(dto: { title: $title, type: $type }) {
|
||||
id
|
||||
title
|
||||
type
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
suffix = str(int(time.time()))
|
||||
# API валидирует type: camera|intercom|access
|
||||
variables = {"title": f"kvs-service-{suffix}", "type": "access"}
|
||||
with allure.step("GraphQL: createService"):
|
||||
resp = _exec_or_fail(op_name="createService(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createService response", resp)
|
||||
service = resp.get("data", {}).get("createService")
|
||||
assert isinstance(service, dict) and service.get("id"), f"createService не вернул id. Ответ: {resp}"
|
||||
service_id = service["id"]
|
||||
self.service_id = service_id
|
||||
|
||||
def _cleanup_delete_service() -> None:
|
||||
delete_mutation = """mutation { deleteService(id: "%s") }""".strip() % service_id
|
||||
_exec_or_fail(op_name="deleteService(mutation)", token=token, query=delete_mutation, variables=None, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_service)
|
||||
return service_id
|
||||
|
||||
def ensure_plan(self) -> str:
|
||||
if self.plan_id:
|
||||
return self.plan_id
|
||||
assert self.kvs is not None
|
||||
token = self.kvs.ensure_token()
|
||||
service_id = self.ensure_service()
|
||||
place_id = self.kvs.ensure_place()
|
||||
|
||||
mutation = """
|
||||
mutation ($input: CreatePlanDTO!) {
|
||||
createPlan(dto: $input) {
|
||||
id
|
||||
service_ids
|
||||
bundle_ids
|
||||
place_id
|
||||
place_ids
|
||||
price
|
||||
title
|
||||
discount
|
||||
payment_interval
|
||||
price_without_discount
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
suffix = str(int(time.time()))
|
||||
variables = {
|
||||
"input": {
|
||||
"services": [service_id],
|
||||
"place_ids": [place_id],
|
||||
"price": 200,
|
||||
"payment_interval": 1,
|
||||
"discount": 0,
|
||||
"title": f"plan-kvs-{suffix}",
|
||||
}
|
||||
}
|
||||
with allure.step("GraphQL: createPlan"):
|
||||
resp = _exec_or_fail(op_name="createPlan(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createPlan response", resp)
|
||||
plan = resp.get("data", {}).get("createPlan")
|
||||
assert isinstance(plan, dict) and plan.get("id"), f"createPlan не вернул id. Ответ: {resp}"
|
||||
# Проверяем, что service действительно включён в plan (как просили)
|
||||
service_ids = plan.get("service_ids")
|
||||
assert isinstance(service_ids, list) and service_id in service_ids, (
|
||||
f"Созданный service_id не попал в plan.service_ids. service_id={service_id!r}, service_ids={service_ids!r}"
|
||||
)
|
||||
plan_id = plan["id"]
|
||||
self.plan_id = plan_id
|
||||
|
||||
def _cleanup_delete_plan() -> None:
|
||||
delete_mutation = """mutation { deletePlan(id: "%s") }""".strip() % plan_id
|
||||
_exec_or_fail(op_name="deletePlan(mutation)", token=token, query=delete_mutation, variables=None, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_plan)
|
||||
return plan_id
|
||||
|
||||
def create_subscription(self) -> str:
|
||||
if self.subscription_id:
|
||||
return self.subscription_id
|
||||
assert self.kvs is not None
|
||||
token = self.kvs.ensure_token()
|
||||
plan_id = self.ensure_plan()
|
||||
service_id = self.ensure_service()
|
||||
place_id = self.kvs.ensure_place()
|
||||
subscriber_id = self.kvs.create_user()
|
||||
|
||||
# Пользователь должен быть участником места (как в addUserToPlace тесте)
|
||||
add_resp = self.kvs.add_user_to_place(account_id=subscriber_id, place_id=place_id)
|
||||
_attach_json("addUserToPlace (for subscription) response", add_resp)
|
||||
payload = add_resp.get("data", {}).get("addUserToPlace")
|
||||
assert isinstance(payload, dict) and payload.get("member_id"), f"addUserToPlace не вернул member_id: {add_resp!r}"
|
||||
|
||||
members_resp = self.kvs.query_place_members(place_id=place_id)
|
||||
_attach_json("place members (after addUserToPlace) response", members_resp)
|
||||
results = members_resp.get("data", {}).get("place", {}).get("results", [])
|
||||
assert isinstance(results, list) and results, f"place.results пустой: {members_resp!r}"
|
||||
members = results[0].get("members") if isinstance(results[0], dict) else None
|
||||
assert isinstance(members, list), f"members не list: {members!r}"
|
||||
user_ids = []
|
||||
for m in members:
|
||||
if isinstance(m, dict) and isinstance(m.get("user"), dict):
|
||||
uid = m["user"].get("id")
|
||||
if isinstance(uid, str):
|
||||
user_ids.append(uid)
|
||||
assert subscriber_id in user_ids, f"subscriber_id не найден в place.members.user.id. subscriber_id={subscriber_id!r}, got={user_ids!r}"
|
||||
|
||||
mutation = """
|
||||
mutation createsub($plan_id: String!, $subscriber_id: String!, $place_id: String!, $service_id: String!) {
|
||||
createSubscription(dto: { plan_id: $plan_id, subscriber_id: $subscriber_id, place_id: $place_id, service_id: $service_id }) {
|
||||
id
|
||||
services { id title }
|
||||
user { id data { first_name last_name } }
|
||||
plan { id title }
|
||||
place_id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"plan_id": plan_id, "subscriber_id": subscriber_id, "place_id": place_id, "service_id": service_id}
|
||||
with allure.step("GraphQL: createSubscription"):
|
||||
resp = _exec_or_fail(op_name="createSubscription(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createSubscription response", resp)
|
||||
sub = resp.get("data", {}).get("createSubscription")
|
||||
assert isinstance(sub, dict) and sub.get("id"), f"createSubscription не вернул id. Ответ: {resp}"
|
||||
# Проверяем, что service в subscription совпадает с созданным
|
||||
services = sub.get("services")
|
||||
assert isinstance(services, list) and any(isinstance(s, dict) and s.get("id") == service_id for s in services), (
|
||||
f"service_id не найден в subscription.services. service_id={service_id!r}, services={services!r}"
|
||||
)
|
||||
plan_obj = sub.get("plan")
|
||||
assert isinstance(plan_obj, dict) and plan_obj.get("id") == plan_id, f"subscription.plan.id не совпал: {plan_obj!r}"
|
||||
assert sub.get("place_id") == place_id, f"subscription.place_id не совпал: {sub!r}"
|
||||
user_obj = sub.get("user")
|
||||
assert isinstance(user_obj, dict) and user_obj.get("id") == subscriber_id, f"subscription.user.id не совпал: {user_obj!r}"
|
||||
subscription_id = sub["id"]
|
||||
self.subscription_id = subscription_id
|
||||
|
||||
def _cleanup_delete_subscription() -> None:
|
||||
del_mut = """mutation($id: String!) { deleteSubscription(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deleteSubscription(mutation)", token=token, query=del_mut, variables={"id": subscription_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_subscription)
|
||||
return subscription_id
|
||||
|
||||
def query_invoices(self) -> dict[str, Any]:
|
||||
assert self.kvs is not None
|
||||
token = self.kvs.ensure_token()
|
||||
place_id = self.kvs.ensure_place()
|
||||
query = """
|
||||
query invoicesInfo($place_id: String!) {
|
||||
invoices(place_id: $place_id, status: pending) {
|
||||
id
|
||||
price
|
||||
status
|
||||
subscriptions
|
||||
place_id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
with allure.step("GraphQL: invoices (pending)"):
|
||||
resp = _exec_or_fail(op_name="invoices(query)", token=token, query=query, variables={"place_id": place_id}, company_id=self.company_id)
|
||||
_attach_json("invoices response", resp)
|
||||
return resp
|
||||
|
||||
def delete_subscription_now(self) -> dict[str, Any]:
|
||||
assert self.kvs is not None
|
||||
token = self.kvs.ensure_token()
|
||||
sub_id = self.subscription_id
|
||||
assert isinstance(sub_id, str) and sub_id, "Нет subscription_id."
|
||||
mutation = """mutation($id: String!) { deleteSubscription(id: $id) }""".strip()
|
||||
with allure.step("GraphQL: deleteSubscription"):
|
||||
resp = _exec_or_fail(op_name="deleteSubscription(mutation)", token=token, query=mutation, variables={"id": sub_id}, company_id=self.company_id)
|
||||
_attach_json("deleteSubscription response", resp)
|
||||
return resp
|
||||
|
||||
102
Ticket Mock API.json
Normal file
102
Ticket Mock API.json
Normal file
@ -0,0 +1,102 @@
|
||||
{
|
||||
"uuid": "51a1d02d-6cf4-4254-b0b6-5ec3400b8558",
|
||||
"lastMigration": 33,
|
||||
"name": "Ticket Mock API",
|
||||
"endpointPrefix": "",
|
||||
"latency": 0,
|
||||
"port": 8080,
|
||||
"hostname": "",
|
||||
"folders": [],
|
||||
"routes": [
|
||||
{
|
||||
"uuid": "75f18ed9-f146-456b-93f9-9de1f1a677c6",
|
||||
"type": "http",
|
||||
"documentation": "",
|
||||
"method": "post",
|
||||
"endpoint": "graphql",
|
||||
"responses": [
|
||||
{
|
||||
"uuid": "50ff5216-ff57-41a6-9dad-d5a275cec327",
|
||||
"body": "{\n \"data\": {\n \"createTicket\": {\n \"id\": \"507f1f77bcf86cd799439011\",\n \"__typename\": \"TicketObject\",\n \"category\": {\n \"id\": \"{{body.variables.category_id}}\",\n \"title\": \"Техническая поддержка\"\n }\n }\n }\n}",
|
||||
"latency": 0,
|
||||
"statusCode": 200,
|
||||
"label": "",
|
||||
"headers": [],
|
||||
"bodyType": "INLINE",
|
||||
"filePath": "",
|
||||
"databucketID": "",
|
||||
"sendFileAsBody": false,
|
||||
"rules": [
|
||||
{
|
||||
"target": "body",
|
||||
"modifier": "$.query",
|
||||
"value": "\\bcreateTicket\\b",
|
||||
"invert": false,
|
||||
"operator": "regex"
|
||||
}
|
||||
],
|
||||
"rulesOperator": "OR",
|
||||
"disableTemplating": false,
|
||||
"fallbackTo404": false,
|
||||
"default": true,
|
||||
"crudKey": "id",
|
||||
"callbacks": []
|
||||
}
|
||||
],
|
||||
"responseMode": "FALLBACK",
|
||||
"streamingMode": null,
|
||||
"streamingInterval": 0
|
||||
}
|
||||
],
|
||||
"rootChildren": [
|
||||
{
|
||||
"type": "route",
|
||||
"uuid": "75f18ed9-f146-456b-93f9-9de1f1a677c6"
|
||||
}
|
||||
],
|
||||
"proxyMode": true,
|
||||
"proxyHost": "https://admin.dev.dipal.ru",
|
||||
"proxyRemovePrefix": false,
|
||||
"tlsOptions": {
|
||||
"enabled": false,
|
||||
"type": "CERT",
|
||||
"pfxPath": "",
|
||||
"certPath": "",
|
||||
"keyPath": "",
|
||||
"caPath": "",
|
||||
"passphrase": ""
|
||||
},
|
||||
"cors": true,
|
||||
"headers": [
|
||||
{
|
||||
"key": "Content-Type",
|
||||
"value": "application/json"
|
||||
},
|
||||
{
|
||||
"key": "Access-Control-Allow-Origin",
|
||||
"value": "*"
|
||||
},
|
||||
{
|
||||
"key": "Access-Control-Allow-Methods",
|
||||
"value": "GET,POST,PUT,PATCH,DELETE,HEAD,OPTIONS"
|
||||
},
|
||||
{
|
||||
"key": "Access-Control-Allow-Headers",
|
||||
"value": "Content-Type, Origin, Accept, Authorization, Content-Length, X-Requested-With"
|
||||
}
|
||||
],
|
||||
"proxyReqHeaders": [
|
||||
{
|
||||
"key": "x-company-id",
|
||||
"value": "65437401ae3af6f8ffcdbaf8"
|
||||
}
|
||||
],
|
||||
"proxyResHeaders": [
|
||||
{
|
||||
"key": "",
|
||||
"value": ""
|
||||
}
|
||||
],
|
||||
"data": [],
|
||||
"callbacks": []
|
||||
}
|
||||
60
Ticket/features/CategoryInfo.feature
Normal file
60
Ticket/features/CategoryInfo.feature
Normal file
@ -0,0 +1,60 @@
|
||||
Feature: Ticket GraphQL (category + employee)
|
||||
|
||||
Background: Authorize as employer
|
||||
When get access token
|
||||
Then access token is valid
|
||||
|
||||
Scenario: Query ticket categories by place_id
|
||||
When create place multiple for ticket
|
||||
And create ticket category for created place
|
||||
And query ticket categories by created place id
|
||||
Then ticket_category results are not empty
|
||||
And created ticket category is present in results
|
||||
|
||||
Scenario: query employee by category+company
|
||||
When create place multiple for ticket
|
||||
And create ticket category for created place
|
||||
And create user for ticket
|
||||
And create employee for created user
|
||||
And create category group for created category
|
||||
And connect employee to category group
|
||||
When query employee by category and company
|
||||
Then employee results are not empty
|
||||
And each employee result has id and user fields
|
||||
And created employee username is in results
|
||||
|
||||
Scenario: Query employee response shape (may be empty)
|
||||
When query employee by category and company
|
||||
Then each employee result has id and user fields
|
||||
|
||||
Scenario: Change ticket category and verify employee authorization
|
||||
When prepare ticket and categories for category change test
|
||||
And change ticket category to in_group category
|
||||
And query tickets by created place id
|
||||
Then ticket category changed from old to in_group
|
||||
And employee is authorized for ticket
|
||||
When change ticket category to out_group category
|
||||
And query tickets by created place id
|
||||
Then employee is NOT authorized for ticket
|
||||
|
||||
Scenario: Assign ticket employee and verify group membership rules
|
||||
When prepare ticket and employees for assign employee test
|
||||
And assign ticket to fixed in_group employee
|
||||
And query tickets by created place id
|
||||
Then ticket assignee is fixed employee
|
||||
When assign ticket to new in_group employee
|
||||
And query tickets by created place id
|
||||
Then ticket assignee is new in_group employee
|
||||
When assign ticket to out_group employee (should fail)
|
||||
And query tickets by created place id
|
||||
Then ticket assignee is still new in_group employee
|
||||
|
||||
Scenario: Assign and unassign ticket employee
|
||||
When prepare ticket and employees for unassign employee test
|
||||
And assign ticket to new grouped employee
|
||||
And query tickets by created place id
|
||||
Then ticket assignee is new grouped employee
|
||||
When unassign ticket from new grouped employee
|
||||
And query tickets by created place id
|
||||
Then ticket assignee is empty
|
||||
|
||||
41
Ticket/features/environment.py
Normal file
41
Ticket/features/environment.py
Normal file
@ -0,0 +1,41 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import traceback
|
||||
from typing import Any, Callable
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
|
||||
|
||||
def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||
# behave context не типизирован, поэтому сохраняем список без аннотации справа (pyright ругается).
|
||||
context._cleanup_fns = [] # type: ignore[attr-defined]
|
||||
|
||||
# GraphQL endpoint выбирается в worklib.graphql_client.execute_graphql:
|
||||
# - явный аргумент graphql_url
|
||||
# - env GRAPHQL_URL
|
||||
# - DEFAULT_GRAPHQL_URL
|
||||
#
|
||||
# Для Mockoon (proxy или чистый mock) достаточно перед запуском тестов выставить:
|
||||
# GRAPHQL_URL=http://localhost:8080/graphql
|
||||
#
|
||||
# Если пользователь выставил env USE_MOCKOON=1, аккуратно подменим GRAPHQL_URL,
|
||||
# но не трогаем его, если он уже задан явно.
|
||||
if os.getenv("USE_MOCKOON") in {"1", "true", "True"} and not os.getenv("GRAPHQL_URL"):
|
||||
os.environ["GRAPHQL_URL"] = "http://localhost:8081/graphql"
|
||||
context.graphql_url = os.getenv("GRAPHQL_URL")
|
||||
|
||||
def after_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||
cleanup_fns: list[Callable[[], None]] = getattr(context, "_cleanup_fns", [])
|
||||
while cleanup_fns:
|
||||
fn = cleanup_fns.pop()
|
||||
try:
|
||||
with allure.step(f"Cleanup: {getattr(fn, '__name__', 'cleanup')}"):
|
||||
fn()
|
||||
except Exception:
|
||||
allure.attach(
|
||||
traceback.format_exc(),
|
||||
name="Cleanup error",
|
||||
attachment_type=AttachmentType.TEXT,
|
||||
)
|
||||
|
||||
85
Ticket/features/steps/category_info_steps.py
Normal file
85
Ticket/features/steps/category_info_steps.py
Normal file
@ -0,0 +1,85 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
from behave import then, when
|
||||
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
from worklib.graphql_client import execute_graphql
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
@when("query ticket categories by created place id") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_query_ticket_categories_by_place(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
place_id = td.ensure_place()
|
||||
|
||||
query = """
|
||||
query categoryinfo($place_id: String!) {
|
||||
ticket_category(filters: {place_id: $place_id}) {
|
||||
results {
|
||||
id
|
||||
title
|
||||
place_ids
|
||||
company_id
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"place_id": place_id}
|
||||
|
||||
with allure.step("GraphQL: ticket_category(filters: place_id)"):
|
||||
resp = execute_graphql(query=query, variables=variables, company_id=td.company_id, access_token=token)
|
||||
_attach_json("ticket_category response", resp)
|
||||
context.ticket_category_response = resp
|
||||
|
||||
|
||||
@then("ticket_category results are not empty") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_ticket_category_results_not_empty(context) -> None:
|
||||
resp = getattr(context, "ticket_category_response", None)
|
||||
assert isinstance(resp, dict), f"Нет ticket_category_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("ticket_category", {}).get("results")
|
||||
_attach_json("ticket_category.results (extracted)", results)
|
||||
assert isinstance(results, list), f"ticket_category.results должен быть list. Ответ: {resp}"
|
||||
assert len(results) > 0, "ticket_category.results пустой — тест должен падать"
|
||||
|
||||
|
||||
@then("created ticket category is present in results") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_created_ticket_category_present(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
created_category_id = td.category_id
|
||||
created_place_id = td.place_id
|
||||
assert created_category_id, "Нет category_id в TicketTestData (category ещё не создана)."
|
||||
assert created_place_id, "Нет place_id в TicketTestData (place ещё не создан)."
|
||||
|
||||
resp = getattr(context, "ticket_category_response", None)
|
||||
assert isinstance(resp, dict), f"Нет ticket_category_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("ticket_category", {}).get("results", [])
|
||||
assert isinstance(results, list), "ticket_category.results не list"
|
||||
assert len(results) > 0, "ticket_category.results пустой — тест должен падать"
|
||||
|
||||
match = None
|
||||
for item in results:
|
||||
if isinstance(item, dict) and item.get("id") == created_category_id:
|
||||
match = item
|
||||
break
|
||||
_attach_json("ticket_category.match (found)", match)
|
||||
assert match is not None, f"Не нашли созданную категорию id={created_category_id} в results"
|
||||
assert match.get("company_id") == td.company_id, f"company_id не совпал: {match.get('company_id')} != {td.company_id}"
|
||||
place_ids = match.get("place_ids")
|
||||
assert isinstance(place_ids, list), f"place_ids должен быть list, получено: {place_ids!r}"
|
||||
assert created_place_id in place_ids, f"В place_ids нет созданного place_id={created_place_id}. place_ids={place_ids}"
|
||||
|
||||
22
Ticket/features/steps/common_auth_steps.py
Normal file
22
Ticket/features/steps/common_auth_steps.py
Normal file
@ -0,0 +1,22 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import then, when
|
||||
|
||||
from worklib import admin_data
|
||||
|
||||
|
||||
@when("get access token") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_get_access_token(context) -> None:
|
||||
if not getattr(context, "access_token", None):
|
||||
token = admin_data.get_access_token_from_env()
|
||||
context.access_token = token
|
||||
admin_data.get_or_create_user("tester").access_token = token
|
||||
|
||||
|
||||
@then("access token is valid") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_token_is_valid(context) -> None:
|
||||
token = getattr(context, "access_token", None)
|
||||
assert isinstance(token, str) and token.strip(), f"access_token пустой/не строка: {token}"
|
||||
|
||||
108
Ticket/features/steps/employee_query_steps.py
Normal file
108
Ticket/features/steps/employee_query_steps.py
Normal file
@ -0,0 +1,108 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
from behave import then, when
|
||||
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
from worklib.graphql_client import execute_graphql
|
||||
|
||||
|
||||
DEFAULT_CATEGORY_ID = "6569776b0bb9d14b23bd4de7"
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
@when("query employee by category and company") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_query_employee(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
|
||||
query = """
|
||||
query employee($category_id: String!, $company_id: String!) {
|
||||
employee(filters: {category_id: $category_id, company_id: $company_id}) {
|
||||
results {
|
||||
id
|
||||
company { id name }
|
||||
user {
|
||||
id
|
||||
username
|
||||
data { first_name last_name }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
category_id = td.category_id or DEFAULT_CATEGORY_ID
|
||||
variables = {"category_id": category_id, "company_id": td.company_id}
|
||||
|
||||
with allure.step("GraphQL: employee(filters: category_id + company_id)"):
|
||||
resp = execute_graphql(query=query, variables=variables, company_id=td.company_id, access_token=token)
|
||||
_attach_json("employee response", resp)
|
||||
context.employee_response = resp
|
||||
|
||||
|
||||
@then("employee results are not empty") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_employee_results_not_empty(context) -> None:
|
||||
resp = getattr(context, "employee_response", None)
|
||||
assert isinstance(resp, dict), f"Нет employee_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("employee", {}).get("results")
|
||||
_attach_json("employee.results (extracted)", results)
|
||||
assert isinstance(results, list), f"employee.results должен быть list. Ответ: {resp}"
|
||||
assert len(results) > 0, "employee.results пустой — тест должен падать"
|
||||
|
||||
|
||||
@then("each employee result has id and user fields") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_each_result_has_fields(context) -> None:
|
||||
resp = getattr(context, "employee_response", None)
|
||||
assert isinstance(resp, dict), f"Нет employee_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("employee", {}).get("results", [])
|
||||
assert isinstance(results, list), "employee.results не list"
|
||||
for i, item in enumerate(results):
|
||||
assert isinstance(item, dict), f"results[{i}] не объект: {type(item)}"
|
||||
assert item.get("id"), f"results[{i}].id пустой"
|
||||
company = item.get("company")
|
||||
assert isinstance(company, dict), f"results[{i}].company не объект: {company}"
|
||||
assert company.get("id"), f"results[{i}].company.id пустой"
|
||||
assert company.get("name"), f"results[{i}].company.name пустой"
|
||||
user = item.get("user")
|
||||
assert isinstance(user, dict), f"results[{i}].user не объект: {user}"
|
||||
assert user.get("id"), f"results[{i}].user.id пустой"
|
||||
assert user.get("username"), f"results[{i}].user.username пустой"
|
||||
data = user.get("data")
|
||||
assert isinstance(data, dict), f"results[{i}].user.data не объект: {data}"
|
||||
assert data.get("first_name"), f"results[{i}].user.data.first_name пустой"
|
||||
assert data.get("last_name"), f"results[{i}].user.data.last_name пустой"
|
||||
|
||||
|
||||
@then("created employee username is in results") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_created_username_in_results(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
username = td.username
|
||||
assert username, "Нет username в TicketTestData (user ещё не создан)."
|
||||
|
||||
resp = getattr(context, "employee_response", None)
|
||||
assert isinstance(resp, dict), f"Нет employee_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("employee", {}).get("results", [])
|
||||
assert isinstance(results, list), "employee.results не list"
|
||||
|
||||
usernames: list[str] = []
|
||||
for item in results:
|
||||
if isinstance(item, dict):
|
||||
user = item.get("user")
|
||||
if isinstance(user, dict) and user.get("username"):
|
||||
usernames.append(user["username"])
|
||||
_attach_json("employee.usernames (extracted)", usernames)
|
||||
assert username in usernames, f"Не нашли созданного пользователя {username} в employee.results: {usernames}"
|
||||
|
||||
179
Ticket/features/steps/ticket_category_change_steps.py
Normal file
179
Ticket/features/steps/ticket_category_change_steps.py
Normal file
@ -0,0 +1,179 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from typing import Any
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
from behave import then, when
|
||||
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
from worklib.graphql_client import execute_graphql
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
def _get_change_ids(context) -> dict[str, str]:
|
||||
ids = getattr(context, "ticket_category_change_ids", None)
|
||||
assert isinstance(ids, dict), f"Нет ticket_category_change_ids в контексте: {ids}"
|
||||
for k in ("old", "in_group", "out_group"):
|
||||
assert ids.get(k), f"ticket_category_change_ids[{k!r}] пустой: {ids}"
|
||||
return ids
|
||||
|
||||
|
||||
def _get_change_titles(context) -> dict[str, str]:
|
||||
titles = getattr(context, "ticket_category_change_titles", None)
|
||||
assert isinstance(titles, dict), f"Нет ticket_category_change_titles в контексте: {titles}"
|
||||
for k in ("old", "in_group", "out_group"):
|
||||
assert titles.get(k), f"ticket_category_change_titles[{k!r}] пустой: {titles}"
|
||||
return titles
|
||||
|
||||
|
||||
@when("change ticket category to in_group category") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_change_category_to_in_group(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
ids = _get_change_ids(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id (ticket ещё не создан)."
|
||||
|
||||
mutation = """
|
||||
mutation updatecategory($ticket_id: String!, $category_id: String!) {
|
||||
changeTicketCategory(dto: {ticket_id: $ticket_id, category_id: $category_id})
|
||||
}
|
||||
""".strip()
|
||||
variables = {"ticket_id": ticket_id, "category_id": ids["in_group"]}
|
||||
with allure.step("GraphQL: changeTicketCategory (to in_group)"):
|
||||
resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
|
||||
_attach_json("changeTicketCategory response", resp)
|
||||
context.change_category_response = resp
|
||||
|
||||
|
||||
@when("change ticket category to out_group category") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_change_category_to_out_group(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
ids = _get_change_ids(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id (ticket ещё не создан)."
|
||||
|
||||
mutation = """
|
||||
mutation updatecategory($ticket_id: String!, $category_id: String!) {
|
||||
changeTicketCategory(dto: {ticket_id: $ticket_id, category_id: $category_id})
|
||||
}
|
||||
""".strip()
|
||||
variables = {"ticket_id": ticket_id, "category_id": ids["out_group"]}
|
||||
with allure.step("GraphQL: changeTicketCategory (to out_group)"):
|
||||
resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
|
||||
_attach_json("changeTicketCategory response", resp)
|
||||
context.change_category_response = resp
|
||||
|
||||
|
||||
@when("query tickets by created place id") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_query_tickets_by_place(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
place_id = td.place_id
|
||||
assert place_id, "Нет place_id (place ещё не создан)."
|
||||
|
||||
query = """
|
||||
query tickets($place_id: String!) {
|
||||
ticket(
|
||||
filter: { place_id: $place_id },
|
||||
pagination: { limit: 100, skip: 0 }
|
||||
) {
|
||||
results {
|
||||
number
|
||||
id
|
||||
category { id title }
|
||||
assignee { id user { id username data { first_name last_name } } }
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"place_id": place_id}
|
||||
with allure.step("GraphQL: ticket(filter: place_id)"):
|
||||
resp = execute_graphql(query=query, variables=variables, company_id=td.company_id, access_token=token)
|
||||
_attach_json("ticket response", resp)
|
||||
context.ticket_query_response = resp
|
||||
|
||||
|
||||
def _find_ticket_in_results(context, *, ticket_id: str) -> dict[str, Any]:
|
||||
resp = getattr(context, "ticket_query_response", None)
|
||||
assert isinstance(resp, dict), f"Нет ticket_query_response или не dict: {resp}"
|
||||
results = resp.get("data", {}).get("ticket", {}).get("results", [])
|
||||
assert isinstance(results, list), f"ticket.results не list. Ответ: {resp}"
|
||||
for item in results:
|
||||
if isinstance(item, dict) and item.get("id") == ticket_id:
|
||||
return item
|
||||
_attach_json("ticket.results (extracted)", results)
|
||||
raise AssertionError(f"Не нашли ticket id={ticket_id} в ticket.results")
|
||||
|
||||
|
||||
@then("ticket category changed from old to in_group") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_category_changed_to_in_group(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
ids = _get_change_ids(context)
|
||||
titles = _get_change_titles(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
|
||||
item = _find_ticket_in_results(context, ticket_id=ticket_id)
|
||||
category = item.get("category")
|
||||
assert isinstance(category, dict), f"ticket.category не объект: {category}"
|
||||
assert category.get("id"), "ticket.category.id пустой"
|
||||
assert category.get("title"), "ticket.category.title пустой"
|
||||
assert category["id"] != ids["old"], f"Категория не поменялась, всё ещё old={ids['old']}"
|
||||
assert category["id"] == ids["in_group"], f"Ожидали in_group={ids['in_group']}, получили {category['id']}"
|
||||
assert category["title"] != titles["old"], f"title не поменялся, всё ещё old title={titles['old']!r}"
|
||||
assert category["title"] == titles["in_group"], f"Ожидали title={titles['in_group']!r}, получили {category['title']!r}"
|
||||
|
||||
|
||||
@then("employee is authorized for ticket") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_employee_authorized(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
|
||||
item = _find_ticket_in_results(context, ticket_id=ticket_id)
|
||||
assignee = item.get("assignee")
|
||||
assert isinstance(assignee, dict), f"assignee должен быть объектом (уполномочен), получено: {assignee!r}"
|
||||
assert assignee.get("id"), "assignee.id пустой"
|
||||
|
||||
user = assignee.get("user")
|
||||
assert isinstance(user, dict), f"assignee.user не объект: {user!r}"
|
||||
if td.account_id:
|
||||
assert user.get("id") == td.account_id, f"user.id не совпал с созданным user_id: {user.get('id')!r} != {td.account_id!r}"
|
||||
data = user.get("data")
|
||||
assert isinstance(data, dict), f"assignee.user.data не объект: {data!r}"
|
||||
assert data.get("first_name") == td.default_user_first_name, f"first_name не совпал: {data.get('first_name')!r}"
|
||||
assert data.get("last_name") == td.default_user_last_name, f"last_name не совпал: {data.get('last_name')!r}"
|
||||
|
||||
|
||||
@then("employee is NOT authorized for ticket") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_employee_not_authorized(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
|
||||
item = _find_ticket_in_results(context, ticket_id=ticket_id)
|
||||
assignee = item.get("assignee")
|
||||
if assignee is None:
|
||||
return
|
||||
# если assignee есть, то он должен быть не нашим user
|
||||
if isinstance(assignee, dict) and isinstance(assignee.get("user"), dict) and isinstance(assignee["user"].get("data"), dict):
|
||||
data = assignee["user"]["data"]
|
||||
assert not (
|
||||
data.get("first_name") == td.default_user_first_name and data.get("last_name") == td.default_user_last_name
|
||||
), f"Employee всё ещё уполномочен (assignee совпал с созданным user): {data}"
|
||||
return
|
||||
raise AssertionError(f"Неожиданная структура assignee при проверке NOT authorized: {assignee!r}")
|
||||
|
||||
127
Ticket/features/steps/ticket_category_change_testdata_steps.py
Normal file
127
Ticket/features/steps/ticket_category_change_testdata_steps.py
Normal file
@ -0,0 +1,127 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import when
|
||||
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
from worklib.graphql_client import execute_graphql
|
||||
|
||||
|
||||
DEFAULT_TICKETINFO_PLACE_ID = "682733c16773cfa73dc8d0a7"
|
||||
|
||||
|
||||
@when("prepare ticket and categories for category change test") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_prepare_ticket_and_categories(context) -> None:
|
||||
"""
|
||||
Подготовка тестовых данных:
|
||||
- place
|
||||
- 3 категории (old / in_group / out_group)
|
||||
- user + employee
|
||||
- category group по категории in_group + connect employee
|
||||
- ticket с категорией old (createTicket может падать по правам — это ок, тест должен упасть)
|
||||
"""
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
|
||||
token = td.ensure_token()
|
||||
|
||||
query = """
|
||||
query ticketinfo($place_id: String!) {
|
||||
ticket(pagination:{skip:0,limit:25} ,filter:{place_id:$place_id}){
|
||||
results {
|
||||
id
|
||||
category { id title }
|
||||
assignee { id user { id username } }
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
# Сначала пытаемся работать с существующей заявкой на заранее известном place_id.
|
||||
# Если на стенде нет заявок — пытаемся создать свою (если createTicket разрешён).
|
||||
td.place_id = DEFAULT_TICKETINFO_PLACE_ID
|
||||
resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
|
||||
results = resp.get("data", {}).get("ticket", {}).get("results", [])
|
||||
|
||||
if isinstance(results, list) and results:
|
||||
ticket0 = results[0]
|
||||
assert isinstance(ticket0, dict) and ticket0.get("id"), f"Некорректный ticket: {ticket0!r}"
|
||||
old_category = ticket0.get("category")
|
||||
assert isinstance(old_category, dict) and old_category.get("id") and old_category.get("title"), f"У ticket нет category: {ticket0!r}"
|
||||
|
||||
context.ticket_id = ticket0["id"]
|
||||
td.ticket_id = ticket0["id"]
|
||||
old_category_id = old_category["id"]
|
||||
old_category_title = old_category["title"]
|
||||
place_id_for_new_categories = td.place_id
|
||||
else:
|
||||
# fallback: создаём тестовую заявку сами
|
||||
td.place_id = None
|
||||
td.ensure_place()
|
||||
place_id_for_new_categories = td.place_id
|
||||
assert place_id_for_new_categories
|
||||
old_category_id = td.create_ticket_category(title="cat-old", place_id=place_id_for_new_categories)
|
||||
old_category_title = "cat-old"
|
||||
try:
|
||||
td.create_ticket_with_category(category_id=old_category_id, place_id=place_id_for_new_categories)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(
|
||||
"Нет доступных tickets для проверки (по умолчанию берём place_id "
|
||||
f"{DEFAULT_TICKETINFO_PLACE_ID}) и createTicket запрещён на стенде. "
|
||||
"Укажите place_id с существующими заявками (поменяйте DEFAULT_TICKETINFO_PLACE_ID в шаге) "
|
||||
"или дайте права на createTicket. "
|
||||
f"Детали: {e}"
|
||||
)
|
||||
context.ticket_id = td.ticket_id
|
||||
|
||||
ids = {
|
||||
"old": old_category_id,
|
||||
"in_group": td.create_ticket_category(title=f"cat-in-group-{td.ticket_id}", place_id=place_id_for_new_categories),
|
||||
"out_group": td.create_ticket_category(title=f"cat-out-group-{td.ticket_id}", place_id=place_id_for_new_categories),
|
||||
}
|
||||
titles = {"old": old_category_title, "in_group": f"cat-in-group-{td.ticket_id}", "out_group": f"cat-out-group-{td.ticket_id}"}
|
||||
context.ticket_category_change_ids = ids
|
||||
context.ticket_category_change_titles = titles
|
||||
|
||||
# создаём нового employee (user создастся внутри) — он должен оставаться привязанным в группе №1
|
||||
td.create_employee()
|
||||
assert td.account_id and td.employee_id, "Не создался employee/user для теста смены категории"
|
||||
context.ticket_employee_id = td.employee_id
|
||||
context.ticket_account_id = td.account_id
|
||||
context.ticket_username = td.username
|
||||
|
||||
fixed_member = {"user_id": td.fixed_user_id, "employee_id": td.fixed_employee_id}
|
||||
new_member = {"user_id": td.account_id, "employee_id": td.employee_id}
|
||||
|
||||
# Группа №1: две категории (old + in_group), в ней новый employee + фиксированный employee
|
||||
# => при смене old -> in_group новый employee должен остаться.
|
||||
td.create_category_group_for_categories(
|
||||
[ids["old"], ids["in_group"]],
|
||||
members=[new_member, fixed_member],
|
||||
cache=False,
|
||||
)
|
||||
|
||||
# Группа №2: категория out_group, в ней только фиксированный employee (обязательное требование)
|
||||
# => при смене на out_group новый employee должен "отпасть".
|
||||
td.create_category_group_for_categories(
|
||||
[ids["out_group"]],
|
||||
members=[fixed_member],
|
||||
cache=False,
|
||||
)
|
||||
|
||||
# cleanup: вернуть категорию обратно
|
||||
cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
if isinstance(cleanup_fns, list):
|
||||
def _restore_category() -> None:
|
||||
mutation = """
|
||||
mutation updatecategory($ticket_id: String!, $category_id: String!) {
|
||||
changeTicketCategory(dto: {ticket_id: $ticket_id, category_id: $category_id})
|
||||
}
|
||||
""".strip()
|
||||
execute_graphql(
|
||||
query=mutation,
|
||||
variables={"ticket_id": td.ticket_id, "category_id": ids["old"]},
|
||||
company_id=td.company_id,
|
||||
access_token=token,
|
||||
)
|
||||
cleanup_fns.append(_restore_category)
|
||||
|
||||
304
Ticket/features/steps/ticket_employee_steps.py
Normal file
304
Ticket/features/steps/ticket_employee_steps.py
Normal file
@ -0,0 +1,304 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
from behave import then, when
|
||||
import json
|
||||
from typing import Any
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
from worklib.graphql_client import execute_graphql
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
DEFAULT_TICKETINFO_PLACE_ID = "682733c16773cfa73dc8d0a7"
|
||||
|
||||
|
||||
@when("create user for ticket") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_user_for_ticket(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.create_user()
|
||||
context.ticket_account_id = td.account_id
|
||||
context.ticket_username = td.username
|
||||
|
||||
|
||||
@when("create employee for created user") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_employee(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.create_employee()
|
||||
context.ticket_employee_id = td.employee_id
|
||||
|
||||
|
||||
@when("create category group for created category") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_category_group(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.create_category_group()
|
||||
context.ticket_category_group_id = td.category_group_id
|
||||
|
||||
|
||||
@when("connect employee to category group") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_connect_employee(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.connect_employee_to_category_group()
|
||||
|
||||
|
||||
@when("prepare ticket and employees for assign employee test") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_prepare_ticket_and_employees_for_assign(context) -> None:
|
||||
"""
|
||||
Подготовка:
|
||||
- 1 place
|
||||
- 1 category
|
||||
- 1 category group (для этой категории)
|
||||
- 3 employee:
|
||||
- fixed employee (в группе)
|
||||
- новый in_group employee (в группе)
|
||||
- новый out_group employee (вне группы)
|
||||
- 1 ticket в этой категории
|
||||
"""
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
|
||||
# Берём существующую заявку и её категорию (createTicket часто запрещён)
|
||||
query = """
|
||||
query ticketinfo($place_id: String!) {
|
||||
ticket(pagination:{skip:0,limit:25} ,filter:{place_id:$place_id}){
|
||||
results {
|
||||
id
|
||||
category { id title }
|
||||
assignee { id user { id username } }
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
td.place_id = DEFAULT_TICKETINFO_PLACE_ID
|
||||
resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
|
||||
results = resp.get("data", {}).get("ticket", {}).get("results", [])
|
||||
if isinstance(results, list) and results:
|
||||
ticket0 = results[0]
|
||||
assert isinstance(ticket0, dict) and ticket0.get("id"), f"Некорректный ticket: {ticket0!r}"
|
||||
category = ticket0.get("category")
|
||||
assert isinstance(category, dict) and category.get("id"), f"У ticket нет category: {ticket0!r}"
|
||||
context.ticket_id = ticket0["id"]
|
||||
td.ticket_id = ticket0["id"]
|
||||
category_id = category["id"]
|
||||
else:
|
||||
# fallback: создаём свою заявку, если это разрешено
|
||||
td.place_id = None
|
||||
td.ensure_place()
|
||||
category_id = td.ensure_ticket_category()
|
||||
try:
|
||||
td.create_ticket_with_category(category_id=category_id, place_id=td.place_id)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(
|
||||
"Нет доступных tickets для проверки assignTicketEmployee (по умолчанию берём place_id "
|
||||
f"{DEFAULT_TICKETINFO_PLACE_ID}) и createTicket запрещён на стенде. "
|
||||
"Укажите place_id с существующими заявками (поменяйте DEFAULT_TICKETINFO_PLACE_ID в шаге) "
|
||||
"или дайте права на createTicket. "
|
||||
f"Детали: {e}"
|
||||
)
|
||||
context.ticket_id = td.ticket_id
|
||||
with allure.step("GraphQL: ticket(pagination:skip:0,limit:25,filter:place_id)"):
|
||||
resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
|
||||
_attach_json("ticket response", resp)
|
||||
context.ticket_response = resp
|
||||
# employee #2 (new in-group)
|
||||
td.create_employee()
|
||||
assert td.account_id and td.employee_id, "Не создался in-group employee"
|
||||
in_group_user_id = td.account_id
|
||||
in_group_employee_id = td.employee_id
|
||||
|
||||
fixed_member = {"user_id": td.fixed_user_id, "employee_id": td.fixed_employee_id}
|
||||
new_in_group_member = {"user_id": in_group_user_id, "employee_id": in_group_employee_id}
|
||||
|
||||
td.create_category_group_for_categories([category_id], members=[fixed_member, new_in_group_member], cache=False)
|
||||
|
||||
# employee #3 (out-group): создаём ещё одного нового employee, но НЕ добавляем в group
|
||||
td_out = TicketTestData(company_id=td.company_id)
|
||||
td_out.access_token = td.access_token
|
||||
td_out._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
td_out.create_employee()
|
||||
assert td_out.account_id and td_out.employee_id, "Не создался out-group employee"
|
||||
out_group_user_id = td_out.account_id
|
||||
|
||||
# сохраняем ids для проверок
|
||||
context.assign_fixed_user_id = td.fixed_user_id
|
||||
context.assign_in_group_user_id = in_group_user_id
|
||||
context.assign_out_group_user_id = out_group_user_id
|
||||
|
||||
|
||||
def _assign_ticket_employee(context, *, employee_user_id: str, expect_error: bool) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
|
||||
mutation = """
|
||||
mutation assignTicketEmployee($ticket_id: String!, $employee_user_id: String!) {
|
||||
assignTicketEmployee(dto: {ticket_id: $ticket_id, employee_user_id: $employee_user_id})
|
||||
}
|
||||
""".strip()
|
||||
variables = {"ticket_id": ticket_id, "employee_user_id": employee_user_id}
|
||||
try:
|
||||
resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
|
||||
context.assign_ticket_employee_response = resp
|
||||
if expect_error:
|
||||
raise AssertionError(f"Ожидали ошибку при assignTicketEmployee, но получили успех: {resp}")
|
||||
except Exception as e:
|
||||
context.assign_ticket_employee_error = str(e)
|
||||
if not expect_error:
|
||||
raise
|
||||
|
||||
|
||||
@when("assign ticket to fixed in_group employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assign_ticket_fixed_employee(context) -> None:
|
||||
_assign_ticket_employee(context, employee_user_id=getattr(context, "assign_fixed_user_id"), expect_error=False)
|
||||
|
||||
|
||||
@when("assign ticket to new in_group employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assign_ticket_new_in_group_employee(context) -> None:
|
||||
_assign_ticket_employee(context, employee_user_id=getattr(context, "assign_in_group_user_id"), expect_error=False)
|
||||
|
||||
|
||||
@when("assign ticket to out_group employee (should fail)") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assign_ticket_out_group_employee(context) -> None:
|
||||
_assign_ticket_employee(context, employee_user_id=getattr(context, "assign_out_group_user_id"), expect_error=True)
|
||||
|
||||
|
||||
@when("prepare ticket and employees for unassign employee test") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_prepare_ticket_and_employees_for_unassign(context) -> None:
|
||||
"""
|
||||
Подготовка для unassign:
|
||||
- 1 категория + 1 группа категорий (для этой категории)
|
||||
- 2 работника:
|
||||
- fixed employee (в группе): user_id/employee_id заданы константами
|
||||
- новый employee (в группе) — его будем assign/unassign
|
||||
- 1 ticket (существующий по place_id либо созданный, если разрешено)
|
||||
"""
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
|
||||
query = """
|
||||
query ticketinfo($place_id: String!) {
|
||||
ticket(pagination:{skip:0,limit:25} ,filter:{place_id:$place_id}){
|
||||
results {
|
||||
id
|
||||
category { id title }
|
||||
assignee { id user { id username } }
|
||||
}
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
td.place_id = DEFAULT_TICKETINFO_PLACE_ID
|
||||
resp = execute_graphql(query=query, variables={"place_id": td.place_id}, company_id=td.company_id, access_token=token)
|
||||
results = resp.get("data", {}).get("ticket", {}).get("results", [])
|
||||
|
||||
if isinstance(results, list) and results:
|
||||
ticket0 = results[0]
|
||||
assert isinstance(ticket0, dict) and ticket0.get("id"), f"Некорректный ticket: {ticket0!r}"
|
||||
category = ticket0.get("category")
|
||||
assert isinstance(category, dict) and category.get("id"), f"У ticket нет category: {ticket0!r}"
|
||||
context.ticket_id = ticket0["id"]
|
||||
td.ticket_id = ticket0["id"]
|
||||
category_id = category["id"]
|
||||
else:
|
||||
# fallback: создаём свою заявку, если это разрешено
|
||||
td.place_id = None
|
||||
td.ensure_place()
|
||||
category_id = td.ensure_ticket_category()
|
||||
try:
|
||||
td.create_ticket_with_category(category_id=category_id, place_id=td.place_id)
|
||||
except AssertionError as e:
|
||||
raise AssertionError(
|
||||
"Нет доступных tickets для проверки unassignTicketEmployee (по умолчанию берём place_id "
|
||||
f"{DEFAULT_TICKETINFO_PLACE_ID}) и createTicket запрещён на стенде. "
|
||||
"Укажите place_id с существующими заявками (поменяйте DEFAULT_TICKETINFO_PLACE_ID в шаге) "
|
||||
"или дайте права на createTicket. "
|
||||
f"Детали: {e}"
|
||||
)
|
||||
context.ticket_id = td.ticket_id
|
||||
|
||||
# новый employee (его будем assign/unassign)
|
||||
td_new = TicketTestData(company_id=td.company_id)
|
||||
td_new.access_token = td.access_token
|
||||
td_new._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
td_new.create_employee()
|
||||
assert td_new.account_id and td_new.employee_id, "Не создался новый employee для unassign"
|
||||
|
||||
fixed_member = {"user_id": td.fixed_user_id, "employee_id": td.fixed_employee_id}
|
||||
new_member = {"user_id": td_new.account_id, "employee_id": td_new.employee_id}
|
||||
td.create_category_group_for_categories([category_id], members=[fixed_member, new_member], cache=False)
|
||||
|
||||
context.unassign_new_user_id = td_new.account_id
|
||||
|
||||
|
||||
@when("assign ticket to new grouped employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assign_ticket_to_new_grouped_employee(context) -> None:
|
||||
_assign_ticket_employee(context, employee_user_id=getattr(context, "unassign_new_user_id"), expect_error=False)
|
||||
|
||||
|
||||
@when("unassign ticket from new grouped employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_unassign_ticket_from_new_grouped_employee(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
token = td.ensure_token()
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
employee_user_id = getattr(context, "unassign_new_user_id", None)
|
||||
assert isinstance(employee_user_id, str) and employee_user_id, "Нет unassign_new_user_id."
|
||||
|
||||
mutation = """
|
||||
mutation unassignTicketEmployee($ticket_id: String!, $employee_user_id: String!) {
|
||||
unassignTicketEmployee(dto: {ticket_id: $ticket_id, employee_user_id: $employee_user_id})
|
||||
}
|
||||
""".strip()
|
||||
variables = {"ticket_id": ticket_id, "employee_user_id": employee_user_id}
|
||||
resp = execute_graphql(query=mutation, variables=variables, company_id=td.company_id, access_token=token)
|
||||
context.unassign_ticket_employee_response = resp
|
||||
|
||||
|
||||
def _get_ticket_assignee_user_id(context) -> str | None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
ticket_id = td.ticket_id or getattr(context, "ticket_id", None)
|
||||
assert ticket_id, "Нет ticket_id."
|
||||
resp = getattr(context, "ticket_query_response", None)
|
||||
assert isinstance(resp, dict), f"Нет ticket_query_response: {resp!r}"
|
||||
results = resp.get("data", {}).get("ticket", {}).get("results", [])
|
||||
assert isinstance(results, list), f"ticket.results не list: {results!r}"
|
||||
for item in results:
|
||||
if isinstance(item, dict) and item.get("id") == ticket_id:
|
||||
assignee = item.get("assignee")
|
||||
if assignee is None:
|
||||
return None
|
||||
if isinstance(assignee, dict) and isinstance(assignee.get("user"), dict):
|
||||
return assignee["user"].get("id")
|
||||
raise AssertionError(f"Неожиданная структура assignee: {assignee!r}")
|
||||
raise AssertionError(f"Не нашли ticket id={ticket_id} в results")
|
||||
|
||||
|
||||
@then("ticket assignee is fixed employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_assignee_fixed(context) -> None:
|
||||
assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_fixed_user_id")
|
||||
|
||||
|
||||
@then("ticket assignee is new in_group employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_assignee_new_in_group(context) -> None:
|
||||
assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_in_group_user_id")
|
||||
|
||||
|
||||
@then("ticket assignee is still new in_group employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_assignee_still_new_in_group(context) -> None:
|
||||
assert _get_ticket_assignee_user_id(context) == getattr(context, "assign_in_group_user_id")
|
||||
|
||||
|
||||
@then("ticket assignee is new grouped employee") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_assignee_new_grouped_employee(context) -> None:
|
||||
assert _get_ticket_assignee_user_id(context) == getattr(context, "unassign_new_user_id")
|
||||
|
||||
|
||||
@then("ticket assignee is empty") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_assert_assignee_empty(context) -> None:
|
||||
assignee_user_id = _get_ticket_assignee_user_id(context)
|
||||
assert assignee_user_id is None, f"Ожидали пустой assignee после unassign, получили user_id={assignee_user_id!r}"
|
||||
29
Ticket/features/steps/ticket_testdata_steps.py
Normal file
29
Ticket/features/steps/ticket_testdata_steps.py
Normal file
@ -0,0 +1,29 @@
|
||||
# pyright: reportCallIssue=false
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from behave import when
|
||||
|
||||
from Ticket.testdata.ticket_test_data import TicketTestData
|
||||
|
||||
|
||||
@when("create place multiple for ticket") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_place_multiple(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.ensure_place()
|
||||
context.ticket_place_id = td.place_id
|
||||
|
||||
|
||||
@when("create ticket category for created place") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_ticket_category_for_place(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.ensure_ticket_category()
|
||||
context.ticket_category_id = td.category_id
|
||||
|
||||
|
||||
@when("create ticket for created place and category") # pyright: ignore[reportGeneralTypeIssues]
|
||||
def step_create_ticket(context) -> None:
|
||||
td = TicketTestData.from_behave_context(context)
|
||||
td.ensure_ticket()
|
||||
context.ticket_id = td.ticket_id
|
||||
|
||||
460
Ticket/testdata/ticket_test_data.py
vendored
Normal file
460
Ticket/testdata/ticket_test_data.py
vendored
Normal file
@ -0,0 +1,460 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import random
|
||||
import time
|
||||
from dataclasses import dataclass
|
||||
from dataclasses import field
|
||||
from typing import Any, Callable, Optional
|
||||
|
||||
import allure # pyright: ignore[reportMissingImports]
|
||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||
|
||||
from worklib import admin_data
|
||||
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
||||
|
||||
|
||||
def _attach_json(name: str, payload: Any) -> None:
|
||||
allure.attach(
|
||||
json.dumps(payload, ensure_ascii=False, indent=2),
|
||||
name=name,
|
||||
attachment_type=AttachmentType.JSON,
|
||||
)
|
||||
|
||||
|
||||
def _exec_or_fail(*, op_name: str, token: str, query: str, variables: dict[str, Any] | None = None, company_id: str) -> dict[str, Any]:
|
||||
try:
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
variables=variables,
|
||||
company_id=company_id,
|
||||
access_token=token,
|
||||
)
|
||||
except PermissionError as e:
|
||||
allure.attach(str(e), name=f"Forbidden: {op_name}", attachment_type=AttachmentType.TEXT)
|
||||
raise AssertionError(f"Forbidden на операции: {op_name}") from e
|
||||
|
||||
|
||||
@dataclass
|
||||
class TicketTestData:
|
||||
"""
|
||||
Хранилище/фабрика тестовых данных для Ticket GraphQL.
|
||||
|
||||
- Создаёт сущности (place/category/ticket/user/employee/...) по мере необходимости
|
||||
- Кеширует полученные id в полях
|
||||
- Регистрирует cleanup в behave context (если передан)
|
||||
"""
|
||||
|
||||
company_id: str = DEFAULT_COMPANY_ID
|
||||
parent_place_id: str = "6915dc03462d5aea0adc8cbd"
|
||||
default_user_first_name: str = "kvstest1"
|
||||
default_user_last_name: str = "kvstest2"
|
||||
|
||||
# Employee that must exist on the stand and be present in multiple groups
|
||||
fixed_user_id: str = "e47362a9-5354-4b42-97cc-c00dfe1c54f1"
|
||||
fixed_employee_id: str = "69cbe1d59547f08c1cf556ff"
|
||||
|
||||
# cached ids
|
||||
access_token: Optional[str] = None
|
||||
place_id: Optional[str] = None
|
||||
category_id: Optional[str] = None
|
||||
ticket_id: Optional[str] = None
|
||||
account_id: Optional[str] = None
|
||||
employee_id: Optional[str] = None
|
||||
category_group_id: Optional[str] = None
|
||||
username: Optional[str] = None
|
||||
# title -> category_id
|
||||
category_ids: dict[str, str] = field(default_factory=dict)
|
||||
# role -> title (последние 3 категории для теста смены категории)
|
||||
last_category_titles_by_role: dict[str, str] = field(default_factory=dict)
|
||||
|
||||
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
||||
|
||||
@classmethod
|
||||
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "TicketTestData":
|
||||
td: TicketTestData | None = getattr(context, "ticket_test_data", None)
|
||||
if isinstance(td, cls):
|
||||
if not td.access_token and getattr(context, "access_token", None):
|
||||
td.access_token = context.access_token
|
||||
if not td._cleanup_fns:
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
return td
|
||||
|
||||
td = cls(company_id=company_id)
|
||||
td.access_token = getattr(context, "access_token", None) or None
|
||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||
setattr(context, "ticket_test_data", td)
|
||||
return td
|
||||
|
||||
def _register_cleanup(self, fn: Callable[[], None]) -> None:
|
||||
if self._cleanup_fns is not None:
|
||||
self._cleanup_fns.append(fn)
|
||||
|
||||
def ensure_token(self) -> str:
|
||||
if self.access_token:
|
||||
return self.access_token
|
||||
token = admin_data.get_or_create_user("tester").access_token
|
||||
assert token, "Нет access_token (ни в context, ни в admin_data.get_or_create_user('tester'))."
|
||||
self.access_token = token
|
||||
return token
|
||||
|
||||
def ensure_place(self) -> str:
|
||||
if self.place_id:
|
||||
return self.place_id
|
||||
token = self.ensure_token()
|
||||
mutation = """
|
||||
mutation ($place_type: PlaceType!, $names: [String!]!, $parent_id: String, $address_id: String) {
|
||||
createPlaceMultiple(
|
||||
dto: {place_type: $place_type, names: $names, parent_id: $parent_id, address_id: $address_id}
|
||||
) {
|
||||
id
|
||||
__typename
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"names": ["test"], "parent_id": self.parent_place_id, "place_type": "flat"}
|
||||
with allure.step("GraphQL: createPlaceMultiple"):
|
||||
resp = _exec_or_fail(op_name="createPlaceMultiple(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createPlaceMultiple response", resp)
|
||||
created = resp.get("data", {}).get("createPlaceMultiple")
|
||||
if isinstance(created, list):
|
||||
assert created, f"createPlaceMultiple вернул пустой список. Ответ: {resp}"
|
||||
created0 = created[0]
|
||||
else:
|
||||
created0 = created
|
||||
assert isinstance(created0, dict), f"createPlaceMultiple вернул не объект: {created0!r}. Ответ: {resp}"
|
||||
place_id = created0.get("id")
|
||||
assert place_id, f"createPlaceMultiple не вернул id. Ответ: {resp}"
|
||||
self.place_id = place_id
|
||||
|
||||
def _cleanup_delete_place() -> None:
|
||||
delete_mutation = """mutation deleteplace($id: String!) { deletePlace(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deletePlace(mutation)", token=token, query=delete_mutation, variables={"id": place_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_place)
|
||||
return place_id
|
||||
|
||||
def ensure_ticket_category(self) -> str:
|
||||
if self.category_id:
|
||||
return self.category_id
|
||||
token = self.ensure_token()
|
||||
place_id = self.ensure_place()
|
||||
mutation = """
|
||||
mutation ($input: CreateTicketCategoryInput!) {
|
||||
createTicketCategory(dto: $input) {
|
||||
id
|
||||
title
|
||||
place_ids
|
||||
company_id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"input": {"title": "tester1", "place_ids": [place_id], "company_id": self.company_id}}
|
||||
with allure.step("GraphQL: createTicketCategory"):
|
||||
resp = _exec_or_fail(op_name="createTicketCategory(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createTicketCategory response", resp)
|
||||
category = resp.get("data", {}).get("createTicketCategory")
|
||||
assert isinstance(category, dict) and category.get("id"), f"createTicketCategory не вернул id. Ответ: {resp}"
|
||||
category_id = category["id"]
|
||||
self.category_id = category_id
|
||||
|
||||
def _cleanup_delete_category() -> None:
|
||||
delete_mutation = """mutation deleteTicketcategory($id: String!) { deleteTicketCategory(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deleteTicketCategory(mutation)", token=token, query=delete_mutation, variables={"id": category_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_category)
|
||||
return category_id
|
||||
|
||||
def create_ticket_category(self, *, title: str, place_id: Optional[str] = None) -> str:
|
||||
"""
|
||||
Создаёт ticket category (не перетирая self.category_id), сохраняет в self.category_ids[title].
|
||||
"""
|
||||
if title in self.category_ids:
|
||||
return self.category_ids[title]
|
||||
token = self.ensure_token()
|
||||
pid = place_id or self.ensure_place()
|
||||
mutation = """
|
||||
mutation ($input: CreateTicketCategoryInput!) {
|
||||
createTicketCategory(dto: $input) {
|
||||
id
|
||||
title
|
||||
place_ids
|
||||
company_id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"input": {"title": title, "place_ids": [pid], "company_id": self.company_id}}
|
||||
with allure.step(f"GraphQL: createTicketCategory ({title})"):
|
||||
resp = _exec_or_fail(op_name="createTicketCategory(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createTicketCategory response", resp)
|
||||
category = resp.get("data", {}).get("createTicketCategory")
|
||||
assert isinstance(category, dict) and category.get("id"), f"createTicketCategory не вернул id. Ответ: {resp}"
|
||||
category_id = category["id"]
|
||||
self.category_ids[title] = category_id
|
||||
|
||||
def _cleanup_delete_category() -> None:
|
||||
delete_mutation = """mutation deleteTicketcategory($id: String!) { deleteTicketCategory(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deleteTicketCategory(mutation)", token=token, query=delete_mutation, variables={"id": category_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_category)
|
||||
return category_id
|
||||
|
||||
def ensure_three_ticket_categories(self) -> dict[str, str]:
|
||||
"""
|
||||
Создаёт 3 категории с разными названиями под текущий place.
|
||||
Возвращает dict с ключами: old/in_group/out_group.
|
||||
"""
|
||||
pid = self.ensure_place()
|
||||
suffix = str(int(time.time()))
|
||||
titles = {
|
||||
"old": f"cat-old-{suffix}",
|
||||
"in_group": f"cat-in-group-{suffix}",
|
||||
"out_group": f"cat-out-group-{suffix}",
|
||||
}
|
||||
self.last_category_titles_by_role = titles
|
||||
ids = {k: self.create_ticket_category(title=v, place_id=pid) for k, v in titles.items()}
|
||||
return ids
|
||||
|
||||
def create_ticket_with_category(self, *, category_id: str, place_id: Optional[str] = None) -> str:
|
||||
"""
|
||||
Создаёт ticket с указанными place_id и category_id.
|
||||
ВАЖНО: если на стенде нет прав на createTicket, тест должен падать (как и требуется).
|
||||
"""
|
||||
token = self.ensure_token()
|
||||
pid = place_id or self.ensure_place()
|
||||
mutation = """
|
||||
mutation createticket($category_id: String!, $place_id: String!) {
|
||||
createTicket(dto: {subject:"my first ticket", body:"try a ticket", category_id:$category_id, place_id:$place_id }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"category_id": category_id, "place_id": pid}
|
||||
with allure.step("GraphQL: createTicket"):
|
||||
resp = _exec_or_fail(op_name="createTicket(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createTicket response", resp)
|
||||
ticket_id = resp.get("data", {}).get("createTicket", {}).get("id")
|
||||
assert ticket_id, f"createTicket не вернул id. Ответ: {resp}"
|
||||
self.ticket_id = ticket_id
|
||||
|
||||
def _cleanup_delete_ticket() -> None:
|
||||
delete_mutation = """mutation deleteTicket($id: String!) { deleteTicket(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deleteTicket(mutation)", token=token, query=delete_mutation, variables={"id": ticket_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_ticket)
|
||||
return ticket_id
|
||||
|
||||
def ensure_ticket(self) -> str:
|
||||
if self.ticket_id:
|
||||
return self.ticket_id
|
||||
token = self.ensure_token()
|
||||
place_id = self.ensure_place()
|
||||
category_id = self.ensure_ticket_category()
|
||||
mutation = """
|
||||
mutation createticket($category_id: String!, $place_id: String!) {
|
||||
createTicket(dto: {subject:"my first ticket", body:"try a ticket", category_id:$category_id, place_id:$place_id }) {
|
||||
id
|
||||
}
|
||||
}
|
||||
""".strip()
|
||||
variables = {"category_id": category_id, "place_id": place_id}
|
||||
with allure.step("GraphQL: createTicket"):
|
||||
resp = _exec_or_fail(op_name="createTicket(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createTicket response", resp)
|
||||
ticket_id = resp.get("data", {}).get("createTicket", {}).get("id")
|
||||
assert ticket_id, f"createTicket не вернул id. Ответ: {resp}"
|
||||
self.ticket_id = ticket_id
|
||||
|
||||
def _cleanup_delete_ticket() -> None:
|
||||
delete_mutation = """mutation deleteTicket($id: String!) { deleteTicket(id: $id) }""".strip()
|
||||
_exec_or_fail(op_name="deleteTicket(mutation)", token=token, query=delete_mutation, variables={"id": ticket_id}, company_id=self.company_id)
|
||||
|
||||
# Удалять ticket первым (до категории/плейса).
|
||||
self._register_cleanup(_cleanup_delete_ticket)
|
||||
return ticket_id
|
||||
|
||||
def create_user(self) -> str:
|
||||
if self.account_id:
|
||||
return self.account_id
|
||||
token = self.ensure_token()
|
||||
username = f"+7999{random.randint(1000000, 9999999)}"
|
||||
mutation = """
|
||||
mutation createUser($input: CreateAccountDTO!) {
|
||||
createUser(dto: $input)
|
||||
}
|
||||
""".strip()
|
||||
variables = {
|
||||
"input": {
|
||||
"username": username,
|
||||
"first_name": self.default_user_first_name,
|
||||
"last_name": self.default_user_last_name,
|
||||
"is_demo": True,
|
||||
"password": "",
|
||||
}
|
||||
}
|
||||
with allure.step("GraphQL: createUser"):
|
||||
resp = _exec_or_fail(op_name="createUser(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createUser response", resp)
|
||||
created = resp.get("data", {}).get("createUser")
|
||||
if isinstance(created, dict):
|
||||
account_id = created.get("id")
|
||||
returned_username = created.get("username")
|
||||
else:
|
||||
account_id = created
|
||||
returned_username = None
|
||||
assert isinstance(account_id, str) and account_id, f"createUser не вернул id. Ответ: {resp}"
|
||||
self.account_id = account_id
|
||||
self.username = returned_username or username
|
||||
|
||||
def _cleanup_delete_user() -> None:
|
||||
delete_mutation = """
|
||||
mutation deleteUser($account_id: String!) { deleteUser(account_id: $account_id) }
|
||||
""".strip()
|
||||
_exec_or_fail(
|
||||
op_name="deleteUser(mutation)",
|
||||
token=token,
|
||||
query=delete_mutation,
|
||||
variables={"account_id": account_id},
|
||||
company_id=self.company_id,
|
||||
)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_user)
|
||||
return account_id
|
||||
|
||||
def create_employee(self) -> str:
|
||||
if self.employee_id:
|
||||
return self.employee_id
|
||||
token = self.ensure_token()
|
||||
account_id = self.create_user()
|
||||
mutation_with_status = """
|
||||
mutation createEmployee($user_id: String!) {
|
||||
addEmployee(dto: {user_id: $user_id, attributes: [], role: admin, company_id: "%s"}) {
|
||||
id
|
||||
status
|
||||
}
|
||||
}
|
||||
""".strip() % self.company_id
|
||||
mutation_without_status = """
|
||||
mutation createEmployee($user_id: String!) {
|
||||
addEmployee(dto: {user_id: $user_id, attributes: [], role: admin, company_id: "%s"}) {
|
||||
id
|
||||
}
|
||||
}
|
||||
""".strip() % self.company_id
|
||||
with allure.step("GraphQL: addEmployee"):
|
||||
try:
|
||||
resp = _exec_or_fail(
|
||||
op_name="addEmployee(mutation, with status)",
|
||||
token=token,
|
||||
query=mutation_with_status,
|
||||
variables={"user_id": account_id},
|
||||
company_id=self.company_id,
|
||||
)
|
||||
except RuntimeError as e:
|
||||
# На стенде поле EmployeeObject.status иногда ломает GraphQL (500: non-nullable + null).
|
||||
if "EmployeeObject.status" not in str(e):
|
||||
raise
|
||||
allure.attach(
|
||||
str(e),
|
||||
name="Skipping employee.status check (API bug)",
|
||||
attachment_type=AttachmentType.TEXT,
|
||||
)
|
||||
resp = _exec_or_fail(
|
||||
op_name="addEmployee(mutation, without status)",
|
||||
token=token,
|
||||
query=mutation_without_status,
|
||||
variables={"user_id": account_id},
|
||||
company_id=self.company_id,
|
||||
)
|
||||
_attach_json("addEmployee response", resp)
|
||||
add_employee = resp.get("data", {}).get("addEmployee", {})
|
||||
employee_id = add_employee.get("id") if isinstance(add_employee, dict) else None
|
||||
assert employee_id, f"addEmployee не вернул employee_id. Ответ: {resp}"
|
||||
status = add_employee.get("status") if isinstance(add_employee, dict) else None
|
||||
if status is not None:
|
||||
assert status == "active", f"status при создании employee должен быть active, получено: {status!r}. Ответ: {resp}"
|
||||
self.employee_id = employee_id
|
||||
return employee_id
|
||||
|
||||
def create_category_group(self) -> str:
|
||||
category_id = self.ensure_ticket_category()
|
||||
return self.create_category_group_for_categories([category_id])
|
||||
|
||||
def create_category_group_for_categories(
|
||||
self,
|
||||
category_ids: list[str],
|
||||
*,
|
||||
members: Optional[list[dict[str, str]]] = None,
|
||||
cache: bool = True,
|
||||
) -> str:
|
||||
"""
|
||||
Создаёт CategoryGroup для указанных категорий.
|
||||
|
||||
- members: список участников вида {"user_id": "...", "employee_id": "..."}
|
||||
Если не передан, создаётся один новый user/employee и добавляется в группу.
|
||||
- cache: если True, сохраняет group_id в self.category_group_id (старое поведение).
|
||||
Если False, позволяет создавать несколько групп в рамках одного теста.
|
||||
"""
|
||||
if cache and self.category_group_id:
|
||||
return self.category_group_id
|
||||
assert category_ids and all(isinstance(x, str) and x for x in category_ids), f"category_ids пустой/некорректный: {category_ids!r}"
|
||||
|
||||
token = self.ensure_token()
|
||||
if members is None:
|
||||
account_id = self.create_user()
|
||||
employee_id = self.create_employee()
|
||||
members = [{"user_id": account_id, "employee_id": employee_id}]
|
||||
assert members and all(isinstance(m, dict) and m.get("user_id") and m.get("employee_id") for m in members), f"members некорректный: {members!r}"
|
||||
mutation = """
|
||||
mutation createcategoryGroup($name: String!, $category_ids: [String!]!, $employees: [GroupMemberDto!]!) {
|
||||
createCategoryGroup(dto: {
|
||||
name: $name,
|
||||
employees: $employees,
|
||||
company_id: "%s",
|
||||
category_ids: $category_ids
|
||||
}) { id }
|
||||
}
|
||||
""".strip() % self.company_id
|
||||
variables = {
|
||||
"name": f"tester-{int(time.time())}",
|
||||
"category_ids": category_ids,
|
||||
"employees": members,
|
||||
}
|
||||
with allure.step("GraphQL: createCategoryGroup"):
|
||||
resp = _exec_or_fail(op_name="createCategoryGroup(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("createCategoryGroup response", resp)
|
||||
group_id = resp.get("data", {}).get("createCategoryGroup", {}).get("id")
|
||||
assert group_id, f"createCategoryGroup не вернул id. Ответ: {resp}"
|
||||
if cache:
|
||||
self.category_group_id = group_id
|
||||
|
||||
def _cleanup_delete_group() -> None:
|
||||
delete_mutation = """
|
||||
mutation deletecategoryGroup($id: String!) { deleteCategoryGroup(id: $id) }
|
||||
""".strip()
|
||||
_exec_or_fail(op_name="deleteCategoryGroup(mutation)", token=token, query=delete_mutation, variables={"id": group_id}, company_id=self.company_id)
|
||||
|
||||
self._register_cleanup(_cleanup_delete_group)
|
||||
return group_id
|
||||
|
||||
def connect_employee_to_category_group(self) -> None:
|
||||
token = self.ensure_token()
|
||||
group_id = self.create_category_group()
|
||||
employee_id = self.create_employee()
|
||||
account_id = self.account_id
|
||||
_attach_json("connectEmployee inputs", {"group_id": group_id, "employee_id": employee_id, "account_id": account_id})
|
||||
|
||||
def _looks_like_uuid(value: str) -> bool:
|
||||
return isinstance(value, str) and value.count("-") == 4 and len(value) >= 32
|
||||
|
||||
mutation = """
|
||||
mutation connectEmployee($input: AddEmployeesToCategoryGroupDTO!) {
|
||||
addEmployeesToCategoryGroup(dto: $input)
|
||||
}
|
||||
""".strip()
|
||||
employee_ids = [employee_id] if _looks_like_uuid(employee_id) else ([account_id] if account_id else [employee_id])
|
||||
variables = {"input": {"id": group_id, "employee_ids": employee_ids}}
|
||||
with allure.step("GraphQL: addEmployeesToCategoryGroup (connectEmployee)"):
|
||||
resp = _exec_or_fail(op_name="addEmployeesToCategoryGroup(mutation)", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||
_attach_json("addEmployeesToCategoryGroup response", resp)
|
||||
|
||||
2
allure-report/app.js
Normal file
2
allure-report/app.js
Normal file
File diff suppressed because one or more lines are too long
21
allure-report/data/attachments/1aab0f3339a11384.json
Normal file
21
allure-report/data/attachments/1aab0f3339a11384.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a5c963548ff04f752605",
|
||||
"members": [
|
||||
{
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"username": "+79997035425"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/1f74595bdb2dbf3e.json
Normal file
8
allure-report/data/attachments/1f74595bdb2dbf3e.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a59d0bfa76f761dd1b02",
|
||||
"member_id": "71cd5726-c404-490f-bb39-49fd3c52e9db"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
allure-report/data/attachments/29ddf7dfcd55804d.json
Normal file
25
allure-report/data/attachments/29ddf7dfcd55804d.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"data": {
|
||||
"createSubscription": {
|
||||
"id": "69e8a81866221071869bcc3e",
|
||||
"services": [
|
||||
{
|
||||
"id": "69e8a8181a037043d86d23dd",
|
||||
"title": "kvs-service-1776855066"
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2"
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"id": "69e8a81866221071869bcc3d",
|
||||
"title": "plan-kvs-1776855066"
|
||||
},
|
||||
"place_id": "69e8a8180bfa76f761dd1b20"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/367c9a130542d7dd.json
Normal file
8
allure-report/data/attachments/367c9a130542d7dd.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a5c963548ff04f752605",
|
||||
"member_id": "27a692df-2cd4-45e7-97db-a8696f2691af"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/378376b29d7b9701.json
Normal file
10
allure-report/data/attachments/378376b29d7b9701.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a59d0bfa76f761dd1b02",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/38c433f3956a6c24.json
Normal file
10
allure-report/data/attachments/38c433f3956a6c24.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a5c863548ff04f752602",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/396b351bade8c719.json
Normal file
18
allure-report/data/attachments/396b351bade8c719.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"created_at": "2026-04-22T10:51:04.774Z",
|
||||
"updated_at": "2026-04-22T10:51:04.774Z",
|
||||
"username": "+79996530370",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/40934732210e9bb6.json
Normal file
8
allure-report/data/attachments/40934732210e9bb6.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a5c95d6417545a7f4848",
|
||||
"member_id": "c89ab70b-4159-4cee-b9a1-01883b645b75"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/41dfa1c4132a125.json
Normal file
21
allure-report/data/attachments/41dfa1c4132a125.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a8180bfa76f761dd1b20",
|
||||
"members": [
|
||||
{
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"username": "+79996530370"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
9
allure-report/data/attachments/44503099ef7202b2.json
Normal file
9
allure-report/data/attachments/44503099ef7202b2.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"data": {
|
||||
"createService": {
|
||||
"id": "69e8a8181a037043d86d23dd",
|
||||
"title": "kvs-service-1776855066",
|
||||
"type": "access"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/48650f521ac6db09.json
Normal file
21
allure-report/data/attachments/48650f521ac6db09.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a59d0bfa76f761dd1b02",
|
||||
"members": [
|
||||
{
|
||||
"id": "71cd5726-c404-490f-bb39-49fd3c52e9db",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "71cd5726-c404-490f-bb39-49fd3c52e9db",
|
||||
"username": "+79992490173"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/491f5cc788feffbf.json
Normal file
10
allure-report/data/attachments/491f5cc788feffbf.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a8180bfa76f761dd1b20",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
20
allure-report/data/attachments/53b395d7395630f4.json
Normal file
20
allure-report/data/attachments/53b395d7395630f4.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlan": {
|
||||
"id": "69e8a5c966221071869bcc3b",
|
||||
"service_ids": [
|
||||
"69e8a5c91a037043d86d23d8"
|
||||
],
|
||||
"bundle_ids": [],
|
||||
"place_id": "69e8a5c963548ff04f752605",
|
||||
"place_ids": [
|
||||
"69e8a5c963548ff04f752605"
|
||||
],
|
||||
"price": 200,
|
||||
"title": "plan-kvs-1776854475",
|
||||
"discount": "0",
|
||||
"payment_interval": 1,
|
||||
"price_without_discount": null
|
||||
}
|
||||
}
|
||||
}
|
||||
12
allure-report/data/attachments/54bf6b4cd11c49ba.json
Normal file
12
allure-report/data/attachments/54bf6b4cd11c49ba.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a5c863548ff04f752602",
|
||||
"members": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/59132c0415da35d2.json
Normal file
18
allure-report/data/attachments/59132c0415da35d2.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "e03812e4-8689-4689-b561-29a40b290851",
|
||||
"created_at": "2026-04-22T10:51:04.087Z",
|
||||
"updated_at": "2026-04-22T10:51:04.087Z",
|
||||
"username": "+79999216433",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
12
allure-report/data/attachments/5f3bbbf8ce320408.json
Normal file
12
allure-report/data/attachments/5f3bbbf8ce320408.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a8170bfa76f761dd1b19",
|
||||
"members": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/63d52fcc0da5c578.json
Normal file
10
allure-report/data/attachments/63d52fcc0da5c578.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a59c5d6417545a7f4829",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/6546748ec0ab8329.json
Normal file
10
allure-report/data/attachments/6546748ec0ab8329.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a5c95d6417545a7f4848",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/656f70177aed963d.json
Normal file
21
allure-report/data/attachments/656f70177aed963d.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a5c963548ff04f752605",
|
||||
"members": [
|
||||
{
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"username": "+79997035425"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/665acbd07f5e77af.json
Normal file
10
allure-report/data/attachments/665acbd07f5e77af.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a8185d6417545a7f4863",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/6d590f15fa51bd4c.json
Normal file
18
allure-report/data/attachments/6d590f15fa51bd4c.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"created_at": "2026-04-22T10:40:30.004Z",
|
||||
"updated_at": "2026-04-22T10:40:30.004Z",
|
||||
"username": "+79994167356",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
16
allure-report/data/attachments/76cef127604dbda8.txt
Normal file
16
allure-report/data/attachments/76cef127604dbda8.txt
Normal file
@ -0,0 +1,16 @@
|
||||
Traceback (most recent call last):
|
||||
File "KVSTest\features\environment.py", line 21, in after_scenario
|
||||
fn()
|
||||
~~^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 230, in _cleanup_delete_subscription
|
||||
_exec_or_fail(op_name="deleteSubscription(mutation)", token=token, query=del_mut, variables={"id": subscription_id}, company_id=self.company_id)
|
||||
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 25, in _exec_or_fail
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
...<2 lines>...
|
||||
access_token=token,
|
||||
)
|
||||
File "C:\Users\Степаан\PycharmProjects\work\worklib\graphql_client.py", line 65, in execute_graphql
|
||||
raise RuntimeError(f"GraphQL errors: {errors}")
|
||||
RuntimeError: GraphQL errors: [{'message': 'Not Found', 'code': 'Client Error', 'status': 404, 'description': 'The server has not found anything matching the Request-URI'}]
|
||||
8
allure-report/data/attachments/7754937c407a360d.json
Normal file
8
allure-report/data/attachments/7754937c407a360d.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a5c963548ff04f752605",
|
||||
"member_id": "27a692df-2cd4-45e7-97db-a8696f2691af"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
allure-report/data/attachments/80b593c242e8118c.json
Normal file
20
allure-report/data/attachments/80b593c242e8118c.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlan": {
|
||||
"id": "69e8a81866221071869bcc3d",
|
||||
"service_ids": [
|
||||
"69e8a8181a037043d86d23dd"
|
||||
],
|
||||
"bundle_ids": [],
|
||||
"place_id": "69e8a8180bfa76f761dd1b20",
|
||||
"place_ids": [
|
||||
"69e8a8180bfa76f761dd1b20"
|
||||
],
|
||||
"price": 200,
|
||||
"title": "plan-kvs-1776855066",
|
||||
"discount": "0",
|
||||
"payment_interval": 1,
|
||||
"price_without_discount": null
|
||||
}
|
||||
}
|
||||
}
|
||||
5
allure-report/data/attachments/80e82bb57c98f39e.json
Normal file
5
allure-report/data/attachments/80e82bb57c98f39e.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"data": {
|
||||
"deleteSubscription": true
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/81111fcbd74b07a8.json
Normal file
18
allure-report/data/attachments/81111fcbd74b07a8.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "71cd5726-c404-490f-bb39-49fd3c52e9db",
|
||||
"created_at": "2026-04-22T10:40:29.354Z",
|
||||
"updated_at": "2026-04-22T10:40:29.354Z",
|
||||
"username": "+79992490173",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/837cf6ef7b1b67f6.json
Normal file
10
allure-report/data/attachments/837cf6ef7b1b67f6.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a59d5d6417545a7f4844",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
9
allure-report/data/attachments/83ed91fdc971f7d2.json
Normal file
9
allure-report/data/attachments/83ed91fdc971f7d2.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"data": {
|
||||
"createService": {
|
||||
"id": "69e8a5c91a037043d86d23d8",
|
||||
"title": "kvs-service-1776854475",
|
||||
"type": "access"
|
||||
}
|
||||
}
|
||||
}
|
||||
12
allure-report/data/attachments/85791a35718da0ec.json
Normal file
12
allure-report/data/attachments/85791a35718da0ec.json
Normal file
@ -0,0 +1,12 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a59c5d6417545a7f4829",
|
||||
"members": []
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/85a124398c66573a.json
Normal file
21
allure-report/data/attachments/85a124398c66573a.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a8180bfa76f761dd1b20",
|
||||
"members": [
|
||||
{
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1",
|
||||
"username": "+79996530370"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
15
allure-report/data/attachments/88688f49a102cb3d.json
Normal file
15
allure-report/data/attachments/88688f49a102cb3d.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": {
|
||||
"invoices": [
|
||||
{
|
||||
"id": "69e8a59e1a037043d86d23d7",
|
||||
"price": 200,
|
||||
"status": "pending",
|
||||
"subscriptions": [
|
||||
"69e8a59e1a037043d86d23d6"
|
||||
],
|
||||
"place_id": "69e8a59d5d6417545a7f4844"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
15
allure-report/data/attachments/8c91dd2c7a5698ca.json
Normal file
15
allure-report/data/attachments/8c91dd2c7a5698ca.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": {
|
||||
"invoices": [
|
||||
{
|
||||
"id": "69e8a5c91a037043d86d23db",
|
||||
"price": 200,
|
||||
"status": "pending",
|
||||
"subscriptions": [
|
||||
"69e8a5c91a037043d86d23da"
|
||||
],
|
||||
"place_id": "69e8a5c963548ff04f752605"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/97b1f91ede190475.json
Normal file
18
allure-report/data/attachments/97b1f91ede190475.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "c89ab70b-4159-4cee-b9a1-01883b645b75",
|
||||
"created_at": "2026-04-22T10:41:13.059Z",
|
||||
"updated_at": "2026-04-22T10:41:13.059Z",
|
||||
"username": "+79993659600",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/99d52768ac3154a6.json
Normal file
8
allure-report/data/attachments/99d52768ac3154a6.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a59d5d6417545a7f4844",
|
||||
"member_id": "a6542878-eba4-45ff-b1ba-38fe31a1349e"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
allure-report/data/attachments/9ca25cc7ba048cfd.json
Normal file
20
allure-report/data/attachments/9ca25cc7ba048cfd.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlan": {
|
||||
"id": "69e8a59d1a037043d86d23d4",
|
||||
"service_ids": [
|
||||
"69e8a59d1a037043d86d23d3"
|
||||
],
|
||||
"bundle_ids": [],
|
||||
"place_id": "69e8a59d5d6417545a7f4844",
|
||||
"place_ids": [
|
||||
"69e8a59d5d6417545a7f4844"
|
||||
],
|
||||
"price": 200,
|
||||
"title": "plan-kvs-1776854432",
|
||||
"discount": "0",
|
||||
"payment_interval": 1,
|
||||
"price_without_discount": null
|
||||
}
|
||||
}
|
||||
}
|
||||
18
allure-report/data/attachments/9e5d490d4ef78e22.json
Normal file
18
allure-report/data/attachments/9e5d490d4ef78e22.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"data": {
|
||||
"createUser": {
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"created_at": "2026-04-22T10:41:13.668Z",
|
||||
"updated_at": "2026-04-22T10:41:13.668Z",
|
||||
"username": "+79997035425",
|
||||
"user_data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2",
|
||||
"email": ""
|
||||
},
|
||||
"is_demo": true,
|
||||
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||
"roles": []
|
||||
}
|
||||
}
|
||||
}
|
||||
25
allure-report/data/attachments/a190f1fcd0591b56.json
Normal file
25
allure-report/data/attachments/a190f1fcd0591b56.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"data": {
|
||||
"createSubscription": {
|
||||
"id": "69e8a5c91a037043d86d23da",
|
||||
"services": [
|
||||
{
|
||||
"id": "69e8a5c91a037043d86d23d8",
|
||||
"title": "kvs-service-1776854475"
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"id": "27a692df-2cd4-45e7-97db-a8696f2691af",
|
||||
"data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2"
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"id": "69e8a5c966221071869bcc3b",
|
||||
"title": "plan-kvs-1776854475"
|
||||
},
|
||||
"place_id": "69e8a5c963548ff04f752605"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/a89dd32350045c08.json
Normal file
21
allure-report/data/attachments/a89dd32350045c08.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a8185d6417545a7f4863",
|
||||
"members": [
|
||||
{
|
||||
"id": "e03812e4-8689-4689-b561-29a40b290851",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "e03812e4-8689-4689-b561-29a40b290851",
|
||||
"username": "+79999216433"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/a9f7f6046e5d4a4f.json
Normal file
10
allure-report/data/attachments/a9f7f6046e5d4a4f.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a8170bfa76f761dd1b19",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/ae294bec39e0eef0.json
Normal file
8
allure-report/data/attachments/ae294bec39e0eef0.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a59d5d6417545a7f4844",
|
||||
"member_id": "a6542878-eba4-45ff-b1ba-38fe31a1349e"
|
||||
}
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/afad259b291c1908.json
Normal file
8
allure-report/data/attachments/afad259b291c1908.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a8180bfa76f761dd1b20",
|
||||
"member_id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
allure-report/data/attachments/b033d501bace25ae.json
Normal file
5
allure-report/data/attachments/b033d501bace25ae.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"data": {
|
||||
"deleteSubscription": true
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/b36ae1ff3b5353be.json
Normal file
21
allure-report/data/attachments/b36ae1ff3b5353be.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a5c95d6417545a7f4848",
|
||||
"members": [
|
||||
{
|
||||
"id": "c89ab70b-4159-4cee-b9a1-01883b645b75",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "c89ab70b-4159-4cee-b9a1-01883b645b75",
|
||||
"username": "+79993659600"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
16
allure-report/data/attachments/bcfcd90aed1f9eba.txt
Normal file
16
allure-report/data/attachments/bcfcd90aed1f9eba.txt
Normal file
@ -0,0 +1,16 @@
|
||||
Traceback (most recent call last):
|
||||
File "KVSTest\features\environment.py", line 21, in after_scenario
|
||||
fn()
|
||||
~~^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 230, in _cleanup_delete_subscription
|
||||
_exec_or_fail(op_name="deleteSubscription(mutation)", token=token, query=del_mut, variables={"id": subscription_id}, company_id=self.company_id)
|
||||
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 25, in _exec_or_fail
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
...<2 lines>...
|
||||
access_token=token,
|
||||
)
|
||||
File "C:\Users\Степаан\PycharmProjects\work\worklib\graphql_client.py", line 65, in execute_graphql
|
||||
raise RuntimeError(f"GraphQL errors: {errors}")
|
||||
RuntimeError: GraphQL errors: [{'message': 'Not Found', 'code': 'Client Error', 'status': 404, 'description': 'The server has not found anything matching the Request-URI'}]
|
||||
15
allure-report/data/attachments/c30937d7188dd984.json
Normal file
15
allure-report/data/attachments/c30937d7188dd984.json
Normal file
@ -0,0 +1,15 @@
|
||||
{
|
||||
"data": {
|
||||
"invoices": [
|
||||
{
|
||||
"id": "69e8a81966221071869bcc3f",
|
||||
"price": 200,
|
||||
"status": "pending",
|
||||
"subscriptions": [
|
||||
"69e8a81866221071869bcc3e"
|
||||
],
|
||||
"place_id": "69e8a8180bfa76f761dd1b20"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/c9e3b231f365aad5.json
Normal file
21
allure-report/data/attachments/c9e3b231f365aad5.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a59d5d6417545a7f4844",
|
||||
"members": [
|
||||
{
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"username": "+79994167356"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
9
allure-report/data/attachments/d5647cda56154f2d.json
Normal file
9
allure-report/data/attachments/d5647cda56154f2d.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"data": {
|
||||
"createService": {
|
||||
"id": "69e8a59d1a037043d86d23d3",
|
||||
"title": "kvs-service-1776854432",
|
||||
"type": "access"
|
||||
}
|
||||
}
|
||||
}
|
||||
21
allure-report/data/attachments/d5f63016df73b96b.json
Normal file
21
allure-report/data/attachments/d5f63016df73b96b.json
Normal file
@ -0,0 +1,21 @@
|
||||
{
|
||||
"data": {
|
||||
"place": {
|
||||
"results": [
|
||||
{
|
||||
"id": "69e8a59d5d6417545a7f4844",
|
||||
"members": [
|
||||
{
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"parent_id": null,
|
||||
"user": {
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"username": "+79994167356"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
16
allure-report/data/attachments/da8bb627749ff3e5.txt
Normal file
16
allure-report/data/attachments/da8bb627749ff3e5.txt
Normal file
@ -0,0 +1,16 @@
|
||||
Traceback (most recent call last):
|
||||
File "KVSTest\features\environment.py", line 21, in after_scenario
|
||||
fn()
|
||||
~~^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 230, in _cleanup_delete_subscription
|
||||
_exec_or_fail(op_name="deleteSubscription(mutation)", token=token, query=del_mut, variables={"id": subscription_id}, company_id=self.company_id)
|
||||
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
File "C:\Users\Степаан\PycharmProjects\work\KVSTest\testdata\subscription_test_data.py", line 25, in _exec_or_fail
|
||||
return execute_graphql(
|
||||
query=query,
|
||||
...<2 lines>...
|
||||
access_token=token,
|
||||
)
|
||||
File "C:\Users\Степаан\PycharmProjects\work\worklib\graphql_client.py", line 65, in execute_graphql
|
||||
raise RuntimeError(f"GraphQL errors: {errors}")
|
||||
RuntimeError: GraphQL errors: [{'message': 'Not Found', 'code': 'Client Error', 'status': 404, 'description': 'The server has not found anything matching the Request-URI'}]
|
||||
8
allure-report/data/attachments/df5541a2d7bf2683.json
Normal file
8
allure-report/data/attachments/df5541a2d7bf2683.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a8185d6417545a7f4863",
|
||||
"member_id": "e03812e4-8689-4689-b561-29a40b290851"
|
||||
}
|
||||
}
|
||||
}
|
||||
5
allure-report/data/attachments/e48af2bc3e0cb93b.json
Normal file
5
allure-report/data/attachments/e48af2bc3e0cb93b.json
Normal file
@ -0,0 +1,5 @@
|
||||
{
|
||||
"data": {
|
||||
"deleteSubscription": true
|
||||
}
|
||||
}
|
||||
25
allure-report/data/attachments/e71e08853c8d808a.json
Normal file
25
allure-report/data/attachments/e71e08853c8d808a.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"data": {
|
||||
"createSubscription": {
|
||||
"id": "69e8a59e1a037043d86d23d6",
|
||||
"services": [
|
||||
{
|
||||
"id": "69e8a59d1a037043d86d23d3",
|
||||
"title": "kvs-service-1776854432"
|
||||
}
|
||||
],
|
||||
"user": {
|
||||
"id": "a6542878-eba4-45ff-b1ba-38fe31a1349e",
|
||||
"data": {
|
||||
"first_name": "kvstest1",
|
||||
"last_name": "kvstest2"
|
||||
}
|
||||
},
|
||||
"plan": {
|
||||
"id": "69e8a59d1a037043d86d23d4",
|
||||
"title": "plan-kvs-1776854432"
|
||||
},
|
||||
"place_id": "69e8a59d5d6417545a7f4844"
|
||||
}
|
||||
}
|
||||
}
|
||||
10
allure-report/data/attachments/ef9f82bdf46a2c07.json
Normal file
10
allure-report/data/attachments/ef9f82bdf46a2c07.json
Normal file
@ -0,0 +1,10 @@
|
||||
{
|
||||
"data": {
|
||||
"createPlaceMultiple": [
|
||||
{
|
||||
"id": "69e8a5c963548ff04f752605",
|
||||
"__typename": "PlaceObject"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
8
allure-report/data/attachments/efb29951bb9a32ab.json
Normal file
8
allure-report/data/attachments/efb29951bb9a32ab.json
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"data": {
|
||||
"addUserToPlace": {
|
||||
"place_id": "69e8a8180bfa76f761dd1b20",
|
||||
"member_id": "4dfe1f78-8dd7-40ff-b0e1-55536422c6b1"
|
||||
}
|
||||
}
|
||||
}
|
||||
4
allure-report/data/behaviors.csv
Normal file
4
allure-report/data/behaviors.csv
Normal file
@ -0,0 +1,4 @@
|
||||
"BROKEN","EPIC","FAILED","FEATURE","PASSED","SKIPPED","STORY","UNKNOWN"
|
||||
"0","","0","KVS GraphQL (place + members)","2","0","","0"
|
||||
"0","","0","Place info (REST/GraphQL/WebSocket)","2","0","","0"
|
||||
"0","","0","KVS GraphQL subscription","1","0","","0"
|
||||
|
1
allure-report/data/behaviors.json
Normal file
1
allure-report/data/behaviors.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"b1a8273437954620fa374b796ffaacdd","name":"behaviors","children":[{"name":"Place info (REST/GraphQL/WebSocket)","children":[{"name":"Authorize as employer","uid":"9c2bc11534322c25","parentUid":"b10d31223e5a74c6f18fff0f5696f8ee","status":"passed","time":{"start":1776855065463,"stop":1776855065765,"duration":302},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Get place info","uid":"d01c25fbb85460f4","parentUid":"b10d31223e5a74c6f18fff0f5696f8ee","status":"passed","time":{"start":1776855065767,"stop":1776855065822,"duration":55},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]}],"uid":"b10d31223e5a74c6f18fff0f5696f8ee"},{"name":"KVS GraphQL (place + members)","children":[{"name":"Get place info (dynamic place, no hardcode)","uid":"7a6a9a042ff6da05","parentUid":"ded2778bb914aacc5dcd5003813f711c","status":"passed","time":{"start":1776855065825,"stop":1776855066169,"duration":344},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":true,"parameters":[],"tags":[]},{"name":"Add user to place and verify member appears","uid":"7bcb3b93b332730e","parentUid":"ded2778bb914aacc5dcd5003813f711c","status":"passed","time":{"start":1776855066170,"stop":1776855066735,"duration":565},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]}],"uid":"ded2778bb914aacc5dcd5003813f711c"},{"name":"KVS GraphQL subscription","children":[{"name":"Create subscription, check invoices, delete subscription","uid":"eeb4568cb2661d08","parentUid":"ccabf020a779865991f68fbb0346b2db","status":"passed","time":{"start":1776855066739,"stop":1776855067705,"duration":966},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]}],"uid":"ccabf020a779865991f68fbb0346b2db"}]}
|
||||
0
allure-report/data/categories.csv
Normal file
0
allure-report/data/categories.csv
Normal file
|
|
1
allure-report/data/categories.json
Normal file
1
allure-report/data/categories.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"4b4757e66a1912dae1a509f688f20b0f","name":"categories","children":[]}
|
||||
1
allure-report/data/packages.json
Normal file
1
allure-report/data/packages.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"83edc06c07f9ae9e47eb6dd1b683e4e2","name":"packages","children":[{"name":"Authorize as employer","uid":"9c2bc11534322c25","parentUid":"83edc06c07f9ae9e47eb6dd1b683e4e2","status":"passed","time":{"start":1776855065463,"stop":1776855065765,"duration":302},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Get place info","uid":"d01c25fbb85460f4","parentUid":"83edc06c07f9ae9e47eb6dd1b683e4e2","status":"passed","time":{"start":1776855065767,"stop":1776855065822,"duration":55},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Get place info (dynamic place, no hardcode)","uid":"7a6a9a042ff6da05","parentUid":"83edc06c07f9ae9e47eb6dd1b683e4e2","status":"passed","time":{"start":1776855065825,"stop":1776855066169,"duration":344},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":true,"parameters":[],"tags":[]},{"name":"Add user to place and verify member appears","uid":"7bcb3b93b332730e","parentUid":"83edc06c07f9ae9e47eb6dd1b683e4e2","status":"passed","time":{"start":1776855066170,"stop":1776855066735,"duration":565},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Create subscription, check invoices, delete subscription","uid":"eeb4568cb2661d08","parentUid":"83edc06c07f9ae9e47eb6dd1b683e4e2","status":"passed","time":{"start":1776855066739,"stop":1776855067705,"duration":966},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]}]}
|
||||
6
allure-report/data/suites.csv
Normal file
6
allure-report/data/suites.csv
Normal file
@ -0,0 +1,6 @@
|
||||
"DESCRIPTION","DURATION IN MS","NAME","PARENT SUITE","START TIME","STATUS","STOP TIME","SUB SUITE","SUITE","TEST CLASS","TEST METHOD"
|
||||
"","344","Get place info (dynamic place, no hardcode)","","2026-04-22","passed","2026-04-22","","","",""
|
||||
"","55","Get place info","","2026-04-22","passed","2026-04-22","","","",""
|
||||
"","565","Add user to place and verify member appears","","2026-04-22","passed","2026-04-22","","","",""
|
||||
"","302","Authorize as employer","","2026-04-22","passed","2026-04-22","","","",""
|
||||
"","966","Create subscription, check invoices, delete subscription","","2026-04-22","passed","2026-04-22","","","",""
|
||||
|
1
allure-report/data/suites.json
Normal file
1
allure-report/data/suites.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"98d3104e051c652961429bf95fa0b5d6","name":"suites","children":[{"name":"Authorize as employer","uid":"9c2bc11534322c25","parentUid":"98d3104e051c652961429bf95fa0b5d6","status":"passed","time":{"start":1776855065463,"stop":1776855065765,"duration":302},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Get place info","uid":"d01c25fbb85460f4","parentUid":"98d3104e051c652961429bf95fa0b5d6","status":"passed","time":{"start":1776855065767,"stop":1776855065822,"duration":55},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Get place info (dynamic place, no hardcode)","uid":"7a6a9a042ff6da05","parentUid":"98d3104e051c652961429bf95fa0b5d6","status":"passed","time":{"start":1776855065825,"stop":1776855066169,"duration":344},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":true,"parameters":[],"tags":[]},{"name":"Add user to place and verify member appears","uid":"7bcb3b93b332730e","parentUid":"98d3104e051c652961429bf95fa0b5d6","status":"passed","time":{"start":1776855066170,"stop":1776855066735,"duration":565},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]},{"name":"Create subscription, check invoices, delete subscription","uid":"eeb4568cb2661d08","parentUid":"98d3104e051c652961429bf95fa0b5d6","status":"passed","time":{"start":1776855066739,"stop":1776855067705,"duration":966},"flaky":false,"newFailed":false,"newPassed":false,"newBroken":false,"retriesCount":2,"retriesStatusChange":false,"parameters":[],"tags":[]}]}
|
||||
1
allure-report/data/test-cases/207f4f7e62f50ecd.json
Normal file
1
allure-report/data/test-cases/207f4f7e62f50ecd.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"207f4f7e62f50ecd","name":"Authorize as employer","fullName":"Place info (REST/GraphQL/WebSocket): Authorize as employer","historyId":"671d36bc7d85d5b78ec36b2e34a7884b","time":{"start":1776854474515,"stop":1776854474820,"duration":305},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":0,"retriesStatusChange":false,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get access token","time":{"start":1776854474516,"stop":1776854474818,"duration":302},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then access token is valid","time":{"start":1776854474818,"stop":1776854474819,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":2},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"Place info (REST/GraphQL/WebSocket)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":true,"retry":true,"extra":{"categories":[],"tags":[]},"source":"207f4f7e62f50ecd.json","parameterValues":[]}
|
||||
1
allure-report/data/test-cases/3233dd19cfc118fc.json
Normal file
1
allure-report/data/test-cases/3233dd19cfc118fc.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"3233dd19cfc118fc","name":"Get place info","fullName":"Place info (REST/GraphQL/WebSocket): Get place info","historyId":"ad3dd3c4cc300bb9a4f6fcd9cfe24502","time":{"start":1776854474822,"stop":1776854474870,"duration":48},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":0,"retriesStatusChange":false,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get place info","time":{"start":1776854474823,"stop":1776854474869,"duration":46},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then place info is valid for query data","time":{"start":1776854474869,"stop":1776854474870,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":2},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"Place info (REST/GraphQL/WebSocket)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":true,"retry":true,"extra":{"categories":[],"tags":[]},"source":"3233dd19cfc118fc.json","parameterValues":[]}
|
||||
1
allure-report/data/test-cases/49083d3578d997f4.json
Normal file
1
allure-report/data/test-cases/49083d3578d997f4.json
Normal file
File diff suppressed because one or more lines are too long
1
allure-report/data/test-cases/5be1b68cadb47a6c.json
Normal file
1
allure-report/data/test-cases/5be1b68cadb47a6c.json
Normal file
File diff suppressed because one or more lines are too long
1
allure-report/data/test-cases/63e03a2b27e8d9f2.json
Normal file
1
allure-report/data/test-cases/63e03a2b27e8d9f2.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"63e03a2b27e8d9f2","name":"Get place info","fullName":"Place info (REST/GraphQL/WebSocket): Get place info","historyId":"ad3dd3c4cc300bb9a4f6fcd9cfe24502","time":{"start":1776854431019,"stop":1776854431114,"duration":95},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":0,"retriesStatusChange":false,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get place info","time":{"start":1776854431020,"stop":1776854431112,"duration":92},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then place info is valid for query data","time":{"start":1776854431113,"stop":1776854431113,"duration":0},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":2},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"Place info (REST/GraphQL/WebSocket)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":true,"retry":true,"extra":{"categories":[],"tags":[]},"source":"63e03a2b27e8d9f2.json","parameterValues":[]}
|
||||
1
allure-report/data/test-cases/7a6a9a042ff6da05.json
Normal file
1
allure-report/data/test-cases/7a6a9a042ff6da05.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"7a6a9a042ff6da05","name":"Get place info (dynamic place, no hardcode)","fullName":"KVS GraphQL (place + members): Get place info (dynamic place, no hardcode)","historyId":"c1bd554320a2aefbe4b77b8dc3a01b64","time":{"start":1776855065825,"stop":1776855066169,"duration":344},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":2,"retriesStatusChange":true,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get access token","time":{"start":1776855065826,"stop":1776855066011,"duration":185},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then access token is valid","time":{"start":1776855066011,"stop":1776855066012,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"When create place for kvs","time":{"start":1776855066012,"stop":1776855066060,"duration":48},"status":"passed","steps":[{"name":"GraphQL: createPlaceMultiple (KVS)","time":{"start":1776855066014,"stop":1776855066059,"duration":45},"status":"passed","steps":[],"attachments":[{"uid":"a9f7f6046e5d4a4f","name":"createPlaceMultiple response","source":"a9f7f6046e5d4a4f.json","type":"application/json","size":148}],"parameters":[],"attachmentsCount":1,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":1,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":1},{"name":"And query place members for created kvs place","time":{"start":1776855066060,"stop":1776855066109,"duration":49},"status":"passed","steps":[{"name":"GraphQL: place members (KVS)","time":{"start":1776855066061,"stop":1776855066108,"duration":47},"status":"passed","steps":[],"attachments":[{"uid":"5f3bbbf8ce320408","name":"place members response","source":"5f3bbbf8ce320408.json","type":"application/json","size":155}],"parameters":[],"attachmentsCount":1,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":1,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":1},{"name":"Then kvs place members response has correct shape for created place","time":{"start":1776855066109,"stop":1776855066110,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Cleanup: _cleanup_delete_place","time":{"start":1776855066111,"stop":1776855066168,"duration":57},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":2,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":8},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"KVS GraphQL (place + members)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":false,"retry":false,"extra":{"severity":"normal","retries":[{"uid":"c4ccaf28f30f5e79","status":"passed","time":{"start":1776854474872,"stop":1776854475143,"duration":271}},{"uid":"ffb09160c14b657","status":"failed","statusDetails":"AssertionError: members пустой: []\n","time":{"start":1776854431117,"stop":1776854431427,"duration":310}}],"categories":[],"tags":[]},"source":"7a6a9a042ff6da05.json","parameterValues":[]}
|
||||
1
allure-report/data/test-cases/7ba6e9730bad3e99.json
Normal file
1
allure-report/data/test-cases/7ba6e9730bad3e99.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"7ba6e9730bad3e99","name":"Authorize as employer","fullName":"Place info (REST/GraphQL/WebSocket): Authorize as employer","historyId":"671d36bc7d85d5b78ec36b2e34a7884b","time":{"start":1776854430677,"stop":1776854431017,"duration":340},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":0,"retriesStatusChange":false,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get access token","time":{"start":1776854430679,"stop":1776854431015,"duration":336},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then access token is valid","time":{"start":1776854431015,"stop":1776854431016,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":2},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"Place info (REST/GraphQL/WebSocket)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":true,"retry":true,"extra":{"categories":[],"tags":[]},"source":"7ba6e9730bad3e99.json","parameterValues":[]}
|
||||
1
allure-report/data/test-cases/7bcb3b93b332730e.json
Normal file
1
allure-report/data/test-cases/7bcb3b93b332730e.json
Normal file
File diff suppressed because one or more lines are too long
1
allure-report/data/test-cases/7ff516c1ef46fb74.json
Normal file
1
allure-report/data/test-cases/7ff516c1ef46fb74.json
Normal file
File diff suppressed because one or more lines are too long
1
allure-report/data/test-cases/9c2bc11534322c25.json
Normal file
1
allure-report/data/test-cases/9c2bc11534322c25.json
Normal file
@ -0,0 +1 @@
|
||||
{"uid":"9c2bc11534322c25","name":"Authorize as employer","fullName":"Place info (REST/GraphQL/WebSocket): Authorize as employer","historyId":"671d36bc7d85d5b78ec36b2e34a7884b","time":{"start":1776855065463,"stop":1776855065765,"duration":302},"status":"passed","flaky":false,"newFailed":false,"newBroken":false,"newPassed":false,"retriesCount":2,"retriesStatusChange":false,"beforeStages":[],"testStage":{"status":"passed","steps":[{"name":"When get access token","time":{"start":1776855065465,"stop":1776855065762,"duration":297},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0},{"name":"Then access token is valid","time":{"start":1776855065763,"stop":1776855065764,"duration":1},"status":"passed","steps":[],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":false,"stepsCount":0}],"attachments":[],"parameters":[],"attachmentsCount":0,"shouldDisplayMessage":false,"attachmentStep":false,"hasContent":true,"stepsCount":2},"afterStages":[],"labels":[{"name":"severity","value":"normal"},{"name":"feature","value":"Place info (REST/GraphQL/WebSocket)"},{"name":"framework","value":"behave"},{"name":"language","value":"cpython3"},{"name":"resultFormat","value":"allure2"}],"parameters":[],"links":[],"hidden":false,"retry":false,"extra":{"severity":"normal","retries":[{"uid":"207f4f7e62f50ecd","status":"passed","time":{"start":1776854474515,"stop":1776854474820,"duration":305}},{"uid":"7ba6e9730bad3e99","status":"passed","time":{"start":1776854430677,"stop":1776854431017,"duration":340}}],"categories":[],"tags":[]},"source":"9c2bc11534322c25.json","parameterValues":[]}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user