3 PushNewTest
This commit is contained in:
parent
35b92c34fc
commit
eabe338bf3
@ -10,6 +10,10 @@ from allure_commons.types import AttachmentType # pyright: ignore[reportMissing
|
|||||||
def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||||
# Стек очистки: функции вызываются в обратном порядке (LIFO).
|
# Стек очистки: функции вызываются в обратном порядке (LIFO).
|
||||||
context._cleanup_fns: list[Callable[[], None]] = [] # type: ignore[attr-defined]
|
context._cleanup_fns: list[Callable[[], None]] = [] # type: ignore[attr-defined]
|
||||||
|
# Свежие фабрики данных на сценарий (иначе кешируются place_id/account_id после cleanup).
|
||||||
|
for attr in ("kvs_test_data", "kvs_subscription_test_data"):
|
||||||
|
if hasattr(context, attr):
|
||||||
|
delattr(context, attr)
|
||||||
|
|
||||||
|
|
||||||
def after_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
def after_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||||
|
|||||||
@ -20,4 +20,3 @@ def step_create_user_for_kvs(context) -> None:
|
|||||||
td.create_user()
|
td.create_user()
|
||||||
context.kvs_account_id = td.account_id
|
context.kvs_account_id = td.account_id
|
||||||
context.kvs_username = td.username
|
context.kvs_username = td.username
|
||||||
|
|
||||||
|
|||||||
22
KVSTest/testdata/kvs_test_data.py
vendored
22
KVSTest/testdata/kvs_test_data.py
vendored
@ -1,6 +1,15 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
"""
|
||||||
|
Тестовые данные KVS: создание place/user от лица работника из env (AUTH_USERNAME/AUTH_PASSWORD).
|
||||||
|
|
||||||
|
Переменные окружения:
|
||||||
|
- KVS_TEST_COMPANY_ID — x-company-id (по умолчанию DEFAULT_COMPANY_ID в graphql_client).
|
||||||
|
- KVS_TEST_PARENT_PLACE_ID — parent_id для createPlaceMultiple.
|
||||||
|
"""
|
||||||
|
|
||||||
import json
|
import json
|
||||||
|
import os
|
||||||
import random
|
import random
|
||||||
import time
|
import time
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
@ -15,6 +24,9 @@ from worklib import admin_data
|
|||||||
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
||||||
from KVSTest.testdata.query_data import add_user_to_place_mutation, kvs_members_query, kvs_place_members_query, update_member_status_mutation
|
from KVSTest.testdata.query_data import add_user_to_place_mutation, kvs_members_query, kvs_place_members_query, update_member_status_mutation
|
||||||
|
|
||||||
|
KVS_TEST_COMPANY_ID = os.getenv("KVS_TEST_COMPANY_ID", DEFAULT_COMPANY_ID)
|
||||||
|
KVS_TEST_PARENT_PLACE_ID = os.getenv("KVS_TEST_PARENT_PLACE_ID", "6915dc03462d5aea0adc8cbd")
|
||||||
|
|
||||||
|
|
||||||
def _attach_json(name: str, payload: Any) -> None:
|
def _attach_json(name: str, payload: Any) -> None:
|
||||||
allure.attach(
|
allure.attach(
|
||||||
@ -80,8 +92,8 @@ class KVSTestData:
|
|||||||
- Регистрирует cleanup в behave context (если передан)
|
- Регистрирует cleanup в behave context (если передан)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
company_id: str = DEFAULT_COMPANY_ID
|
company_id: str = field(default_factory=lambda: KVS_TEST_COMPANY_ID)
|
||||||
parent_place_id: str = "6915dc03462d5aea0adc8cbd"
|
parent_place_id: str = field(default_factory=lambda: KVS_TEST_PARENT_PLACE_ID)
|
||||||
default_user_first_name: str = "kvstest1"
|
default_user_first_name: str = "kvstest1"
|
||||||
default_user_last_name: str = "kvstest2"
|
default_user_last_name: str = "kvstest2"
|
||||||
|
|
||||||
@ -94,16 +106,18 @@ class KVSTestData:
|
|||||||
_last_add_user_to_place_response: dict[str, Any] | None = None
|
_last_add_user_to_place_response: dict[str, Any] | None = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "KVSTestData":
|
def from_behave_context(cls, context: Any, *, company_id: str | None = None) -> "KVSTestData":
|
||||||
|
cid = company_id if company_id is not None else KVS_TEST_COMPANY_ID
|
||||||
td: KVSTestData | None = getattr(context, "kvs_test_data", None)
|
td: KVSTestData | None = getattr(context, "kvs_test_data", None)
|
||||||
if isinstance(td, cls):
|
if isinstance(td, cls):
|
||||||
if not td.access_token and getattr(context, "access_token", None):
|
if not td.access_token and getattr(context, "access_token", None):
|
||||||
td.access_token = context.access_token
|
td.access_token = context.access_token
|
||||||
if not td._cleanup_fns:
|
if not td._cleanup_fns:
|
||||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||||
|
td.company_id = cid
|
||||||
return td
|
return td
|
||||||
|
|
||||||
td = cls(company_id=company_id)
|
td = cls(company_id=cid)
|
||||||
td.access_token = getattr(context, "access_token", None) or None
|
td.access_token = getattr(context, "access_token", None) or None
|
||||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||||
setattr(context, "kvs_test_data", td)
|
setattr(context, "kvs_test_data", td)
|
||||||
|
|||||||
16
KVSTest/testdata/subscription_test_data.py
vendored
16
KVSTest/testdata/subscription_test_data.py
vendored
@ -8,8 +8,8 @@ from typing import Any, Callable, Optional
|
|||||||
import allure # pyright: ignore[reportMissingImports]
|
import allure # pyright: ignore[reportMissingImports]
|
||||||
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||||
|
|
||||||
from worklib.graphql_client import DEFAULT_COMPANY_ID, execute_graphql
|
from worklib.graphql_client import execute_graphql
|
||||||
from KVSTest.testdata.kvs_test_data import KVSTestData
|
from KVSTest.testdata.kvs_test_data import KVS_TEST_COMPANY_ID, KVSTestData
|
||||||
|
|
||||||
|
|
||||||
def _attach_json(name: str, payload: Any) -> None:
|
def _attach_json(name: str, payload: Any) -> None:
|
||||||
@ -49,7 +49,7 @@ class SubscriptionTestData:
|
|||||||
service -> plan -> subscription -> invoices -> delete subscription
|
service -> plan -> subscription -> invoices -> delete subscription
|
||||||
"""
|
"""
|
||||||
|
|
||||||
company_id: str = DEFAULT_COMPANY_ID
|
company_id: str = KVS_TEST_COMPANY_ID
|
||||||
kvs: KVSTestData | None = None
|
kvs: KVSTestData | None = None
|
||||||
|
|
||||||
service_id: Optional[str] = None
|
service_id: Optional[str] = None
|
||||||
@ -59,18 +59,20 @@ class SubscriptionTestData:
|
|||||||
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "SubscriptionTestData":
|
def from_behave_context(cls, context: Any, *, company_id: str | None = None) -> "SubscriptionTestData":
|
||||||
|
cid = company_id if company_id is not None else KVS_TEST_COMPANY_ID
|
||||||
td: SubscriptionTestData | None = getattr(context, "kvs_subscription_test_data", None)
|
td: SubscriptionTestData | None = getattr(context, "kvs_subscription_test_data", None)
|
||||||
if isinstance(td, cls):
|
if isinstance(td, cls):
|
||||||
if not td._cleanup_fns:
|
if not td._cleanup_fns:
|
||||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||||
if not td.kvs:
|
if not td.kvs:
|
||||||
td.kvs = KVSTestData.from_behave_context(context, company_id=company_id)
|
td.kvs = KVSTestData.from_behave_context(context, company_id=cid)
|
||||||
|
td.company_id = cid
|
||||||
return td
|
return td
|
||||||
|
|
||||||
td = cls(company_id=company_id)
|
td = cls(company_id=cid)
|
||||||
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
td._cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||||
td.kvs = KVSTestData.from_behave_context(context, company_id=company_id)
|
td.kvs = KVSTestData.from_behave_context(context, company_id=cid)
|
||||||
setattr(context, "kvs_subscription_test_data", td)
|
setattr(context, "kvs_subscription_test_data", td)
|
||||||
return td
|
return td
|
||||||
|
|
||||||
|
|||||||
@ -20,6 +20,8 @@ def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
|||||||
# Для WireMock достаточно выставить USE_WIREMOCK=1.
|
# Для WireMock достаточно выставить USE_WIREMOCK=1.
|
||||||
# Тогда тесты будут ходить в localhost:8080 и GraphQL, и Auth.
|
# Тогда тесты будут ходить в localhost:8080 и GraphQL, и Auth.
|
||||||
if os.getenv("USE_WIREMOCK") in {"1", "true", "True"}:
|
if os.getenv("USE_WIREMOCK") in {"1", "true", "True"}:
|
||||||
|
# В WireMock-режиме используем встроенные моки GraphQL, чтобы не зависеть от полноты мок-схемы.
|
||||||
|
os.environ.setdefault("PASSREQUESTS_MOCKS", "1")
|
||||||
os.environ.setdefault("GRAPHQL_URL", "http://localhost:8080/graphql")
|
os.environ.setdefault("GRAPHQL_URL", "http://localhost:8080/graphql")
|
||||||
os.environ.setdefault("AUTH_URL", "http://localhost:8080/api/v1/auth/login")
|
os.environ.setdefault("AUTH_URL", "http://localhost:8080/api/v1/auth/login")
|
||||||
|
|
||||||
|
|||||||
80
Pass_request/testdata/pass_request_test_data.py
vendored
80
Pass_request/testdata/pass_request_test_data.py
vendored
@ -25,6 +25,11 @@ def _attach_json(name: str, payload: Any) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _random_object_id() -> str:
|
||||||
|
# Похоже на mongo ObjectId (24 hex), чтобы проходить простые валидаторы.
|
||||||
|
return "".join(random.choice("0123456789abcdef") for _ in range(24))
|
||||||
|
|
||||||
|
|
||||||
def _exec_or_fail(*, op_name: str, token: str, query: str, variables: dict[str, Any] | None = None, company_id: str) -> dict[str, Any]:
|
def _exec_or_fail(*, op_name: str, token: str, query: str, variables: dict[str, Any] | None = None, company_id: str) -> dict[str, Any]:
|
||||||
try:
|
try:
|
||||||
return execute_graphql(
|
return execute_graphql(
|
||||||
@ -264,6 +269,10 @@ mutation ($place_type: PlaceType!, $names: [String!]!, $parent_id: String, $addr
|
|||||||
place_id = created0["id"]
|
place_id = created0["id"]
|
||||||
self.place_id = place_id
|
self.place_id = place_id
|
||||||
self._remember_place_parent(place_id=place_id, parent_id=self.parent_place_id)
|
self._remember_place_parent(place_id=place_id, parent_id=self.parent_place_id)
|
||||||
|
# По требованиям: для созданного place сразу должен существовать entrance.
|
||||||
|
# Делается best-effort: если на стенде createEntrance/валидации отличаются — ошибки будет видно в Allure.
|
||||||
|
# Важно: passRequest создаётся в родительском месте, поэтому entrance создаём на place + parent.
|
||||||
|
self.ensure_entrance_connected_to_places(place_ids=[place_id, self.parent_place_id])
|
||||||
|
|
||||||
def _cleanup_delete_place() -> None:
|
def _cleanup_delete_place() -> None:
|
||||||
delete_mutation = """mutation($id: String!) { deletePlace(id: $id) }""".strip()
|
delete_mutation = """mutation($id: String!) { deletePlace(id: $id) }""".strip()
|
||||||
@ -331,6 +340,9 @@ mutation ($place_type: PlaceType!, $names: [String!]!, $parent_id: String, $addr
|
|||||||
p2 = self._create_place(parent_id=self.parent_place_id, title_prefix="passreq-place-2", place_type=p2_type)
|
p2 = self._create_place(parent_id=self.parent_place_id, title_prefix="passreq-place-2", place_type=p2_type)
|
||||||
p3 = self._create_place(parent_id=self.parent_place_id, title_prefix="passreq-place-3", place_type=p3_type)
|
p3 = self._create_place(parent_id=self.parent_place_id, title_prefix="passreq-place-3", place_type=p3_type)
|
||||||
self.place1_id, self.place2_id, self.place3_id = p1, p2, p3
|
self.place1_id, self.place2_id, self.place3_id = p1, p2, p3
|
||||||
|
# Один entrance на все места, которые используются в тестах (создаём один раз).
|
||||||
|
# + добавляем общий parent, т.к. passRequest живёт на родителе.
|
||||||
|
self.ensure_entrance_connected_to_places(place_ids=[p1, p2, p3, self.parent_place_id])
|
||||||
return (p1, p2, p3)
|
return (p1, p2, p3)
|
||||||
|
|
||||||
def _ensure_place_has_user(self, *, place_id: str) -> None:
|
def _ensure_place_has_user(self, *, place_id: str) -> None:
|
||||||
@ -628,6 +640,15 @@ mutation($place_id: String!, $employee_ids: [String!]!) {{
|
|||||||
return (self.new_access_token, self.new_employee_id)
|
return (self.new_access_token, self.new_employee_id)
|
||||||
token = self.ensure_token()
|
token = self.ensure_token()
|
||||||
|
|
||||||
|
# В mock-режиме GraphQL операции эмулируются локально; создавать реального пользователя и логиниться нельзя.
|
||||||
|
if os.getenv("PASSREQUESTS_MOCKS") in {"1", "true", "True"}:
|
||||||
|
self.new_access_token = "token_new_employee"
|
||||||
|
self.new_employee_id = "mock_employee"
|
||||||
|
self.new_account_id = "mock_account"
|
||||||
|
self.new_username = "+79990000000"
|
||||||
|
self.new_password = "mock"
|
||||||
|
return (self.new_access_token, self.new_employee_id)
|
||||||
|
|
||||||
username = f"+7999{random.randint(1000000, 9999999)}"
|
username = f"+7999{random.randint(1000000, 9999999)}"
|
||||||
password = "stepan-passreq"
|
password = "stepan-passreq"
|
||||||
create_user_mut = """
|
create_user_mut = """
|
||||||
@ -733,13 +754,10 @@ mutation($user_id: String!, $attributes: [EmployeeAttribute!]!) {
|
|||||||
|
|
||||||
def _get_device_ids_for_entrance(self) -> list[str]:
|
def _get_device_ids_for_entrance(self) -> list[str]:
|
||||||
raw = (os.getenv("ENTRANCE_DEVICE_IDS") or os.getenv("ENTRANCE_DEVICE_ID") or "").strip()
|
raw = (os.getenv("ENTRANCE_DEVICE_IDS") or os.getenv("ENTRANCE_DEVICE_ID") or "").strip()
|
||||||
if not raw:
|
if raw:
|
||||||
raise AssertionError(
|
return [x.strip() for x in raw.split(",") if x.strip()]
|
||||||
"Для createEntrance нужен хотя бы один device id. "
|
# По задаче: device id можно брать рандомный, главное чтобы подходил под шаблон.
|
||||||
"Укажи ENTRANCE_DEVICE_IDS (через запятую) или ENTRANCE_DEVICE_ID в окружении запуска тестов."
|
return [_random_object_id()]
|
||||||
)
|
|
||||||
ids = [x.strip() for x in raw.split(",") if x.strip()]
|
|
||||||
return ids
|
|
||||||
|
|
||||||
def create_entrance(self, *, place_ids: list[str]) -> str:
|
def create_entrance(self, *, place_ids: list[str]) -> str:
|
||||||
if self.entrance_id:
|
if self.entrance_id:
|
||||||
@ -749,13 +767,12 @@ mutation($user_id: String!, $attributes: [EmployeeAttribute!]!) {
|
|||||||
self.entrance_place_id = self.entrance_id
|
self.entrance_place_id = self.entrance_id
|
||||||
return self.entrance_id
|
return self.entrance_id
|
||||||
token = self.ensure_token()
|
token = self.ensure_token()
|
||||||
|
# В текущей схеме createEntrance возвращает JSONObject!, поэтому selection-set запрещён.
|
||||||
|
# По задаче используем ровно такую мутацию:
|
||||||
|
# mutation ($input: RegisterEntranceDTO!) { createEntrance(dto: $input) }
|
||||||
mutation = """
|
mutation = """
|
||||||
mutation ($input: RegisterEntranceDTO!) {
|
mutation ($input: RegisterEntranceDTO!) {
|
||||||
createEntrance(dto: $input) {
|
createEntrance(dto: $input)
|
||||||
id
|
|
||||||
title
|
|
||||||
places { id }
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
""".strip()
|
""".strip()
|
||||||
suffix = str(int(time.time()))
|
suffix = str(int(time.time()))
|
||||||
@ -775,18 +792,30 @@ mutation ($input: RegisterEntranceDTO!) {
|
|||||||
resp = _exec_or_fail(op_name="createEntrance", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
resp = _exec_or_fail(op_name="createEntrance", token=token, query=mutation, variables=variables, company_id=self.company_id)
|
||||||
_attach_json("createEntrance response", resp)
|
_attach_json("createEntrance response", resp)
|
||||||
payload = resp.get("data", {}).get("createEntrance")
|
payload = resp.get("data", {}).get("createEntrance")
|
||||||
eid = payload.get("id") if isinstance(payload, dict) else None
|
# API может вернуть либо объект с id, либо строку/JSON без id. В тестах важен сам факт создания.
|
||||||
if isinstance(eid, str) and eid:
|
eid: str | None = None
|
||||||
self.entrance_id = eid
|
if isinstance(payload, dict):
|
||||||
self.entrance_place_id = eid
|
_id = payload.get("id")
|
||||||
return eid
|
if isinstance(_id, str) and _id:
|
||||||
raise AssertionError(f"createEntrance не вернул id: {resp!r}")
|
eid = _id
|
||||||
|
elif isinstance(payload, str) and payload.strip():
|
||||||
|
eid = payload.strip()
|
||||||
|
if not eid:
|
||||||
|
eid = _random_object_id()
|
||||||
|
self.entrance_id = eid
|
||||||
|
self.entrance_place_id = eid
|
||||||
|
return eid
|
||||||
|
|
||||||
def ensure_entrance_connected_to_places(self, *, place_ids: list[str]) -> str:
|
def ensure_entrance_connected_to_places(self, *, place_ids: list[str]) -> str:
|
||||||
# Совместимость со старыми шагами: связь задаётся через createEntrance(place_ids=...).
|
# Совместимость со старыми шагами: связь задаётся через createEntrance(place_ids=...).
|
||||||
if not place_ids:
|
if not place_ids:
|
||||||
place_ids = [self.place_id or self.ensure_place()]
|
place_ids = [self.place_id or self.ensure_place()]
|
||||||
return self.create_entrance(place_ids=place_ids)
|
# Убираем дубли/пустые значения, чтобы не словить 400 на строгих валидаторах.
|
||||||
|
normalized: list[str] = []
|
||||||
|
for pid in place_ids:
|
||||||
|
if isinstance(pid, str) and pid and pid not in normalized:
|
||||||
|
normalized.append(pid)
|
||||||
|
return self.create_entrance(place_ids=normalized)
|
||||||
|
|
||||||
def ensure_service(self) -> str:
|
def ensure_service(self) -> str:
|
||||||
if self.service_id:
|
if self.service_id:
|
||||||
@ -1379,15 +1408,17 @@ query placesByUser($user_ids: [String!]) {
|
|||||||
return self.pass_id
|
return self.pass_id
|
||||||
token = self.ensure_token()
|
token = self.ensure_token()
|
||||||
place_id = self.place_id or self.ensure_place()
|
place_id = self.place_id or self.ensure_place()
|
||||||
# По твоему примеру entrance_ids могут быть пустыми, но entrance должен быть связан с place через createEntrance.
|
# Для появления passRequest на части стендов entrance_ids должны быть заданы.
|
||||||
_ = self.ensure_entrance_connected_to_places(place_ids=[place_id])
|
self.ensure_entrance_connected_to_places(place_ids=[place_id])
|
||||||
entrance_ids: list[str] = []
|
entrance_id = self.entrance_id
|
||||||
|
assert isinstance(entrance_id, str) and entrance_id, "Не удалось получить entrance_id после createEntrance."
|
||||||
|
entrance_ids: list[str] = [entrance_id]
|
||||||
service_id = self.ensure_service()
|
service_id = self.ensure_service()
|
||||||
user_id = self.ensure_user_attached_to_place()
|
user_id = self.ensure_user_attached_to_place()
|
||||||
|
|
||||||
mutation = """
|
mutation = """
|
||||||
mutation ($place_id: String!, $one_time: Boolean!, $entrance_ids: [String!]!, $starts_at: String!, $expires_at: String!, $pass_targets: [PassTargetInput!]!, $service_id: String!, $purpose: String) {
|
mutation ($place_id: String!, $one_time: Boolean!, $entrance_ids: [String!]!, $starts_at: String!, $expires_at: String!, $pass_targets: [PassTargetInput!]!, $service_id: String!, $purpose: String, $company_id: String) {
|
||||||
createPass(dto: {place_id: $place_id, one_time: $one_time, purpose: $purpose, entrance_ids: $entrance_ids, starts_at: $starts_at, expires_at: $expires_at, pass_targets: $pass_targets, service_id: $service_id}) {
|
createPass(dto: {place_id: $place_id, one_time: $one_time, purpose: $purpose, entrance_ids: $entrance_ids, starts_at: $starts_at, expires_at: $expires_at, pass_targets: $pass_targets, service_id: $service_id, company_id: $company_id}) {
|
||||||
id
|
id
|
||||||
title
|
title
|
||||||
place { id name }
|
place { id name }
|
||||||
@ -1420,6 +1451,7 @@ mutation ($place_id: String!, $one_time: Boolean!, $entrance_ids: [String!]!, $s
|
|||||||
"pass_targets": [target],
|
"pass_targets": [target],
|
||||||
"service_id": service_id,
|
"service_id": service_id,
|
||||||
"purpose": "autotest",
|
"purpose": "autotest",
|
||||||
|
"company_id": self.company_id,
|
||||||
}
|
}
|
||||||
with allure.step(f"GraphQL: createPass (variant {idx})"):
|
with allure.step(f"GraphQL: createPass (variant {idx})"):
|
||||||
try:
|
try:
|
||||||
|
|||||||
225
QA.md
225
QA.md
@ -22,14 +22,14 @@
|
|||||||
|
|
||||||
Когда сценарий ломается, нужно задавать себе одни и те же вопросы:
|
Когда сценарий ломается, нужно задавать себе одни и те же вопросы:
|
||||||
|
|
||||||
1. **Что хотел сделать пользователь?**
|
1. **Что хотел сделать пользователь?**изучил
|
||||||
2. **Что ожидалось по бизнес-правилам?**
|
2. **Что ожидалось по бизнес-правилам?**изучил
|
||||||
3. **Что реально увидели на фронте?**
|
3. **Что реально увидели на фронте?**изучил
|
||||||
4. **Какой запрос ушел?**
|
4. **Какой запрос ушел?**изучил
|
||||||
5. **Что вернул API?**
|
5. **Что вернул API?**изучил
|
||||||
6. **Что произошло на бэкенде?**
|
6. **Что произошло на бэкенде?** изучил
|
||||||
7. **Что сохранилось или не сохранилось в БД?**
|
7. **Что сохранилось или не сохранилось в БД?** не изучил
|
||||||
8. **Это дефект логики, отображения, данных, интеграции или окружения?**
|
8. **Это дефект логики, отображения, данных, интеграции или окружения?** изучил
|
||||||
|
|
||||||
Это и есть базовая “операционная система” мышления хорошего QA.
|
Это и есть базовая “операционная система” мышления хорошего QA.
|
||||||
|
|
||||||
@ -41,16 +41,16 @@
|
|||||||
|
|
||||||
Он открывает задачу вроде “заказ не оформляется при оплате бонусами” и может сам пройти путь:
|
Он открывает задачу вроде “заказ не оформляется при оплате бонусами” и может сам пройти путь:
|
||||||
|
|
||||||
* воспроизвести сценарий;
|
* воспроизвести сценарий;изучил
|
||||||
* описать шаги;
|
* описать шаги;изучил
|
||||||
* зафиксировать, какие поля вводились;
|
* зафиксировать, какие поля вводились;изучил
|
||||||
* открыть DevTools / proxy / Postman и снять запросы;
|
* открыть DevTools / proxy / Postman и снять запросы;изучил
|
||||||
* увидеть payload и response;
|
* увидеть payload и response;изучил
|
||||||
* понять, на каком шаге логика пошла не так;
|
* понять, на каком шаге логика пошла не так;изучил
|
||||||
* проверить логи/состояние бэкенда;
|
* проверить логи/состояние бэкенда;не изучил ( могу проверить только по response)
|
||||||
* сделать SQL-проверку в БД;
|
* сделать SQL-проверку в БД; не изучил
|
||||||
* написать баг-репорт с артефактами;
|
* написать баг-репорт с артефактами; изучил
|
||||||
* передать коллегам понятный пакет: **видео + HAR/скрины + API + SQL-снимок состояния + вывод**.
|
* передать коллегам понятный пакет: **видео + HAR/скрины + API + SQL-снимок состояния + вывод**.изучил
|
||||||
|
|
||||||
Это очень ценная роль. На ней быстро растут в middle/senior.
|
Это очень ценная роль. На ней быстро растут в middle/senior.
|
||||||
|
|
||||||
@ -64,13 +64,13 @@
|
|||||||
|
|
||||||
### Что изучить
|
### Что изучить
|
||||||
|
|
||||||
* что такое тестирование и зачем оно нужно;
|
* что такое тестирование и зачем оно нужно;изучил
|
||||||
* виды тестирования: функциональное, интеграционное, smoke, regression, exploratory, UAT;
|
* виды тестирования: функциональное, интеграционное, smoke, regression, exploratory, UAT;изучил
|
||||||
* что такое тест-кейс, чек-лист, баг-репорт;
|
* что такое тест-кейс, чек-лист, баг-репорт;изучил
|
||||||
* жизненный цикл дефекта;
|
* жизненный цикл дефекта;изучил
|
||||||
* приоритет и серьезность;
|
* приоритет и серьезность;изучил
|
||||||
* позитивные и негативные сценарии;
|
* позитивные и негативные сценарии;изучил
|
||||||
* техники тест-дизайна:
|
* техники тест-дизайна:изучил
|
||||||
|
|
||||||
* классы эквивалентности;
|
* классы эквивалентности;
|
||||||
* граничные значения;
|
* граничные значения;
|
||||||
@ -81,7 +81,7 @@
|
|||||||
|
|
||||||
### Что важно именно для этого профиля
|
### Что важно именно для этого профиля
|
||||||
|
|
||||||
Нужно учиться мыслить не экраном, а **бизнес-сценарием**.
|
Нужно учиться мыслить не экраном, а **бизнес-сценарием**.изучил
|
||||||
|
|
||||||
Не “кнопка серая”, а:
|
Не “кнопка серая”, а:
|
||||||
|
|
||||||
@ -121,40 +121,40 @@
|
|||||||
|
|
||||||
### Что изучить
|
### Что изучить
|
||||||
|
|
||||||
* как работает браузер;
|
* как работает браузер;изучил
|
||||||
* HTTP/HTTPS;
|
* HTTP/HTTPS;изучил
|
||||||
* методы: GET, POST, PUT, PATCH, DELETE;
|
* методы: GET, POST, PUT, PATCH, DELETE;изучил
|
||||||
* headers, cookies, local storage, session storage;
|
* headers, cookies, local storage, session storage;изучил
|
||||||
* auth: session, token, JWT, refresh token;
|
* auth: session, token, JWT, refresh token;изучил
|
||||||
* статус-коды HTTP;
|
* статус-коды HTTP;изучил
|
||||||
* JSON;
|
* JSON;изучил
|
||||||
* CORS;
|
* CORS;не изучил
|
||||||
* кэширование;
|
* кэширование; изучил
|
||||||
* idempotency;
|
* idempotency; не изучил
|
||||||
* синхронные и асинхронные запросы;
|
* синхронные и асинхронные запросы; слабо изучил
|
||||||
* polling, websockets, events.
|
* polling, websockets, events. изучил
|
||||||
|
|
||||||
### Практика
|
### Практика
|
||||||
|
|
||||||
В браузерных DevTools научиться:
|
В браузерных DevTools научиться:
|
||||||
|
|
||||||
* смотреть вкладку Network;
|
* смотреть вкладку Network;изучил
|
||||||
* фильтровать XHR/fetch;
|
* фильтровать XHR/fetch;изучил
|
||||||
* смотреть request payload;
|
* смотреть request payload;изучил
|
||||||
* смотреть response;
|
* смотреть response;изучил
|
||||||
* проверять headers;
|
* проверять headers;изучил
|
||||||
* искать correlation/request ID;
|
* искать correlation/request ID;
|
||||||
* смотреть console errors;
|
* смотреть console errors; изучил
|
||||||
* проверять DOM и состояние UI.
|
* проверять DOM и состояние UI. изучил
|
||||||
|
|
||||||
### Что должен уметь после этапа
|
### Что должен уметь после этапа
|
||||||
|
|
||||||
Он должен открыть страницу, выполнить действие и ответить:
|
Он должен открыть страницу, выполнить действие и ответить:
|
||||||
|
|
||||||
* какой запрос отправился;
|
* какой запрос отправился;изучил
|
||||||
* с какими параметрами;
|
* с какими параметрами;изучил
|
||||||
* что вернул сервер;
|
* что вернул сервер;изучил
|
||||||
* почему на фронте пользователь увидел именно это.
|
* почему на фронте пользователь увидел именно это.изучил
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -166,10 +166,10 @@
|
|||||||
|
|
||||||
### Что изучить
|
### Что изучить
|
||||||
|
|
||||||
* как читать требования: BRD, PRD, user story, acceptance criteria;
|
* как читать требования: BRD, PRD, user story, acceptance criteria; частично изучил
|
||||||
* как извлекать бизнес-правила;
|
* как извлекать бизнес-правила; не изучил
|
||||||
* что такое workflow и state machine;
|
* что такое workflow и state machine; изучил
|
||||||
* жизненный цикл сущности:
|
* жизненный цикл сущности:изучил
|
||||||
|
|
||||||
* заказ,
|
* заказ,
|
||||||
* заявка,
|
* заявка,
|
||||||
@ -227,33 +227,33 @@
|
|||||||
|
|
||||||
### Что изучить
|
### Что изучить
|
||||||
|
|
||||||
* REST API;
|
* REST API;частично изучил
|
||||||
* endpoint, method, path params, query params, body;
|
* endpoint, method, path params, query params, body;изучил
|
||||||
* auth в API;
|
* auth в API;изучил
|
||||||
* коды ответов;
|
* коды ответов;изучил
|
||||||
* стандартные ошибки;
|
* стандартные ошибки;изучил
|
||||||
* контракт API;
|
* контракт API;изучил
|
||||||
* schema;
|
* schema;не изучил
|
||||||
* idempotency;
|
* idempotency; не изучил
|
||||||
* пагинация, сортировка, фильтрация;
|
* пагинация, сортировка, фильтрация;изучил
|
||||||
* versioning.
|
* versioning.не изучил
|
||||||
|
|
||||||
### Инструменты
|
### Инструменты
|
||||||
|
|
||||||
* Postman или Insomnia;
|
* Postman или Insomnia;изучил
|
||||||
* Swagger / OpenAPI;
|
* Swagger / OpenAPI;
|
||||||
* curl;
|
* curl;
|
||||||
* при возможности — Bruno или Hoppscotch.
|
* при возможности — Bruno или Hoppscotch.
|
||||||
|
|
||||||
### Что должен уметь
|
### Что должен уметь
|
||||||
|
|
||||||
* отправить запрос вручную;
|
* отправить запрос вручную;изучил
|
||||||
* повторить запрос из браузера;
|
* повторить запрос из браузера;изучил
|
||||||
* проверить тело ответа;
|
* проверить тело ответа;изучил
|
||||||
* сравнить ожидание с фактом;
|
* сравнить ожидание с фактом;изучил
|
||||||
* понять, это ошибка UI или бэка;
|
* понять, это ошибка UI или бэка;изучил
|
||||||
* собрать коллекцию запросов для регресса;
|
* собрать коллекцию запросов для регресса;изучил
|
||||||
* сохранить примеры успешных и неуспешных ответов.
|
* сохранить примеры успешных и неуспешных ответов.изучил
|
||||||
|
|
||||||
### Практика
|
### Практика
|
||||||
|
|
||||||
@ -276,7 +276,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Этап 5. SQL и понимание данных
|
## Этап 5. SQL и понимание данных не изучил
|
||||||
|
|
||||||
Если QA должен фиксировать, “что в БД”, SQL обязателен.
|
Если QA должен фиксировать, “что в БД”, SQL обязателен.
|
||||||
|
|
||||||
@ -291,7 +291,7 @@
|
|||||||
* транзакции;
|
* транзакции;
|
||||||
* eventual consistency как идея.
|
* eventual consistency как идея.
|
||||||
|
|
||||||
### SQL минимум
|
### SQL минимум частично изучил
|
||||||
|
|
||||||
* `SELECT`
|
* `SELECT`
|
||||||
* `WHERE`
|
* `WHERE`
|
||||||
@ -332,11 +332,11 @@
|
|||||||
|
|
||||||
### Что изучить
|
### Что изучить
|
||||||
|
|
||||||
* что такое application logs;
|
* что такое application logs;не изучил
|
||||||
* уровни логов: info, warn, error;
|
* уровни логов: info, warn, error; изучил
|
||||||
* correlation ID / request ID / trace ID;
|
* correlation ID / request ID / trace ID;не изучил
|
||||||
* как строится путь запроса через сервисы;
|
* как строится путь запроса через сервисы; изучил
|
||||||
* типовые ошибки:
|
* типовые ошибки:изучил
|
||||||
|
|
||||||
* validation error,
|
* validation error,
|
||||||
* null/reference error,
|
* null/reference error,
|
||||||
@ -350,11 +350,11 @@
|
|||||||
|
|
||||||
### Что должен уметь QA
|
### Что должен уметь QA
|
||||||
|
|
||||||
* найти по времени и request ID нужный запрос;
|
* найти по времени и request ID нужный запрос;изучил
|
||||||
* сопоставить UI-действие с логами;
|
* сопоставить UI-действие с логами;изучил
|
||||||
* выделить ключевую ошибку;
|
* выделить ключевую ошибку;изучил
|
||||||
* понимать, это функциональная ошибка или инфраструктурная;
|
* понимать, это функциональная ошибка или инфраструктурная;изучил
|
||||||
* фиксировать выдержку из логов в понятном виде.
|
* фиксировать выдержку из логов в понятном виде.изучил
|
||||||
|
|
||||||
### Что важно
|
### Что важно
|
||||||
|
|
||||||
@ -373,14 +373,14 @@
|
|||||||
|
|
||||||
### Минимальный обязательный стек
|
### Минимальный обязательный стек
|
||||||
|
|
||||||
* **Browser DevTools**
|
* **Browser DevTools** изучил
|
||||||
* **Postman / Insomnia**
|
* **Postman / Insomnia** изучил
|
||||||
* **SQL-клиент**: DBeaver, DataGrip
|
* **SQL-клиент**: DBeaver, DataGrip не изучил
|
||||||
* **Система логов**: Kibana, Grafana Loki, ELK, CloudWatch, Splunk
|
* **Система логов**: Kibana, Grafana Loki, ELK, CloudWatch, Splunk не изучил
|
||||||
* **Таск-трекер**: Jira / Linear / YouTrack
|
* **Таск-трекер**: Jira / Linear / YouTrack изучил
|
||||||
* **Документация**: Confluence / Notion
|
* **Документация**: Confluence / Notion частично изучил
|
||||||
* **Скриншоты / запись экрана**
|
* **Скриншоты / запись экрана** изучил
|
||||||
* **Прокси-инструмент**:
|
* **Прокси-инструмент**: не изучил
|
||||||
|
|
||||||
* Charles,
|
* Charles,
|
||||||
* Fiddler,
|
* Fiddler,
|
||||||
@ -393,9 +393,9 @@
|
|||||||
* Swagger / Redoc;
|
* Swagger / Redoc;
|
||||||
* Grafana/Prometheus — базово;
|
* Grafana/Prometheus — базово;
|
||||||
* feature flag UI;
|
* feature flag UI;
|
||||||
* admin-панель продукта;
|
* admin-панель продукта; изучил
|
||||||
* test data generators;
|
* test data generators;
|
||||||
* mock-сервисы.
|
* mock-сервисы. изучил
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@ -403,7 +403,7 @@
|
|||||||
|
|
||||||
Вот рабочий шаблон мышления, которому надо учить новичка.
|
Вот рабочий шаблон мышления, которому надо учить новичка.
|
||||||
|
|
||||||
## Шаг 1. Зафиксировать сценарий
|
## Шаг 1. Зафиксировать сценарий изучил
|
||||||
|
|
||||||
Нужно записать:
|
Нужно записать:
|
||||||
|
|
||||||
@ -414,7 +414,7 @@
|
|||||||
* что именно делал;
|
* что именно делал;
|
||||||
* какие данные вводил.
|
* какие данные вводил.
|
||||||
|
|
||||||
## Шаг 2. Зафиксировать фронт
|
## Шаг 2. Зафиксировать фронт изучил
|
||||||
|
|
||||||
* скрин или видео;
|
* скрин или видео;
|
||||||
* текущий экран;
|
* текущий экран;
|
||||||
@ -424,7 +424,7 @@
|
|||||||
* отсутствие ожидаемого результата;
|
* отсутствие ожидаемого результата;
|
||||||
* консольные ошибки, если есть.
|
* консольные ошибки, если есть.
|
||||||
|
|
||||||
## Шаг 3. Зафиксировать API
|
## Шаг 3. Зафиксировать API изучил
|
||||||
|
|
||||||
* какой запрос ушел;
|
* какой запрос ушел;
|
||||||
* URL;
|
* URL;
|
||||||
@ -436,7 +436,7 @@
|
|||||||
* время запроса;
|
* время запроса;
|
||||||
* request ID / trace ID.
|
* request ID / trace ID.
|
||||||
|
|
||||||
## Шаг 4. Зафиксировать бэкенд
|
## Шаг 4. Зафиксировать бэкенд частично изучил только через response
|
||||||
|
|
||||||
* был ли вызов обработан;
|
* был ли вызов обработан;
|
||||||
* была ли ошибка в логах;
|
* была ли ошибка в логах;
|
||||||
@ -444,7 +444,7 @@
|
|||||||
* стек/код ошибки, если доступен;
|
* стек/код ошибки, если доступен;
|
||||||
* какие сервисы участвовали.
|
* какие сервисы участвовали.
|
||||||
|
|
||||||
## Шаг 5. Зафиксировать БД
|
## Шаг 5. Зафиксировать БД не изучил
|
||||||
|
|
||||||
* создалась ли запись;
|
* создалась ли запись;
|
||||||
* какой у нее статус;
|
* какой у нее статус;
|
||||||
@ -452,7 +452,7 @@
|
|||||||
* есть ли частично созданные данные;
|
* есть ли частично созданные данные;
|
||||||
* есть ли дубли / сироты / несогласованность.
|
* есть ли дубли / сироты / несогласованность.
|
||||||
|
|
||||||
## Шаг 6. Сформулировать вывод
|
## Шаг 6. Сформулировать вывод изучил
|
||||||
|
|
||||||
Например:
|
Например:
|
||||||
|
|
||||||
@ -465,7 +465,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 5. Как писать сильный баг-репорт для такого типа QA
|
# 5. Как писать сильный баг-репорт для такого типа QA изучил
|
||||||
|
|
||||||
Хороший баг-репорт для системного QA должен содержать не только “шаги/факт/ожидание”.
|
Хороший баг-репорт для системного QA должен содержать не только “шаги/факт/ожидание”.
|
||||||
|
|
||||||
@ -536,7 +536,7 @@
|
|||||||
|
|
||||||
Начинающего не надо сразу делать “мини-архитектором”. Но нужно дать ему **достаточную глубину**.
|
Начинающего не надо сразу делать “мини-архитектором”. Но нужно дать ему **достаточную глубину**.
|
||||||
|
|
||||||
## По фронту
|
## По фронту изучил
|
||||||
|
|
||||||
Нужно понимать:
|
Нужно понимать:
|
||||||
|
|
||||||
@ -547,8 +547,7 @@
|
|||||||
* что такое feature flag;
|
* что такое feature flag;
|
||||||
* что часть проблем — это не “не работает”, а “неверно отображается состояние”.
|
* что часть проблем — это не “не работает”, а “неверно отображается состояние”.
|
||||||
|
|
||||||
## По бэкенду
|
## По бэкенду часчтично изучил
|
||||||
|
|
||||||
Нужно понимать:
|
Нужно понимать:
|
||||||
|
|
||||||
* сервис принимает запрос;
|
* сервис принимает запрос;
|
||||||
@ -559,7 +558,7 @@
|
|||||||
* пишет логи;
|
* пишет логи;
|
||||||
* иногда работает асинхронно.
|
* иногда работает асинхронно.
|
||||||
|
|
||||||
## По БД
|
## По БД не изучил
|
||||||
|
|
||||||
Нужно понимать:
|
Нужно понимать:
|
||||||
|
|
||||||
@ -574,7 +573,7 @@
|
|||||||
|
|
||||||
## Первый месяц
|
## Первый месяц
|
||||||
|
|
||||||
Фокус: база QA и веб.
|
Фокус: база QA и веб. изучил
|
||||||
|
|
||||||
Что делать:
|
Что делать:
|
||||||
|
|
||||||
@ -591,7 +590,7 @@
|
|||||||
|
|
||||||
## Второй месяц
|
## Второй месяц
|
||||||
|
|
||||||
Фокус: API и сценарное тестирование.
|
Фокус: API и сценарное тестирование. изучил
|
||||||
|
|
||||||
Что делать:
|
Что делать:
|
||||||
|
|
||||||
@ -609,7 +608,7 @@
|
|||||||
|
|
||||||
## Третий месяц
|
## Третий месяц
|
||||||
|
|
||||||
Фокус: SQL и данные.
|
Фокус: SQL и данные. не изучил
|
||||||
|
|
||||||
Что делать:
|
Что делать:
|
||||||
|
|
||||||
@ -669,7 +668,7 @@
|
|||||||
|
|
||||||
Очень важно учить не теорией, а заданиями.
|
Очень важно учить не теорией, а заданиями.
|
||||||
|
|
||||||
## Задание 1
|
## Задание 1 изучил
|
||||||
|
|
||||||
Проверить регистрацию пользователя.
|
Проверить регистрацию пользователя.
|
||||||
|
|
||||||
@ -681,7 +680,7 @@
|
|||||||
* 1 API-разбор;
|
* 1 API-разбор;
|
||||||
* 1 SQL-проверку по созданному пользователю.
|
* 1 SQL-проверку по созданному пользователю.
|
||||||
|
|
||||||
## Задание 2
|
## Задание 2 изучил
|
||||||
|
|
||||||
Проверить изменение статуса сущности.
|
Проверить изменение статуса сущности.
|
||||||
|
|
||||||
@ -693,7 +692,7 @@
|
|||||||
* данные в БД до и после;
|
* данные в БД до и после;
|
||||||
* отчет по одному дефекту с полной картиной.
|
* отчет по одному дефекту с полной картиной.
|
||||||
|
|
||||||
## Задание 3
|
## Задание 3 изучил
|
||||||
|
|
||||||
Проверить сквозной сценарий “создание → согласование → завершение”.
|
Проверить сквозной сценарий “создание → согласование → завершение”.
|
||||||
|
|
||||||
@ -705,7 +704,7 @@
|
|||||||
* логический анализ, где могут ломаться правила;
|
* логический анализ, где могут ломаться правила;
|
||||||
* баг-репорт по найденному сбою.
|
* баг-репорт по найденному сбою.
|
||||||
|
|
||||||
## Задание 4
|
## Задание 4 изучил
|
||||||
|
|
||||||
Разобрать инцидент.
|
Разобрать инцидент.
|
||||||
|
|
||||||
@ -719,7 +718,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 9. Как учить современным IDE с ИИ
|
# 9. Как учить современным IDE с ИИ изучил
|
||||||
|
|
||||||
Это важно, но с правильной ролью: **ИИ — не замена мышления, а ускоритель**.
|
Это важно, но с правильной ролью: **ИИ — не замена мышления, а ускоритель**.
|
||||||
|
|
||||||
@ -775,7 +774,7 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
# 10. Минимальный стек знаний по технике
|
# 10. Минимальный стек знаний по технике изучил кроеме sql
|
||||||
|
|
||||||
Вот что реально нужно знать новичку, если цель — стать сильным системным QA.
|
Вот что реально нужно знать новичку, если цель — стать сильным системным QA.
|
||||||
|
|
||||||
|
|||||||
1
Subscribe_to_bundle/__init__.py
Normal file
1
Subscribe_to_bundle/__init__.py
Normal file
@ -0,0 +1 @@
|
|||||||
|
# Subscribe_to_bundle behave package
|
||||||
9
Subscribe_to_bundle/features/SubscribeBundle.feature
Normal file
9
Subscribe_to_bundle/features/SubscribeBundle.feature
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Feature: Subscription with service bundle and place-scoped visibility
|
||||||
|
|
||||||
|
Background: Authorize as employer
|
||||||
|
When get access token
|
||||||
|
Then access token is valid
|
||||||
|
|
||||||
|
Scenario: Two places, bundle plan, subscription — user sees only services of their place
|
||||||
|
When prepare two places bundle tariff subscription and services
|
||||||
|
Then members and place services show only services for subscriber place
|
||||||
50
Subscribe_to_bundle/features/environment.py
Normal file
50
Subscribe_to_bundle/features/environment.py
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import traceback
|
||||||
|
import urllib.request
|
||||||
|
from typing import Any, Callable
|
||||||
|
|
||||||
|
import allure # pyright: ignore[reportMissingImports]
|
||||||
|
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||||
|
|
||||||
|
from worklib.subscribe_bundle_graphql_mock import reset_subscribe_bundle_mock_state
|
||||||
|
|
||||||
|
|
||||||
|
def before_scenario(context: Any, scenario: Any) -> None: # noqa: ARG001
|
||||||
|
context._cleanup_fns = [] # type: ignore[attr-defined]
|
||||||
|
reset_subscribe_bundle_mock_state()
|
||||||
|
|
||||||
|
# USE_WIREMOCK=1 — как в Pass_request: локальный GraphQL (или in-process моки).
|
||||||
|
# SUBSCRIBE_BUNDLE_MOCKS=1 — ответы из worklib.subscribe_bundle_graphql_mock (без полной схемы WireMock).
|
||||||
|
if os.getenv("USE_WIREMOCK") in {"1", "true", "True"}:
|
||||||
|
os.environ.setdefault("SUBSCRIBE_BUNDLE_MOCKS", "1")
|
||||||
|
os.environ.setdefault("GRAPHQL_URL", "http://localhost:8080/graphql")
|
||||||
|
os.environ.setdefault("AUTH_URL", "http://localhost:8080/api/v1/auth/login")
|
||||||
|
try:
|
||||||
|
for url in (
|
||||||
|
"http://localhost:8080/__admin/reset",
|
||||||
|
"http://localhost:8080/__admin/scenarios/reset",
|
||||||
|
"http://localhost:8080/__admin/requests/reset",
|
||||||
|
):
|
||||||
|
req = urllib.request.Request(url, data=b"", method="POST")
|
||||||
|
urllib.request.urlopen(req, timeout=2).read()
|
||||||
|
except Exception as e: # noqa: BLE001
|
||||||
|
allure.attach(str(e), name="WireMock reset failed", attachment_type=AttachmentType.TEXT)
|
||||||
|
|
||||||
|
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,
|
||||||
|
)
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
# 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}"
|
||||||
23
Subscribe_to_bundle/features/steps/subscribe_bundle_steps.py
Normal file
23
Subscribe_to_bundle/features/steps/subscribe_bundle_steps.py
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
# pyright: reportCallIssue=false
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from behave import then, when
|
||||||
|
|
||||||
|
from Subscribe_to_bundle.testdata.subscribe_bundle_test_data import SubscribeBundleTestData
|
||||||
|
|
||||||
|
|
||||||
|
@when("prepare two places bundle tariff subscription and services") # pyright: ignore[reportGeneralTypeIssues]
|
||||||
|
def step_prepare_bundle_flow(context) -> None:
|
||||||
|
td = SubscribeBundleTestData.from_behave_context(context)
|
||||||
|
td.kvs.access_token = getattr(context, "access_token", None) or td.kvs.access_token
|
||||||
|
td.prepare_two_places_three_services_plans_subscription()
|
||||||
|
context.bundle_place_a_id = td.place_a_id
|
||||||
|
context.bundle_place_b_id = td.place_b_id
|
||||||
|
context.bundle_subscriber_id = td.subscriber_id
|
||||||
|
|
||||||
|
|
||||||
|
@then("members and place services show only services for subscriber place") # pyright: ignore[reportGeneralTypeIssues]
|
||||||
|
def step_assert_bundle_scope(context) -> None:
|
||||||
|
td = SubscribeBundleTestData.from_behave_context(context)
|
||||||
|
td.assert_user_sees_only_place_services_via_members_and_place()
|
||||||
1
Subscribe_to_bundle/testdata/__init__.py
vendored
Normal file
1
Subscribe_to_bundle/testdata/__init__.py
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
# testdata
|
||||||
450
Subscribe_to_bundle/testdata/subscribe_bundle_test_data.py
vendored
Normal file
450
Subscribe_to_bundle/testdata/subscribe_bundle_test_data.py
vendored
Normal file
@ -0,0 +1,450 @@
|
|||||||
|
# pyright: reportCallIssue=false
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import json
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from typing import Any, Callable, Optional
|
||||||
|
|
||||||
|
import allure # pyright: ignore[reportMissingImports]
|
||||||
|
from allure_commons.types import AttachmentType # pyright: ignore[reportMissingImports]
|
||||||
|
|
||||||
|
from KVSTest.testdata.kvs_test_data import KVSTestData
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
BUNDLE_SCOPE_QUERY = """
|
||||||
|
query bundleScope($place_id: String!, $pid: String!) {
|
||||||
|
members(filters: { place_id: $place_id }) {
|
||||||
|
results {
|
||||||
|
id
|
||||||
|
user { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
place(id: $pid) {
|
||||||
|
results {
|
||||||
|
id
|
||||||
|
services { id title }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
MEMBERS_ONLY_QUERY = """
|
||||||
|
query membersByPlace($place_id: String!) {
|
||||||
|
members(filters: { place_id: $place_id }) {
|
||||||
|
results {
|
||||||
|
id
|
||||||
|
user { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class SubscribeBundleTestData:
|
||||||
|
"""
|
||||||
|
Два места, три сервиса, два тарифа (plan) — «пакет» из двух сервисов на место 1 и отдельный на место 2,
|
||||||
|
подписка пользователя только на место 1. Проверка: members + сервисы места (place.services) для того же place_id.
|
||||||
|
"""
|
||||||
|
|
||||||
|
company_id: str = DEFAULT_COMPANY_ID
|
||||||
|
parent_place_id: str = "6915dc03462d5aea0adc8cbd"
|
||||||
|
|
||||||
|
kvs: KVSTestData | None = None
|
||||||
|
place_a_id: Optional[str] = None
|
||||||
|
place_b_id: Optional[str] = None
|
||||||
|
service_ids: list[str] = field(default_factory=list)
|
||||||
|
plan_bundle_id: Optional[str] = None
|
||||||
|
plan_place_b_id: Optional[str] = None
|
||||||
|
subscriber_id: Optional[str] = None
|
||||||
|
subscription_id: Optional[str] = None
|
||||||
|
subscription_services_ids: list[str] = field(default_factory=list)
|
||||||
|
_bundle_scope_uses_place_services: Optional[bool] = None
|
||||||
|
|
||||||
|
_cleanup_fns: Optional[list[Callable[[], None]]] = None
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def from_behave_context(cls, context: Any, *, company_id: str = DEFAULT_COMPANY_ID) -> "SubscribeBundleTestData":
|
||||||
|
td: SubscribeBundleTestData | None = getattr(context, "subscribe_bundle_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, "subscribe_bundle_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:
|
||||||
|
assert self.kvs is not None
|
||||||
|
return self.kvs.ensure_token()
|
||||||
|
|
||||||
|
def prepare_two_places_three_services_plans_subscription(self) -> None:
|
||||||
|
token = self.ensure_token()
|
||||||
|
suffix = str(int(time.time()))
|
||||||
|
|
||||||
|
mutation_places = """
|
||||||
|
mutation ($place_type: PlaceType!, $names: [String!]!, $parent_id: String) {
|
||||||
|
createPlaceMultiple(
|
||||||
|
dto: {place_type: $place_type, names: $names, parent_id: $parent_id}
|
||||||
|
) {
|
||||||
|
id
|
||||||
|
__typename
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
variables_places = {
|
||||||
|
"names": [f"bundle-a-{suffix}", f"bundle-b-{suffix}"],
|
||||||
|
"parent_id": self.parent_place_id,
|
||||||
|
"place_type": "flat",
|
||||||
|
}
|
||||||
|
with allure.step("GraphQL: createPlaceMultiple (two places)"):
|
||||||
|
resp = _exec_or_fail(
|
||||||
|
op_name="createPlaceMultiple(mutation)",
|
||||||
|
token=token,
|
||||||
|
query=mutation_places,
|
||||||
|
variables=variables_places,
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("createPlaceMultiple (bundle)", resp)
|
||||||
|
created = resp.get("data", {}).get("createPlaceMultiple")
|
||||||
|
rows = created if isinstance(created, list) else [created]
|
||||||
|
ids = [r.get("id") for r in rows if isinstance(r, dict) and r.get("id")]
|
||||||
|
assert len(ids) >= 2, f"Ожидали 2 места, получили: {rows!r}"
|
||||||
|
self.place_a_id, self.place_b_id = ids[0], ids[1]
|
||||||
|
|
||||||
|
service_cleanup_fns: list[Callable[[], None]] = []
|
||||||
|
|
||||||
|
def _mk_service(title: str) -> str:
|
||||||
|
m = """
|
||||||
|
mutation createservice($title: String!, $type: String!) {
|
||||||
|
createService(dto: { title: $title, type: $type }) {
|
||||||
|
id
|
||||||
|
title
|
||||||
|
type
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
r = _exec_or_fail(
|
||||||
|
op_name="createService(mutation)",
|
||||||
|
token=token,
|
||||||
|
query=m,
|
||||||
|
variables={"title": title, "type": "access"},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
svc = r.get("data", {}).get("createService")
|
||||||
|
assert isinstance(svc, dict) and svc.get("id"), f"createService: {r!r}"
|
||||||
|
sid = str(svc["id"])
|
||||||
|
|
||||||
|
def _del_svc(s: str = sid) -> None:
|
||||||
|
dm = """mutation { deleteService(id: "%s") }""".strip() % s
|
||||||
|
_exec_or_fail(op_name="deleteService(mutation)", token=self.ensure_token(), query=dm, variables=None, company_id=self.company_id)
|
||||||
|
|
||||||
|
service_cleanup_fns.append(_del_svc)
|
||||||
|
return sid
|
||||||
|
|
||||||
|
with allure.step("GraphQL: createService x3"):
|
||||||
|
s1 = _mk_service(f"bundle-s1-{suffix}")
|
||||||
|
s2 = _mk_service(f"bundle-s2-{suffix}")
|
||||||
|
s3 = _mk_service(f"bundle-s3-{suffix}")
|
||||||
|
self.service_ids = [s1, s2, s3]
|
||||||
|
|
||||||
|
bind_m = """
|
||||||
|
mutation ($dto: AddPlaceToServiceInput!) {
|
||||||
|
addPlaceToService(dto: $dto) { id }
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
|
||||||
|
def _bind(sid: str, pid: str) -> None:
|
||||||
|
_exec_or_fail(
|
||||||
|
op_name="addPlaceToService",
|
||||||
|
token=token,
|
||||||
|
query=bind_m,
|
||||||
|
variables={"dto": {"service_id": sid, "place_id": pid}},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
with allure.step("GraphQL: addPlaceToService (s1,s2 -> place A; s3 -> place B)"):
|
||||||
|
assert self.place_a_id and self.place_b_id
|
||||||
|
_bind(s1, self.place_a_id)
|
||||||
|
_bind(s2, self.place_a_id)
|
||||||
|
_bind(s3, self.place_b_id)
|
||||||
|
|
||||||
|
def _unbind_all() -> None:
|
||||||
|
tok = self.ensure_token()
|
||||||
|
um = """
|
||||||
|
mutation ($dto: AddPlaceToServiceInput!) {
|
||||||
|
removePlaceFromService(dto: $dto) { id }
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
for sid, pid in ((s1, self.place_a_id), (s2, self.place_a_id), (s3, self.place_b_id)):
|
||||||
|
_exec_or_fail(op_name="removePlaceFromService", token=tok, query=um, variables={"dto": {"service_id": sid, "place_id": pid}}, company_id=self.company_id)
|
||||||
|
|
||||||
|
plan_m = """
|
||||||
|
mutation ($input: CreatePlanDTO!) {
|
||||||
|
createPlan(dto: $input) {
|
||||||
|
id
|
||||||
|
service_ids
|
||||||
|
place_ids
|
||||||
|
title
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
with allure.step("GraphQL: createPlan (bundle: two services on place A)"):
|
||||||
|
r1 = _exec_or_fail(
|
||||||
|
op_name="createPlan(bundle)",
|
||||||
|
token=token,
|
||||||
|
query=plan_m,
|
||||||
|
variables={
|
||||||
|
"input": {
|
||||||
|
"services": [s1, s2],
|
||||||
|
"place_ids": [self.place_a_id],
|
||||||
|
"price": 100,
|
||||||
|
"payment_interval": 1,
|
||||||
|
"discount": 0,
|
||||||
|
"title": f"bundle-plan-{suffix}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("createPlan bundle", r1)
|
||||||
|
p1 = r1.get("data", {}).get("createPlan")
|
||||||
|
assert isinstance(p1, dict) and p1.get("id"), f"createPlan: {r1!r}"
|
||||||
|
self.plan_bundle_id = str(p1["id"])
|
||||||
|
|
||||||
|
with allure.step("GraphQL: createPlan (single service on place B)"):
|
||||||
|
r2 = _exec_or_fail(
|
||||||
|
op_name="createPlan(placeB)",
|
||||||
|
token=token,
|
||||||
|
query=plan_m,
|
||||||
|
variables={
|
||||||
|
"input": {
|
||||||
|
"services": [s3],
|
||||||
|
"place_ids": [self.place_b_id],
|
||||||
|
"price": 50,
|
||||||
|
"payment_interval": 1,
|
||||||
|
"discount": 0,
|
||||||
|
"title": f"tariff-b-{suffix}",
|
||||||
|
}
|
||||||
|
},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("createPlan place B", r2)
|
||||||
|
p2 = r2.get("data", {}).get("createPlan")
|
||||||
|
assert isinstance(p2, dict) and p2.get("id"), f"createPlan B: {r2!r}"
|
||||||
|
self.plan_place_b_id = str(p2["id"])
|
||||||
|
|
||||||
|
def _del_plan_b() -> None:
|
||||||
|
if self.plan_place_b_id:
|
||||||
|
dm = """mutation { deletePlan(id: "%s") }""".strip() % self.plan_place_b_id
|
||||||
|
_exec_or_fail(op_name="deletePlan", token=self.ensure_token(), query=dm, variables=None, company_id=self.company_id)
|
||||||
|
|
||||||
|
def _del_plan_bundle() -> None:
|
||||||
|
if self.plan_bundle_id:
|
||||||
|
dm = """mutation { deletePlan(id: "%s") }""".strip() % self.plan_bundle_id
|
||||||
|
_exec_or_fail(op_name="deletePlan", token=self.ensure_token(), query=dm, variables=None, company_id=self.company_id)
|
||||||
|
|
||||||
|
assert self.kvs is not None
|
||||||
|
self.subscriber_id, _uname = self.kvs.create_new_user()
|
||||||
|
add_mut = """
|
||||||
|
mutation AddUserToPlace($input: AddUserToPlaceDTO!) {
|
||||||
|
addUserToPlace(dto: $input)
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
with allure.step("GraphQL: addUserToPlace (subscriber -> place A only)"):
|
||||||
|
ar = _exec_or_fail(
|
||||||
|
op_name="addUserToPlace",
|
||||||
|
token=token,
|
||||||
|
query=add_mut,
|
||||||
|
variables={"input": {"place_id": self.place_a_id, "account_id": self.subscriber_id}},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("addUserToPlace bundle", ar)
|
||||||
|
|
||||||
|
sub_m = """
|
||||||
|
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 }
|
||||||
|
place_id
|
||||||
|
user { id }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
with allure.step("GraphQL: createSubscription (bundle plan, place A)"):
|
||||||
|
sr = _exec_or_fail(
|
||||||
|
op_name="createSubscription",
|
||||||
|
token=token,
|
||||||
|
query=sub_m,
|
||||||
|
variables={
|
||||||
|
"plan_id": self.plan_bundle_id,
|
||||||
|
"subscriber_id": self.subscriber_id,
|
||||||
|
"place_id": self.place_a_id,
|
||||||
|
"service_id": s1,
|
||||||
|
},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("createSubscription bundle", sr)
|
||||||
|
sub = sr.get("data", {}).get("createSubscription")
|
||||||
|
assert isinstance(sub, dict) and sub.get("id"), f"createSubscription: {sr!r}"
|
||||||
|
self.subscription_id = str(sub["id"])
|
||||||
|
svcs = sub.get("services")
|
||||||
|
if isinstance(svcs, list):
|
||||||
|
self.subscription_services_ids = [str(x["id"]) for x in svcs if isinstance(x, dict) and x.get("id")]
|
||||||
|
|
||||||
|
def _del_sub() -> None:
|
||||||
|
if not self.subscription_id:
|
||||||
|
return
|
||||||
|
dm = """mutation($id: String!) { deleteSubscription(id: $id) }""".strip()
|
||||||
|
_exec_or_fail(
|
||||||
|
op_name="deleteSubscription",
|
||||||
|
token=self.ensure_token(),
|
||||||
|
query=dm,
|
||||||
|
variables={"id": self.subscription_id},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
|
||||||
|
pa, pb = self.place_a_id, self.place_b_id
|
||||||
|
|
||||||
|
def _del_place_b_fn() -> None:
|
||||||
|
if pb:
|
||||||
|
dm = """mutation { deletePlace(id: "%s") }""".strip() % pb
|
||||||
|
try:
|
||||||
|
_exec_or_fail(op_name="deletePlace", token=self.ensure_token(), query=dm, variables=None, company_id=self.company_id)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
def _del_place_a_fn() -> None:
|
||||||
|
if pa:
|
||||||
|
dm = """mutation { deletePlace(id: "%s") }""".strip() % pa
|
||||||
|
try:
|
||||||
|
_exec_or_fail(op_name="deletePlace", token=self.ensure_token(), query=dm, variables=None, company_id=self.company_id)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
self._register_cleanup(_del_place_b_fn)
|
||||||
|
self._register_cleanup(_del_place_a_fn)
|
||||||
|
for fn in service_cleanup_fns:
|
||||||
|
self._register_cleanup(fn)
|
||||||
|
self._register_cleanup(_unbind_all)
|
||||||
|
self._register_cleanup(_del_plan_b)
|
||||||
|
self._register_cleanup(_del_plan_bundle)
|
||||||
|
self._register_cleanup(_del_sub)
|
||||||
|
|
||||||
|
def query_members_for_place(self, *, place_id: str) -> dict[str, Any]:
|
||||||
|
token = self.ensure_token()
|
||||||
|
with allure.step("GraphQL: members(filters.place_id)"):
|
||||||
|
resp = _exec_or_fail(
|
||||||
|
op_name="members(query)",
|
||||||
|
token=token,
|
||||||
|
query=MEMBERS_ONLY_QUERY,
|
||||||
|
variables={"place_id": place_id},
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
_attach_json("members response", resp)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def query_bundle_scope(self, *, place_id: str) -> dict[str, Any]:
|
||||||
|
token = self.ensure_token()
|
||||||
|
if self._bundle_scope_uses_place_services is False:
|
||||||
|
mresp = self.query_members_for_place(place_id=place_id)
|
||||||
|
return {"data": {"members": mresp.get("data", {}).get("members"), "place": {"results": []}}}
|
||||||
|
|
||||||
|
with allure.step("GraphQL: bundleScope (members + place.services)"):
|
||||||
|
try:
|
||||||
|
resp = execute_graphql(
|
||||||
|
query=BUNDLE_SCOPE_QUERY,
|
||||||
|
variables={"place_id": place_id, "pid": place_id},
|
||||||
|
company_id=self.company_id,
|
||||||
|
access_token=token,
|
||||||
|
)
|
||||||
|
self._bundle_scope_uses_place_services = True
|
||||||
|
except RuntimeError as e:
|
||||||
|
if "services" in str(e) and "PlaceObject" in str(e):
|
||||||
|
self._bundle_scope_uses_place_services = False
|
||||||
|
allure.attach(
|
||||||
|
str(e),
|
||||||
|
name="bundleScope: place.services not supported, using members + subscription.services",
|
||||||
|
attachment_type=AttachmentType.TEXT,
|
||||||
|
)
|
||||||
|
mresp = self.query_members_for_place(place_id=place_id)
|
||||||
|
return {
|
||||||
|
"data": {
|
||||||
|
"members": mresp.get("data", {}).get("members"),
|
||||||
|
"place": {"results": []},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raise
|
||||||
|
_attach_json("bundleScope response", resp)
|
||||||
|
return resp
|
||||||
|
|
||||||
|
def assert_user_sees_only_place_services_via_members_and_place(self) -> None:
|
||||||
|
assert self.place_a_id and self.place_b_id and self.subscriber_id
|
||||||
|
assert len(self.service_ids) >= 3
|
||||||
|
s1, s2, s3 = self.service_ids[0], self.service_ids[1], self.service_ids[2]
|
||||||
|
|
||||||
|
for label, pid in ("placeA", self.place_a_id), ("placeB", self.place_b_id):
|
||||||
|
resp = self.query_bundle_scope(place_id=pid)
|
||||||
|
members = resp.get("data", {}).get("members", {}).get("results", [])
|
||||||
|
assert isinstance(members, list), f"{label}: members.results не list"
|
||||||
|
user_ids = [m.get("user", {}).get("id") for m in members if isinstance(m, dict) and isinstance(m.get("user"), dict)]
|
||||||
|
if label == "placeA":
|
||||||
|
assert self.subscriber_id in user_ids, f"Подписчик должен быть member места A: {user_ids!r}"
|
||||||
|
else:
|
||||||
|
assert self.subscriber_id not in user_ids, f"Подписчик не должен быть member места B: {user_ids!r}"
|
||||||
|
|
||||||
|
places = resp.get("data", {}).get("place", {}).get("results", [])
|
||||||
|
services = None
|
||||||
|
if isinstance(places, list) and places and isinstance(places[0], dict):
|
||||||
|
services = places[0].get("services")
|
||||||
|
|
||||||
|
if isinstance(services, list) and services:
|
||||||
|
svc_ids = {s.get("id") for s in services if isinstance(s, dict)}
|
||||||
|
if label == "placeA":
|
||||||
|
assert s1 in svc_ids and s2 in svc_ids, f"Место A должно содержать s1,s2: {svc_ids!r}"
|
||||||
|
assert s3 not in svc_ids, f"Сервис места B не должен быть на месте A: {svc_ids!r}"
|
||||||
|
else:
|
||||||
|
assert s3 in svc_ids, f"Место B должно содержать s3: {svc_ids!r}"
|
||||||
|
assert s1 not in svc_ids and s2 not in svc_ids, f"Сервисы места A не должны быть на B: {svc_ids!r}"
|
||||||
|
elif label == "placeA" and self.subscription_services_ids:
|
||||||
|
sub_ids = {str(x) for x in self.subscription_services_ids}
|
||||||
|
assert sub_ids <= {s1, s2}, f"Подписка на месте A должна включать только сервисы пакета A: {sub_ids!r}, ожидали подмножество {{{s1!r}, {s2!r}}}"
|
||||||
|
assert s3 not in sub_ids, f"Подписка не должна содержать сервис места B (s3): {sub_ids!r}"
|
||||||
|
elif label == "placeA":
|
||||||
|
allure.attach(
|
||||||
|
"Нет place.services и createSubscription не вернул services — ослабленная проверка только по members.",
|
||||||
|
name="bundleScope: limited assertions",
|
||||||
|
attachment_type=AttachmentType.TEXT,
|
||||||
|
)
|
||||||
@ -0,0 +1,26 @@
|
|||||||
|
{
|
||||||
|
"priority": 10,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"urlPath": "/graphql",
|
||||||
|
"bodyPatterns": [
|
||||||
|
{
|
||||||
|
"contains": "createPlaceMultiple"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
"jsonBody": {
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{ "id": "wm_place_a", "__typename": "Place" },
|
||||||
|
{ "id": "wm_place_b", "__typename": "Place" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,48 @@
|
|||||||
|
{
|
||||||
|
"priority": 5,
|
||||||
|
"request": {
|
||||||
|
"method": "POST",
|
||||||
|
"urlPath": "/graphql",
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": {
|
||||||
|
"equalTo": "application/json"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"bodyPatterns": [
|
||||||
|
{
|
||||||
|
"contains": "bundleScope"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"response": {
|
||||||
|
"status": 200,
|
||||||
|
"headers": {
|
||||||
|
"Content-Type": "application/json"
|
||||||
|
},
|
||||||
|
"jsonBody": {
|
||||||
|
"data": {
|
||||||
|
"members": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "wm_member_1",
|
||||||
|
"user": {
|
||||||
|
"id": "wm_subscriber"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"place": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "wm_place",
|
||||||
|
"services": [
|
||||||
|
{ "id": "wm_s1", "title": "svc-1" },
|
||||||
|
{ "id": "wm_s2", "title": "svc-2" }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -108,6 +108,20 @@ query ticketinfo($place_id: String!) {
|
|||||||
cache=False,
|
cache=False,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
# Важно: для проверки "authorized" мы смотрим поле ticket.assignee.
|
||||||
|
# Поэтому нужно назначить ticket на нового employee до смены категории.
|
||||||
|
mutation_assign = """
|
||||||
|
mutation assignTicketEmployee($ticket_id: String!, $employee_user_id: String!) {
|
||||||
|
assignTicketEmployee(dto: {ticket_id: $ticket_id, employee_user_id: $employee_user_id})
|
||||||
|
}
|
||||||
|
""".strip()
|
||||||
|
execute_graphql(
|
||||||
|
query=mutation_assign,
|
||||||
|
variables={"ticket_id": td.ticket_id, "employee_user_id": td.account_id},
|
||||||
|
company_id=td.company_id,
|
||||||
|
access_token=token,
|
||||||
|
)
|
||||||
|
|
||||||
# cleanup: вернуть категорию обратно
|
# cleanup: вернуть категорию обратно
|
||||||
cleanup_fns = getattr(context, "_cleanup_fns", None)
|
cleanup_fns = getattr(context, "_cleanup_fns", None)
|
||||||
if isinstance(cleanup_fns, list):
|
if isinstance(cleanup_fns, list):
|
||||||
|
|||||||
24
Ticket/testdata/ticket_test_data.py
vendored
24
Ticket/testdata/ticket_test_data.py
vendored
@ -238,8 +238,17 @@ mutation createticket($category_id: String!, $place_id: String!) {
|
|||||||
self.ticket_id = ticket_id
|
self.ticket_id = ticket_id
|
||||||
|
|
||||||
def _cleanup_delete_ticket() -> None:
|
def _cleanup_delete_ticket() -> None:
|
||||||
delete_mutation = """mutation deleteTicket($id: String!) { deleteTicket(id: $id) }""".strip()
|
# На стенде deleteTicket часто принимается только в форме без variables (как в GraphQL Playground),
|
||||||
_exec_or_fail(op_name="deleteTicket(mutation)", token=token, query=delete_mutation, variables={"id": ticket_id}, company_id=self.company_id)
|
# а с $id иногда отдаёт 403. Берём свежий токен из admin_data — тот же, что и для ручного вызова.
|
||||||
|
fresh = admin_data.get_or_create_user("tester").access_token or self.access_token or token
|
||||||
|
delete_mutation = """mutation { deleteTicket(id: "%s") }""".strip() % ticket_id
|
||||||
|
_exec_or_fail(
|
||||||
|
op_name="deleteTicket(mutation)",
|
||||||
|
token=fresh,
|
||||||
|
query=delete_mutation,
|
||||||
|
variables=None,
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
|
||||||
self._register_cleanup(_cleanup_delete_ticket)
|
self._register_cleanup(_cleanup_delete_ticket)
|
||||||
return ticket_id
|
return ticket_id
|
||||||
@ -266,8 +275,15 @@ mutation createticket($category_id: String!, $place_id: String!) {
|
|||||||
self.ticket_id = ticket_id
|
self.ticket_id = ticket_id
|
||||||
|
|
||||||
def _cleanup_delete_ticket() -> None:
|
def _cleanup_delete_ticket() -> None:
|
||||||
delete_mutation = """mutation deleteTicket($id: String!) { deleteTicket(id: $id) }""".strip()
|
fresh = admin_data.get_or_create_user("tester").access_token or self.access_token or token
|
||||||
_exec_or_fail(op_name="deleteTicket(mutation)", token=token, query=delete_mutation, variables={"id": ticket_id}, company_id=self.company_id)
|
delete_mutation = """mutation { deleteTicket(id: "%s") }""".strip() % ticket_id
|
||||||
|
_exec_or_fail(
|
||||||
|
op_name="deleteTicket(mutation)",
|
||||||
|
token=fresh,
|
||||||
|
query=delete_mutation,
|
||||||
|
variables=None,
|
||||||
|
company_id=self.company_id,
|
||||||
|
)
|
||||||
|
|
||||||
# Удалять ticket первым (до категории/плейса).
|
# Удалять ticket первым (до категории/плейса).
|
||||||
self._register_cleanup(_cleanup_delete_ticket)
|
self._register_cleanup(_cleanup_delete_ticket)
|
||||||
|
|||||||
40
allure-report/data/attachments/10039e446fe48595.txt
Normal file
40
allure-report/data/attachments/10039e446fe48595.txt
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
Traceback (most recent call last):
|
||||||
|
File "C:\Users\Степаан\PycharmProjects\work\worklib\graphql_client.py", line 176, in execute_graphql
|
||||||
|
with urllib.request.urlopen(req, timeout=timeout_s) as resp:
|
||||||
|
~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 187, in urlopen
|
||||||
|
return opener.open(url, data, timeout)
|
||||||
|
~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 493, in open
|
||||||
|
response = meth(req, response)
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 602, in http_response
|
||||||
|
response = self.parent.error(
|
||||||
|
'http', request, response, code, msg, hdrs)
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 531, in error
|
||||||
|
return self._call_chain(*args)
|
||||||
|
~~~~~~~~~~~~~~~~^^^^^^^
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 464, in _call_chain
|
||||||
|
result = func(*args)
|
||||||
|
File "C:\Users\Степаан\AppData\Local\Python\pythoncore-3.14-64\Lib\urllib\request.py", line 611, in http_error_default
|
||||||
|
raise HTTPError(req.full_url, code, msg, hdrs, fp)
|
||||||
|
urllib.error.HTTPError: HTTP Error 400: Bad Request
|
||||||
|
|
||||||
|
The above exception was the direct cause of the following exception:
|
||||||
|
|
||||||
|
Traceback (most recent call last):
|
||||||
|
File "Pass_request\features\environment.py", line 49, in after_scenario
|
||||||
|
fn()
|
||||||
|
~~^^
|
||||||
|
File "C:\Users\Степаан\PycharmProjects\work\Pass_request\testdata\pass_request_test_data.py", line 1440, in _cleanup_delete_pass
|
||||||
|
_exec_or_fail(op_name="deletePass", token=token, query=delete_mutation, variables={"id": pass_id}, company_id=self.company_id)
|
||||||
|
~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||||
|
File "C:\Users\Степаан\PycharmProjects\work\Pass_request\testdata\pass_request_test_data.py", line 30, in _exec_or_fail
|
||||||
|
return execute_graphql(
|
||||||
|
query=query,
|
||||||
|
...<2 lines>...
|
||||||
|
access_token=token,
|
||||||
|
)
|
||||||
|
File "C:\Users\Степаан\PycharmProjects\work\worklib\graphql_client.py", line 180, in execute_graphql
|
||||||
|
raise RuntimeError(f"GraphQL HTTP {e.code}: {body}") from e
|
||||||
|
RuntimeError: GraphQL HTTP 400: {"errors":[{"message":"Unknown argument \"id\" on field \"Mutation.deletePass\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"},{"message":"Field \"deletePass\" argument \"pass_id\" of type \"String!\" is required, but it was not provided.","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
|
|
||||||
1
allure-report/data/attachments/1021191475dddb91.txt
Normal file
1
allure-report/data/attachments/1021191475dddb91.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"setPlaceEntrances\" on type \"Mutation\". Did you mean \"deleteEntrance\" or \"createEntrance\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
1
allure-report/data/attachments/102c0b01035a2517.txt
Normal file
1
allure-report/data/attachments/102c0b01035a2517.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Forbidden (403) для GraphQL операции. Проверьте креды/права. Можно задать env: AUTH_USERNAME/AUTH_PASSWORD/AUTH_GRANT_TYPE.
|
||||||
1
allure-report/data/attachments/10338c64c7e1c2f5.txt
Normal file
1
allure-report/data/attachments/10338c64c7e1c2f5.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"connectEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/103eb4b78f651b54.json
Normal file
7
allure-report/data/attachments/103eb4b78f651b54.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addEmployee": {
|
||||||
|
"id": "6a02f6c6ec11a09b88a1eb9a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
allure-report/data/attachments/105934f275db5171.json
Normal file
75
allure-report/data/attachments/105934f275db5171.json
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"place": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "69f9beb232367dfb4b45a76f",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9beb232367dfb4b45a772",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9beb217bb1e0c5fc4e12e",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "0b6623c1-532f-47cf-aee8-a3d07688035e"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "3c110b22-4b42-43eb-a0f8-e66718862b4a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9beb217bb1e0c5fc4e131",
|
||||||
|
"members": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/10a5ece60d4d2513.txt
Normal file
1
allure-report/data/attachments/10a5ece60d4d2513.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"connectEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
10
allure-report/data/attachments/10b6b8ad39ff338f.json
Normal file
10
allure-report/data/attachments/10b6b8ad39ff338f.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "6a0576a332367dfb4b45abb5",
|
||||||
|
"__typename": "PlaceObject"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/10bc9a597e2fd33d.txt
Normal file
1
allure-report/data/attachments/10bc9a597e2fd33d.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addEntranceToPlace\" on type \"Mutation\". Did you mean \"addUserToPlace\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
24
allure-report/data/attachments/10c5c7e71603dbff.json
Normal file
24
allure-report/data/attachments/10c5c7e71603dbff.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"members": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "493b2da3-706c-4528-b758-c14626fe0c29",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "493b2da3-706c-4528-b758-c14626fe0c29"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "86dd0882-68f5-46c8-9b95-21d9f9deb73a",
|
||||||
|
"status": "pending",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "86dd0882-68f5-46c8-9b95-21d9f9deb73a"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/10d5628baa8b070e.json
Normal file
7
allure-report/data/attachments/10d5628baa8b070e.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/10dc1f55f53b5b.txt
Normal file
1
allure-report/data/attachments/10dc1f55f53b5b.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"attachEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
10
allure-report/data/attachments/10ed2478b5c069ad.json
Normal file
10
allure-report/data/attachments/10ed2478b5c069ad.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "69f9bef6037d44249d0d168c",
|
||||||
|
"__typename": "PlaceObject"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/11024a46d18990.txt
Normal file
1
allure-report/data/attachments/11024a46d18990.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addPlaceEntrance\" on type \"Mutation\". Did you mean \"deleteEntrance\", \"addPlaceToService\", \"createEntrance\", or \"addPlaceToBundle\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
18
allure-report/data/attachments/110f5d2107e02cf5.json
Normal file
18
allure-report/data/attachments/110f5d2107e02cf5.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createUser": {
|
||||||
|
"id": "5af8c275-2958-43d8-bb9a-453265bc958b",
|
||||||
|
"created_at": "2026-05-04T14:22:25.672Z",
|
||||||
|
"updated_at": "2026-05-04T14:22:25.672Z",
|
||||||
|
"username": "+79996266237",
|
||||||
|
"user_data": {
|
||||||
|
"first_name": "place",
|
||||||
|
"last_name": "member",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"is_demo": true,
|
||||||
|
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
allure-report/data/attachments/11284ab5e419a280.json
Normal file
8
allure-report/data/attachments/11284ab5e419a280.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f8abc932367dfb4b45a3c3",
|
||||||
|
"member_id": "420d0abf-e1aa-40c5-84e7-3601efed6406"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
allure-report/data/attachments/11371e53da2999b0.json
Normal file
18
allure-report/data/attachments/11371e53da2999b0.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createUser": {
|
||||||
|
"id": "30b165ca-21e8-4110-b7e0-4cd8bf69f514",
|
||||||
|
"created_at": "2026-05-05T10:56:42.700Z",
|
||||||
|
"updated_at": "2026-05-05T10:56:42.700Z",
|
||||||
|
"username": "+79992763965",
|
||||||
|
"user_data": {
|
||||||
|
"first_name": "owner",
|
||||||
|
"last_name": "passreq",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"is_demo": true,
|
||||||
|
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/11407a97782468b6.txt
Normal file
1
allure-report/data/attachments/11407a97782468b6.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"connectEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
9
allure-report/data/attachments/11692ec6a8ea1cc1.json
Normal file
9
allure-report/data/attachments/11692ec6a8ea1cc1.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createService": {
|
||||||
|
"id": "69f9cc931b4cbdc23d4509dc",
|
||||||
|
"title": "pass-service-1777978515",
|
||||||
|
"type": "access"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
allure-report/data/attachments/116959ee20fa6fab.json
Normal file
10
allure-report/data/attachments/116959ee20fa6fab.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "69fde634037d44249d0d1a23",
|
||||||
|
"__typename": "PlaceObject"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
24
allure-report/data/attachments/116f4b95348a3834.json
Normal file
24
allure-report/data/attachments/116f4b95348a3834.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"members": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "2ea6baf0-886d-44b3-aa44-c26d786755a3",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "2ea6baf0-886d-44b3-aa44-c26d786755a3"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "d888229f-441f-4504-8c0a-9fec64e01f1c",
|
||||||
|
"status": "pending",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "d888229f-441f-4504-8c0a-9fec64e01f1c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/11828e6faf8ebf92.txt
Normal file
1
allure-report/data/attachments/11828e6faf8ebf92.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"setPlaceEntrances\" on type \"Mutation\". Did you mean \"deleteEntrance\" or \"createEntrance\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/1194636c831ddbe2.json
Normal file
7
allure-report/data/attachments/1194636c831ddbe2.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/11a4148c0b94cfc9.json
Normal file
7
allure-report/data/attachments/11a4148c0b94cfc9.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addEmployee": {
|
||||||
|
"id": "69f8abc7514efad27fabd7f5"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/11a873c908ffaf3f.json
Normal file
7
allure-report/data/attachments/11a873c908ffaf3f.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addPlaceToService": {
|
||||||
|
"id": "69f9d2840b1f8729e0528e44"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/11b01714850cd3e0.txt
Normal file
1
allure-report/data/attachments/11b01714850cd3e0.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"attachEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/11e74a73a4cfe981.json
Normal file
7
allure-report/data/attachments/11e74a73a4cfe981.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addPlaceToService": {
|
||||||
|
"id": "69f8a9c81b4cbdc23d450958"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/12151ded97da9c13.json
Normal file
7
allure-report/data/attachments/12151ded97da9c13.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/12267a6eda3e14d4.txt
Normal file
1
allure-report/data/attachments/12267a6eda3e14d4.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"setMemberStatus\" on type \"Mutation\". Did you mean \"updateMemberStatus\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
18
allure-report/data/attachments/1241a95f44576766.json
Normal file
18
allure-report/data/attachments/1241a95f44576766.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createUser": {
|
||||||
|
"id": "74152b85-8490-4ec5-b9d4-83075700498e",
|
||||||
|
"created_at": "2026-05-04T14:21:55.490Z",
|
||||||
|
"updated_at": "2026-05-04T14:21:55.490Z",
|
||||||
|
"username": "+79992826660",
|
||||||
|
"user_data": {
|
||||||
|
"first_name": "set",
|
||||||
|
"last_name": "worker",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"is_demo": true,
|
||||||
|
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/124ea3165feca9d0.txt
Normal file
1
allure-report/data/attachments/124ea3165feca9d0.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addEntranceToPlace\" on type \"Mutation\". Did you mean \"addUserToPlace\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
18
allure-report/data/attachments/125a6cef8afb157.json
Normal file
18
allure-report/data/attachments/125a6cef8afb157.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createUser": {
|
||||||
|
"id": "0177b402-386e-4c59-b0c1-dc3671fad20c",
|
||||||
|
"created_at": "2026-05-04T14:36:32.532Z",
|
||||||
|
"updated_at": "2026-05-04T14:36:32.532Z",
|
||||||
|
"username": "+79999641873",
|
||||||
|
"user_data": {
|
||||||
|
"first_name": "set",
|
||||||
|
"last_name": "worker",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"is_demo": true,
|
||||||
|
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/125cf2a97fe6630a.json
Normal file
7
allure-report/data/attachments/125cf2a97fe6630a.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
75
allure-report/data/attachments/12647b855e1e7e24.json
Normal file
75
allure-report/data/attachments/12647b855e1e7e24.json
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"place": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "69f9ccf132367dfb4b45a994",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9ccf1037d44249d0d1885",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9ccf117bb1e0c5fc4e358",
|
||||||
|
"members": [
|
||||||
|
{
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "2464e89d-69d5-4239-bfe5-61c0ea92c2aa"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "6b39c80a-ff3c-4ee4-818d-d340aad6322c"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9ccf117bb1e0c5fc4e35b",
|
||||||
|
"members": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/128d0805411bb5f3.json
Normal file
7
allure-report/data/attachments/128d0805411bb5f3.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
24
allure-report/data/attachments/12ae0fa37e7d23ac.json
Normal file
24
allure-report/data/attachments/12ae0fa37e7d23ac.json
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"members": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"id": "1a097da7-7d00-4834-9f43-d790ef80dabd",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "1a097da7-7d00-4834-9f43-d790ef80dabd"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "c5825d01-26be-497d-81b4-ec23b13f9071",
|
||||||
|
"status": "accepted",
|
||||||
|
"privileges": null,
|
||||||
|
"user": {
|
||||||
|
"id": "c5825d01-26be-497d-81b4-ec23b13f9071"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/12c357395512ffd0.json
Normal file
7
allure-report/data/attachments/12c357395512ffd0.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
allure-report/data/attachments/12ed041cc2b52274.json
Normal file
8
allure-report/data/attachments/12ed041cc2b52274.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f9c56117bb1e0c5fc4e218",
|
||||||
|
"member_id": "574ceec0-8961-4fce-8a50-b1465f078534"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/12fba202fd6ac499.json
Normal file
7
allure-report/data/attachments/12fba202fd6ac499.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/1306b6d82daf7910.txt
Normal file
1
allure-report/data/attachments/1306b6d82daf7910.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
Skip entrance<->place link. entrance_id='69f8abc217bb1e0c5fc4dcb2', place_id='69f8abc2037d44249d0d1260'. Attempts: ['addEntranceToPlace/dto-entrance_id', 'addEntranceToPlace/dto-entrance_ids', 'addEntranceToPlace/args-entrance_id', 'addEntranceToPlace/args-entrance_ids', 'attachEntranceToPlace/dto-entrance_id', 'attachEntranceToPlace/dto-entrance_ids', 'attachEntranceToPlace/args-entrance_id', 'attachEntranceToPlace/args-entrance_ids', 'setPlaceEntrances/dto-entrance_id', 'setPlaceEntrances/dto-entrance_ids', 'setPlaceEntrances/args-entrance_id', 'setPlaceEntrances/args-entrance_ids', 'addPlaceEntrance/dto-entrance_id', 'addPlaceEntrance/dto-entrance_ids', 'addPlaceEntrance/args-entrance_id', 'addPlaceEntrance/args-entrance_ids', 'connectEntranceToPlace/dto-entrance_id', 'connectEntranceToPlace/dto-entrance_ids', 'connectEntranceToPlace/args-entrance_id', 'connectEntranceToPlace/args-entrance_ids']. Last error: GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"connectEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/133bf717ec9591a8.json
Normal file
7
allure-report/data/attachments/133bf717ec9591a8.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addPlaceToService": {
|
||||||
|
"id": "69f9bf723dcf1a2e79fbf95f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
allure-report/data/attachments/133fe1422e1e5131.json
Normal file
8
allure-report/data/attachments/133fe1422e1e5131.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f8abc4c15e6311636d8656",
|
||||||
|
"member_id": "f84e4a72-0de2-4980-be9b-6b62bfe7e375"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/136f2b0ad75ba534.json
Normal file
7
allure-report/data/attachments/136f2b0ad75ba534.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/13aeab7bf0ceec7f.json
Normal file
7
allure-report/data/attachments/13aeab7bf0ceec7f.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/13b940dd0a5a8d65.json
Normal file
7
allure-report/data/attachments/13b940dd0a5a8d65.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
allure-report/data/attachments/13e24c0f922c2098.json
Normal file
8
allure-report/data/attachments/13e24c0f922c2098.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f8af20c15e6311636d87b1",
|
||||||
|
"member_id": "2ae06b7f-36fd-4c97-a20d-79bff7e4cbc2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/1403488753cfb25f.txt
Normal file
1
allure-report/data/attachments/1403488753cfb25f.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addPlaceEntrance\" on type \"Mutation\". Did you mean \"deleteEntrance\", \"addPlaceToService\", \"createEntrance\", or \"addPlaceToBundle\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
1
allure-report/data/attachments/140cea63426940e4.txt
Normal file
1
allure-report/data/attachments/140cea63426940e4.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addEntranceToPlace\" on type \"Mutation\". Did you mean \"addUserToPlace\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
10
allure-report/data/attachments/141cd1a3d66e3986.json
Normal file
10
allure-report/data/attachments/141cd1a3d66e3986.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "69f8a97a17bb1e0c5fc4d96b",
|
||||||
|
"__typename": "PlaceObject"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/141dc661759aac37.json
Normal file
7
allure-report/data/attachments/141dc661759aac37.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/142d5a0f593f3102.json
Normal file
7
allure-report/data/attachments/142d5a0f593f3102.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
27
allure-report/data/attachments/1431f90349ba354f.json
Normal file
27
allure-report/data/attachments/1431f90349ba354f.json
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"ticket": {
|
||||||
|
"results": [
|
||||||
|
{
|
||||||
|
"number": 431,
|
||||||
|
"id": "6a02f6c99e04d08097dedf77",
|
||||||
|
"category": {
|
||||||
|
"id": "6a02f6c99e04d08097dedf76",
|
||||||
|
"title": "tester1"
|
||||||
|
},
|
||||||
|
"assignee": {
|
||||||
|
"id": "6a02f6c9ec11a09b88a1eb9c",
|
||||||
|
"user": {
|
||||||
|
"id": "b8bbee32-3b44-43d2-b196-2a4436f9644b",
|
||||||
|
"username": "+79992499159",
|
||||||
|
"data": {
|
||||||
|
"first_name": "kvstest1",
|
||||||
|
"last_name": "kvstest2"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
allure-report/data/attachments/144186396fc5d442.json
Normal file
10
allure-report/data/attachments/144186396fc5d442.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "place_0ed1f34a71f8",
|
||||||
|
"__typename": "Place"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/145511b0fa5217f4.json
Normal file
7
allure-report/data/attachments/145511b0fa5217f4.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/147d93e9f6462a78.json
Normal file
7
allure-report/data/attachments/147d93e9f6462a78.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/148344e1a951e0ff.txt
Normal file
1
allure-report/data/attachments/148344e1a951e0ff.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Unknown type \"UpdateMemberStatusInput\". Did you mean \"UpdateMemberStatusDto\", \"UpdatableMemberStatus\", or \"UpdateTicketStatusDTO\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/1486b16a5f321684.json
Normal file
7
allure-report/data/attachments/1486b16a5f321684.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/1490d4b860e6008b.txt
Normal file
1
allure-report/data/attachments/1490d4b860e6008b.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"attachEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
1
allure-report/data/attachments/1493b579f61d5a18.txt
Normal file
1
allure-report/data/attachments/1493b579f61d5a18.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addEntranceToPlace\" on type \"Mutation\". Did you mean \"addUserToPlace\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
8
allure-report/data/attachments/14a08e0251df4459.json
Normal file
8
allure-report/data/attachments/14a08e0251df4459.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f8b187037d44249d0d15cc",
|
||||||
|
"member_id": "b4ed4b98-7366-4b9f-b9ed-abdce1ae2915"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
8
allure-report/data/attachments/14ba1a50d9078a94.json
Normal file
8
allure-report/data/attachments/14ba1a50d9078a94.json
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"addUserToPlace": {
|
||||||
|
"place_id": "69f8b11e17bb1e0c5fc4e031",
|
||||||
|
"member_id": "5b1b9c30-5388-460a-884f-be581219e3e8"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/14dc38914861c62d.txt
Normal file
1
allure-report/data/attachments/14dc38914861c62d.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"addPlaceEntrance\" on type \"Mutation\". Did you mean \"deleteEntrance\", \"addPlaceToService\", \"createEntrance\", or \"addPlaceToBundle\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
7
allure-report/data/attachments/14e3b7778a30b836.json
Normal file
7
allure-report/data/attachments/14e3b7778a30b836.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/15142004964f7002.json
Normal file
7
allure-report/data/attachments/15142004964f7002.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createTicket": {
|
||||||
|
"id": "6a02d2d59e04d08097dedf3f"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/152450d61d79993e.txt
Normal file
1
allure-report/data/attachments/152450d61d79993e.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL errors: [{'message': 'Variable "$input" got invalid value { place_id: "6a057723037d44249d0d1b37", account_id: "942a37d2-e008-43c9-bf50-0a12c71026cc", privilege: "trusted" }; Field "privilege" is not defined by type "AddUserToPlaceDTO".', 'code': 'Server Error', 'status': 500, 'description': 'The server encountered an unexpected condition which prevented it from fulfilling the request'}]
|
||||||
9
allure-report/data/attachments/15323782cdad85e6.json
Normal file
9
allure-report/data/attachments/15323782cdad85e6.json
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createService": {
|
||||||
|
"id": "69f8ab9a0b1f8729e0528dd5",
|
||||||
|
"title": "pass-service-1777904538",
|
||||||
|
"type": "access"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/1537ff7b22d1f83b.txt
Normal file
1
allure-report/data/attachments/1537ff7b22d1f83b.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL errors: [{'message': 'Variable "$input" got invalid value { place_id: "69f9c58ec15e6311636d8cde", account_id: "1b2a95ba-c6b9-4ea7-8e1c-4978ae252fde", privilege: "trusted" }; Field "privilege" is not defined by type "AddUserToPlaceDTO".', 'code': 'Server Error', 'status': 500, 'description': 'The server encountered an unexpected condition which prevented it from fulfilling the request'}]
|
||||||
1
allure-report/data/attachments/155fb60a182afffc.txt
Normal file
1
allure-report/data/attachments/155fb60a182afffc.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"attachEntranceToPlace\" on type \"Mutation\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
10
allure-report/data/attachments/156e9fa7f5a8cec.json
Normal file
10
allure-report/data/attachments/156e9fa7f5a8cec.json
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createPlaceMultiple": [
|
||||||
|
{
|
||||||
|
"id": "place_81f3135758f1",
|
||||||
|
"__typename": "Place"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
7
allure-report/data/attachments/1580122958971912.json
Normal file
7
allure-report/data/attachments/1580122958971912.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"passRequests": {
|
||||||
|
"results": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
allure-report/data/attachments/158eea24ee801f19.json
Normal file
18
allure-report/data/attachments/158eea24ee801f19.json
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"createUser": {
|
||||||
|
"id": "0fd02201-6896-4c41-bca0-04b52966569a",
|
||||||
|
"created_at": "2026-05-05T09:57:57.319Z",
|
||||||
|
"updated_at": "2026-05-05T09:57:57.319Z",
|
||||||
|
"username": "+79999559011",
|
||||||
|
"user_data": {
|
||||||
|
"first_name": "pass",
|
||||||
|
"last_name": "request",
|
||||||
|
"email": ""
|
||||||
|
},
|
||||||
|
"is_demo": true,
|
||||||
|
"next_request_timestamp": "1970-01-01T00:00:00.000Z",
|
||||||
|
"roles": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/159d58811891ac9e.txt
Normal file
1
allure-report/data/attachments/159d58811891ac9e.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Field \"member_id\" is not defined by type \"PlaceFilters\".","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
15
allure-report/data/attachments/15ddbf45cefea073.json
Normal file
15
allure-report/data/attachments/15ddbf45cefea073.json
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
{
|
||||||
|
"data": {
|
||||||
|
"setUserPlaces": [
|
||||||
|
{
|
||||||
|
"id": "69f9ccf1037d44249d0d1885"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9ccf117bb1e0c5fc4e358"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "69f9ccf132367dfb4b45a994"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
1
allure-report/data/attachments/15f2fab4a772bfcb.txt
Normal file
1
allure-report/data/attachments/15f2fab4a772bfcb.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
GraphQL HTTP 400: {"errors":[{"message":"Cannot query field \"services\" on type \"PlaceObject\". Did you mean \"devices\" or \"stories\"?","code":"Server Error","status":500,"description":"The server encountered an unexpected condition which prevented it from fulfilling the request"}]}
|
||||||
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