lucius-mcp 0.2.2__py3-none-any.whl → 0.3.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. {lucius_mcp-0.2.2.dist-info → lucius_mcp-0.3.0.dist-info}/METADATA +8 -1
  2. {lucius_mcp-0.2.2.dist-info → lucius_mcp-0.3.0.dist-info}/RECORD +38 -19
  3. src/client/__init__.py +10 -0
  4. src/client/client.py +289 -8
  5. src/client/generated/README.md +11 -0
  6. src/client/generated/__init__.py +4 -0
  7. src/client/generated/api/__init__.py +2 -0
  8. src/client/generated/api/test_layer_controller_api.py +1746 -0
  9. src/client/generated/api/test_layer_schema_controller_api.py +1415 -0
  10. src/client/generated/docs/TestLayerControllerApi.md +407 -0
  11. src/client/generated/docs/TestLayerSchemaControllerApi.md +350 -0
  12. src/client/overridden/test_case_custom_fields_v2.py +254 -0
  13. src/services/__init__.py +8 -0
  14. src/services/launch_service.py +278 -0
  15. src/services/search_service.py +1 -1
  16. src/services/test_case_service.py +512 -92
  17. src/services/test_layer_service.py +416 -0
  18. src/tools/__init__.py +35 -0
  19. src/tools/create_test_case.py +38 -19
  20. src/tools/create_test_layer.py +33 -0
  21. src/tools/create_test_layer_schema.py +39 -0
  22. src/tools/delete_test_layer.py +31 -0
  23. src/tools/delete_test_layer_schema.py +31 -0
  24. src/tools/get_custom_fields.py +2 -1
  25. src/tools/get_test_case_custom_fields.py +34 -0
  26. src/tools/launches.py +112 -0
  27. src/tools/list_test_layer_schemas.py +43 -0
  28. src/tools/list_test_layers.py +38 -0
  29. src/tools/search.py +6 -3
  30. src/tools/test_layers.py +21 -0
  31. src/tools/update_test_case.py +48 -23
  32. src/tools/update_test_layer.py +33 -0
  33. src/tools/update_test_layer_schema.py +40 -0
  34. src/utils/__init__.py +4 -0
  35. src/utils/links.py +13 -0
  36. {lucius_mcp-0.2.2.dist-info → lucius_mcp-0.3.0.dist-info}/WHEEL +0 -0
  37. {lucius_mcp-0.2.2.dist-info → lucius_mcp-0.3.0.dist-info}/entry_points.txt +0 -0
  38. {lucius_mcp-0.2.2.dist-info → lucius_mcp-0.3.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: lucius-mcp
3
- Version: 0.2.2
3
+ Version: 0.3.0
4
4
  Summary: Allure TestOps MCP Server
5
5
  Project-URL: Homepage, https://github.com/ivanostanin/lucius-mcp
6
6
  Project-URL: Documentation, https://github.com/ivanostanin/lucius-mcp#readme
@@ -44,6 +44,7 @@ Requires-Dist: pre-commit>=4.0.0; extra == 'dev'
44
44
  Requires-Dist: pytest-asyncio>=1.3.0; extra == 'dev'
45
45
  Requires-Dist: pytest-cov>=7.0.0; extra == 'dev'
46
46
  Requires-Dist: pytest-mock>=3.15.1; extra == 'dev'
47
+ Requires-Dist: pytest-xdist>=3.8.0; extra == 'dev'
47
48
  Requires-Dist: pytest>=9.0.2; extra == 'dev'
48
49
  Requires-Dist: respx>=0.22.0; extra == 'dev'
49
50
  Requires-Dist: ruff>=0.14.14; extra == 'dev'
@@ -237,6 +238,12 @@ E2E tests verify the integration with a real Allure TestOps instance. They are i
237
238
  uv run --env-file .env.test pytest tests/e2e/
238
239
  ```
239
240
 
241
+ ```bash
242
+ # Run e2e tests in parallel mode
243
+ uv run --env-file .env.test pytest tests/e2e/ -n auto --dist loadfile
244
+ ```
245
+
246
+
240
247
  **Troubleshooting E2E Failures:**
241
248
 
242
249
  * **401 Unauthorized**: Check `ALLURE_API_TOKEN`. It might be expired.
@@ -1,17 +1,17 @@
1
1
  src/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
2
2
  src/main.py,sha256=JPT9QfOVOj5BGGVJtq6megXZqibTIHRV3zY38jQeI5g,2580
3
3
  src/version.py,sha256=ZAzHiNwu2X1UtwUkcHNmKrXBwRJeVvXbpdfi7wtv3DY,76
4
- src/client/__init__.py,sha256=L0qijQKzYI4ONrhKHv13ET2-Z12HcbgDKN6gHJ7gAVU,1410
5
- src/client/client.py,sha256=P94kg3Q0NzK2gDpSKlYxxC4QRo004k3kygVYmJaq0JU,51420
4
+ src/client/__init__.py,sha256=8FCHe0wiqraDBeSsnA2Q61C22OnGcdqy-ka2hv0NfnA,1634
5
+ src/client/client.py,sha256=GF6tsU7IBCGWroQpLaG0f2GmUz0oGRAm-P30EPKA4_E,62177
6
6
  src/client/exceptions.py,sha256=2iGpxxeiIkptvhptGFoqABI0cOLhGpIWX6c-I9Qc0yM,1498
7
- src/client/generated/README.md,sha256=u4zJ5sHRWRgZhAqtNYQGj8r8aRlMfeOJa61dA0bXwiY,85836
8
- src/client/generated/__init__.py,sha256=UM-tfbMX_J3GFEtxZlxdxsnpj8mPHPJ1Dj-T-bi9vDE,98122
7
+ src/client/generated/README.md,sha256=T3s1Ahzs5vS2DihyY1nP6123r2DqtsdvPyAJHhgGNqs,87576
8
+ src/client/generated/__init__.py,sha256=ncXolJLsz38xW_BPXP2OAmPpSR78REx5Qvk3LBXP-WM,98431
9
9
  src/client/generated/api_client.py,sha256=0m1Ax05egObKEi4V-APnDamj0FZVE5Z4isSu8QPIP8Y,27910
10
10
  src/client/generated/api_response.py,sha256=eMxw1mpmJcoGZ3gs9z6jM4oYoZ10Gjk333s9sKxGv7s,652
11
11
  src/client/generated/configuration.py,sha256=nJvRx1KvWIkb7B0u-PSKRtwmm8JTFPgBz1CpKlp5Gno,17823
12
12
  src/client/generated/exceptions.py,sha256=ZpDRfPSo4xseVbDop1tZQhxJrUweRUcuCHI-pociYmg,6516
13
13
  src/client/generated/rest.py,sha256=U6oxD5gY8dkbGrByetRZRb8UVU7IuFldQULHDM5xrF4,6470
14
- src/client/generated/api/__init__.py,sha256=1K0GST-qYJevppRL2VRBcmLcRA1q4pXOLseyEi6BtkY,2086
14
+ src/client/generated/api/__init__.py,sha256=QXe5ejBgZEUaC9rfD_RvDFafnAy2bmWcyjlQkKh51as,2271
15
15
  src/client/generated/api/custom_field_controller_api.py,sha256=Cu5yPnHHR8DH3MjY1bVF3W2QkhWQsKqSWg75rMo7BpQ,99963
16
16
  src/client/generated/api/custom_field_project_controller_api.py,sha256=itwkdD7cLKOIx2h4k_77lItQyETzhWtiqvjSoKfOpzE,90527
17
17
  src/client/generated/api/custom_field_project_controller_v2_api.py,sha256=iXJtWSwq87b_QOU9WB8srvZijqr9tSOfGGuNhlh2Adc,25852
@@ -33,6 +33,8 @@ src/client/generated/api/test_case_overview_controller_api.py,sha256=amyz7cRN5bJ
33
33
  src/client/generated/api/test_case_scenario_controller_api.py,sha256=ajdjmwRtB5dkb7ya2VZXnp9l5Hw-PpZFNK4WnHKBcaE,115728
34
34
  src/client/generated/api/test_case_search_controller_api.py,sha256=_qOZCmAd_esdDvAS_NHxICF0H-PZZXp2oRDEhi8ehVQ,25705
35
35
  src/client/generated/api/test_case_tag_controller_api.py,sha256=UEpnjq7pEzEbD8mVAKCrKiE6mJFTidkVeQuBNc_PhaU,21661
36
+ src/client/generated/api/test_layer_controller_api.py,sha256=6ZDK_WSbhGy45QwepnvQaiNIAh55DtvpDEYpG1iMbLY,66919
37
+ src/client/generated/api/test_layer_schema_controller_api.py,sha256=Jvf_dr2KDznDMyackmUXwJGf6ecYL93ftMdlEGAslrE,54561
36
38
  src/client/generated/docs/AccessGroupBulkDto.md,sha256=Vu4AJSBP-_PFXF0j9Zmz4AMGbxCJuc2iQyYDK_nt8KA,989
37
39
  src/client/generated/docs/AccessGroupCreateDto.md,sha256=HJKwQNlyaP2uFb6E3AAXC4_WTt1sH0yW0FnRlNJAcTI,1147
38
40
  src/client/generated/docs/AccessGroupDto.md,sha256=icYh1HU2B1u3Z0z-yGq4F9LCzSDiokIJPJGs1ijOaF8,1107
@@ -601,9 +603,11 @@ src/client/generated/docs/TestKeyImportFieldMapping.md,sha256=iqAckrgvUtOkzWW62r
601
603
  src/client/generated/docs/TestKeySchemaCreateDto.md,sha256=CEl3pbSdmaaCN-UeNr4Hbk6NBgP3cuQXxkqhpxuNWGU,1071
602
604
  src/client/generated/docs/TestKeySchemaDto.md,sha256=vm3Y47JH4Lbe34jQqmEYSpyq_eXF9ag3wRbVhj9me0g,1286
603
605
  src/client/generated/docs/TestKeySchemaPatchDto.md,sha256=LBIc2HYWzUHuFzNsMct-JPmg54uhxjkGLbocaBdPvQg,1049
606
+ src/client/generated/docs/TestLayerControllerApi.md,sha256=54am6JEDuAsVyZLlw_QgqFb4M6N0yMpilEzzPON11EI,12870
604
607
  src/client/generated/docs/TestLayerCreateDto.md,sha256=K2eDgc9lFoTTtk050z-FrzOI7ERHLJLTP63h4ulmNF8,948
605
608
  src/client/generated/docs/TestLayerDto.md,sha256=HxvwDGOxKQOGEj8DOdjrjllXCfstq5Um4HZ7jCi6gzI,1093
606
609
  src/client/generated/docs/TestLayerPatchDto.md,sha256=ycPN_vRSLsz1JgsccZiuMYasthocBKuhSfFIgM-g3Rw,946
610
+ src/client/generated/docs/TestLayerSchemaControllerApi.md,sha256=GScfBisbMyvG83i7AZU7-AxbnIX_2Xvd3jkru1VKG9E,11009
607
611
  src/client/generated/docs/TestLayerSchemaCreateDto.md,sha256=HC5LDCw0j2pfBdPzulwzQilB2hL2wyPABpSE7R4A3z0,1096
608
612
  src/client/generated/docs/TestLayerSchemaDto.md,sha256=GmGeA0bi-CFRfHRi_mmCwX73UtWoXsNAIsjMVAO1cs0,1288
609
613
  src/client/generated/docs/TestLayerSchemaPatchDto.md,sha256=vLucMqrWoRz8GZ0JgaYlRuLnt4Tiomv50eC_E1KcJfE,1074
@@ -1421,28 +1425,43 @@ src/client/generated/models/workflow_row_dto.py,sha256=lJaFfDmJISi6JTG5pvc2TKG_j
1421
1425
  src/client/generated/models/workflow_schema_create_dto.py,sha256=hgXdh72-pgT8QQeebQyXiwo4u5mYQOib8NCy_2UG4V8,2795
1422
1426
  src/client/generated/models/workflow_schema_dto.py,sha256=pK6kpb7vIHE4dkdBrS2G871tnt2abuAumdB8i_M5-gk,3807
1423
1427
  src/client/generated/models/workflow_schema_patch_dto.py,sha256=m4RDERQlbWY997XTMgufFp5vQgYEQRmkJ8jMQNhZjr4,2729
1424
- src/services/__init__.py,sha256=Lq29htiPlTQ-_gqsZQz_E4HB13KMYs2SgMsq2m9biLU,44
1428
+ src/client/overridden/test_case_custom_fields_v2.py,sha256=3bzOTWaOAUAJftDdHVSA76QjvyvOiWOnPcupMDV5WqE,10464
1429
+ src/services/__init__.py,sha256=4Wn2FhgzcBtFnG5SmMlKluRwAi0-0UnId0CuvDZfzYI,394
1425
1430
  src/services/attachment_service.py,sha256=_aQ-CAOCCDth_sWz1EyDw4W-1POQDsQ28YYCHUEXAlA,3890
1426
- src/services/search_service.py,sha256=KW8pzZ9_oo3nd9yam5Eh-Gp7eXKIQ6lKeYr3e5RHKCs,7853
1431
+ src/services/launch_service.py,sha256=9vqoZup115QMsf8JcEXVT-YTXX0ogVUO3qKV3D1Vyqs,10561
1432
+ src/services/search_service.py,sha256=H_j4Cpq-trDYh9P3DD3yU57gTftdxx-YN7O_zf5umio,7832
1427
1433
  src/services/shared_step_service.py,sha256=CSHLnf4ziuWG3XfeCHYokuVT-0t3T4ZwJzSs3W0cY4U,12722
1428
- src/services/test_case_service.py,sha256=Pkk9XWIqog22gzsoHbVWFJqAQZ_UVRIw374BfuWfAlI,43411
1429
- src/tools/__init__.py,sha256=29cJwQ92Yg7FOH0lJA6VPRcj8HNJvw1vQpaSndP5894,1308
1430
- src/tools/create_test_case.py,sha256=FNRZA4EwrcnrmBikqTSmYKDKxv6AcCYcyeTDrTGAg1I,3195
1434
+ src/services/test_case_service.py,sha256=e_Io-g0hgQbaEk6LRW4_xcQuh0_BgclITDkZKxkh8bE,62627
1435
+ src/services/test_layer_service.py,sha256=bu93l_I2zYrxAsAjtSeNVpUly6Uj-MVcLftgENM3uRI,14752
1436
+ src/tools/__init__.py,sha256=lusyeBu44J-2JS6DCzJIHrpp79uhdXgA3OnSIg5-suo,2301
1437
+ src/tools/create_test_case.py,sha256=DEFEU8wxJe-itsbfWR59cmXjlBpjlXo-3DB8fMx-qbs,3967
1438
+ src/tools/create_test_layer.py,sha256=6l8eP6bGqyOILJvOAhbCClht_FSYHLcgxCeNdwYMZhc,1111
1439
+ src/tools/create_test_layer_schema.py,sha256=QuHvBgSYqyyPPSDtMaijOuM1Lzt8mH3u96owTNyLMhA,1569
1431
1440
  src/tools/delete_test_case.py,sha256=kAWNAFN5on5jt7dCuWt4i-k8C8QKobtMMfgHizjbcH0,2058
1432
- src/tools/get_custom_fields.py,sha256=VxAikxd2GayOqw09Bm2E6jBQreBVPCJBLPLJNTHI-Ic,1749
1441
+ src/tools/delete_test_layer.py,sha256=nJ2ZhzM4Ca9seaoShczwjovHqsAxSLGWP0rcX1Kwke4,998
1442
+ src/tools/delete_test_layer_schema.py,sha256=GBsIAO6pikTMYDD0r-OCjtSGipseD_Mk0dchPi-38-w,1046
1443
+ src/tools/get_custom_fields.py,sha256=jTbPzzFDUWZNWB1TbThpq8z5OnxaW24eK2-FX0Ba_Oc,1782
1444
+ src/tools/get_test_case_custom_fields.py,sha256=vcEj5CWaBtvva-buN2WEGBNuwzgXNHj9nOsrn2jOcO4,1336
1445
+ src/tools/launches.py,sha256=2RjFuFsaKX1q3u7vpg91KOJffNMv3KkkBaNE4sH2oJ4,4299
1433
1446
  src/tools/link_shared_step.py,sha256=m6jKudXeBnS4fC47ULVjuwsBisDyPVgJHuNFjDuRrE8,3734
1434
- src/tools/search.py,sha256=XnMQjBBpqIDhNz2YxiJroAnqSV48JJmvsNlA7USb-2g,13754
1447
+ src/tools/list_test_layer_schemas.py,sha256=ju5fjXMXGho9jJ8TRXYooFHMzJDThhyFso0z8OZPgas,1580
1448
+ src/tools/list_test_layers.py,sha256=nH_VIJHt5kxfmD5z6_N4UER3anvNY3D-ukPo3HL15hk,1202
1449
+ src/tools/search.py,sha256=MxFQTdyAoHtjSIZVZYRFhmh2KDnDQNnwdaV-IBZW0Vc,13904
1435
1450
  src/tools/shared_steps.py,sha256=VYNEC4TBhZyTp_vZypOtsHjvXpwtf_wUx0C9bCY86p8,7193
1451
+ src/tools/test_layers.py,sha256=lnvL-wKkexQ5sldC_3P-fWOZXCQDPBbDB03Cu1VudJ0,793
1436
1452
  src/tools/unlink_shared_step.py,sha256=Zrr8UgFzpIs76kiHAQGt3JMXIRY943Yh323FNYH05Eg,2554
1437
- src/tools/update_test_case.py,sha256=y3wc5PexfjFEshZHmGQ2MeqhvxopE7G_YES2Vq2IPAA,5349
1438
- src/utils/__init__.py,sha256=glRWiaIBWJlS2bST5-OId63xVmA13P_wp6YcQ1B83ws,38
1453
+ src/tools/update_test_case.py,sha256=8B074dwpVx2cna7XpuAA3vGBi8A06lHYtHy3hlc7y7g,7209
1454
+ src/tools/update_test_layer.py,sha256=LrYNOhv3EQzZF7UjNMLoAKiG1HR5eXw1URpzPRntwcY,1185
1455
+ src/tools/update_test_layer_schema.py,sha256=GAda2drLbecRatU-9r8IuDasEE8lJe1GL1M8bcbfOz0,1559
1456
+ src/utils/__init__.py,sha256=puuJIVHt6RMo6xvMBsvGydGJuW0b6v1zi6rBexmOVGM,114
1439
1457
  src/utils/auth.py,sha256=CfQws2BUct_wmPPbe_COTjHqoeKRfF0PcCs_8pkINjQ,3334
1440
1458
  src/utils/config.py,sha256=q6PUlkfgpFEzrzspbmAYcvBbFOgOIDp5RTk5XcR4IGk,1412
1441
1459
  src/utils/error.py,sha256=kUsp62qbz_Q1pz6dGblGTi0rNixJOiF-Vtp19punh7Q,6347
1460
+ src/utils/links.py,sha256=yPIqofIYbTnglOJ_4xUa-UvPB0S5blARjTClsMzv49w,538
1442
1461
  src/utils/logger.py,sha256=8ZKOitFjwBRwDfD8wt0hfwIkS-rrrw2bjX5CaxgaZ9g,2504
1443
1462
  src/utils/schema_hint.py,sha256=tQkoWTQG7rGbS-pA5Hve-wdb3Sx0869IOy9DCHU_3QM,1811
1444
- lucius_mcp-0.2.2.dist-info/METADATA,sha256=9i3YovTiDFI0niZuMZ04YdEENi7BCmSCAyWLHerGSPs,9234
1445
- lucius_mcp-0.2.2.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
1446
- lucius_mcp-0.2.2.dist-info/entry_points.txt,sha256=Y-XfajjPTM5lObHWqsMYRsLrJRsILKpFGgwBcefRZvw,41
1447
- lucius_mcp-0.2.2.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1448
- lucius_mcp-0.2.2.dist-info/RECORD,,
1463
+ lucius_mcp-0.3.0.dist-info/METADATA,sha256=Bc7FLCKMF6lRiFKP4CZ7FFH57Kph18YGzIrJuVcyTro,9402
1464
+ lucius_mcp-0.3.0.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
1465
+ lucius_mcp-0.3.0.dist-info/entry_points.txt,sha256=Y-XfajjPTM5lObHWqsMYRsLrJRsILKpFGgwBcefRZvw,41
1466
+ lucius_mcp-0.3.0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
1467
+ lucius_mcp-0.3.0.dist-info/RECORD,,
src/client/__init__.py CHANGED
@@ -4,6 +4,11 @@ from .client import (
4
4
  AllureClient,
5
5
  AttachmentStepDtoWithName,
6
6
  BodyStepDtoWithSteps,
7
+ FindAll29200Response,
8
+ LaunchCreateDto,
9
+ LaunchDto,
10
+ PageLaunchDto,
11
+ PageLaunchPreviewDto,
7
12
  PageSharedStepDto,
8
13
  PageTestCaseDto,
9
14
  ScenarioStepCreatedResponseDto,
@@ -39,6 +44,11 @@ __all__ = [
39
44
  "AllureValidationError",
40
45
  "AttachmentStepDtoWithName",
41
46
  "BodyStepDtoWithSteps",
47
+ "FindAll29200Response",
48
+ "LaunchCreateDto",
49
+ "LaunchDto",
50
+ "PageLaunchDto",
51
+ "PageLaunchPreviewDto",
42
52
  "PageSharedStepDto",
43
53
  "PageTestCaseDto",
44
54
  "ScenarioStepCreateDto",
src/client/client.py CHANGED
@@ -10,7 +10,7 @@ from collections.abc import Awaitable
10
10
  from typing import Literal, TypeVar, cast, overload
11
11
 
12
12
  import httpx
13
- from pydantic import SecretStr
13
+ from pydantic import SecretStr, ValidationError
14
14
 
15
15
  from src.client.exceptions import TestCaseNotFoundError
16
16
  from src.utils.config import settings
@@ -27,22 +27,32 @@ from .generated.api.custom_field_controller_api import CustomFieldControllerApi
27
27
  from .generated.api.custom_field_project_controller_api import CustomFieldProjectControllerApi
28
28
  from .generated.api.custom_field_project_controller_v2_api import CustomFieldProjectControllerV2Api
29
29
  from .generated.api.custom_field_value_project_controller_api import CustomFieldValueProjectControllerApi
30
+ from .generated.api.launch_controller_api import LaunchControllerApi
31
+ from .generated.api.launch_search_controller_api import LaunchSearchControllerApi
30
32
  from .generated.api.shared_step_attachment_controller_api import SharedStepAttachmentControllerApi
31
33
  from .generated.api.shared_step_controller_api import SharedStepControllerApi
32
34
  from .generated.api.shared_step_scenario_controller_api import SharedStepScenarioControllerApi
33
35
  from .generated.api.test_case_attachment_controller_api import TestCaseAttachmentControllerApi
34
36
  from .generated.api.test_case_controller_api import TestCaseControllerApi
35
- from .generated.api.test_case_custom_field_controller_api import TestCaseCustomFieldControllerApi
36
37
  from .generated.api.test_case_overview_controller_api import TestCaseOverviewControllerApi
37
38
  from .generated.api.test_case_scenario_controller_api import TestCaseScenarioControllerApi
38
39
  from .generated.api.test_case_search_controller_api import TestCaseSearchControllerApi
40
+ from .generated.api.test_layer_controller_api import TestLayerControllerApi
41
+ from .generated.api.test_layer_schema_controller_api import TestLayerSchemaControllerApi
39
42
  from .generated.api_client import ApiClient
40
43
  from .generated.configuration import Configuration
41
44
  from .generated.exceptions import ApiException
45
+ from .generated.models.aql_validate_response_dto import AqlValidateResponseDto
42
46
  from .generated.models.attachment_step_dto import AttachmentStepDto
43
47
  from .generated.models.body_step_dto import BodyStepDto
44
48
  from .generated.models.custom_field_project_with_values_dto import CustomFieldProjectWithValuesDto
45
49
  from .generated.models.custom_field_value_with_cf_dto import CustomFieldValueWithCfDto
50
+ from .generated.models.custom_field_with_values_dto import CustomFieldWithValuesDto
51
+ from .generated.models.find_all29200_response import FindAll29200Response
52
+ from .generated.models.launch_create_dto import LaunchCreateDto
53
+ from .generated.models.launch_dto import LaunchDto
54
+ from .generated.models.page_launch_dto import PageLaunchDto
55
+ from .generated.models.page_launch_preview_dto import PageLaunchPreviewDto
46
56
  from .generated.models.page_shared_step_dto import PageSharedStepDto
47
57
  from .generated.models.page_test_case_dto import PageTestCaseDto
48
58
  from .generated.models.scenario_step_create_dto import ScenarioStepCreateDto
@@ -62,6 +72,7 @@ from .generated.models.test_case_row_dto import TestCaseRowDto
62
72
  from .generated.models.test_case_scenario_dto import TestCaseScenarioDto
63
73
  from .generated.models.test_case_scenario_v2_dto import TestCaseScenarioV2Dto
64
74
  from .generated.models.test_case_tree_selection_dto import TestCaseTreeSelectionDto
75
+ from .overridden.test_case_custom_fields_v2 import TestCaseCustomFieldV2ControllerApi
65
76
 
66
77
 
67
78
  # Subclasses to add missing fields to generated models
@@ -104,11 +115,15 @@ type ApiType = (
104
115
  | SharedStepScenarioControllerApi
105
116
  | TestCaseOverviewControllerApi
106
117
  | TestCaseSearchControllerApi
107
- | TestCaseCustomFieldControllerApi
118
+ | TestCaseCustomFieldV2ControllerApi
108
119
  | CustomFieldControllerApi
109
120
  | CustomFieldProjectControllerApi
110
121
  | CustomFieldProjectControllerV2Api
111
122
  | CustomFieldValueProjectControllerApi
123
+ | TestLayerControllerApi
124
+ | TestLayerSchemaControllerApi
125
+ | LaunchControllerApi
126
+ | LaunchSearchControllerApi
112
127
  )
113
128
 
114
129
  type NormalizedScenarioDict = dict[str, object]
@@ -123,6 +138,12 @@ __all__ = [
123
138
  "AttachmentStepDtoWithName",
124
139
  "BodyStepDtoWithSteps",
125
140
  "CustomFieldProjectWithValuesDto",
141
+ "CustomFieldWithValuesDto",
142
+ "FindAll29200Response",
143
+ "LaunchCreateDto",
144
+ "LaunchDto",
145
+ "PageLaunchDto",
146
+ "PageLaunchPreviewDto",
126
147
  "PageSharedStepDto",
127
148
  "PageTestCaseDto",
128
149
  "ScenarioStepCreateDto",
@@ -204,11 +225,15 @@ class AllureClient:
204
225
  self._shared_step_scenario_api: SharedStepScenarioControllerApi | None = None
205
226
  self._overview_api: TestCaseOverviewControllerApi
206
227
  self._search_api: TestCaseSearchControllerApi | None = None
207
- self._test_case_custom_field_api: TestCaseCustomFieldControllerApi | None = None
228
+ self._test_case_custom_field_api: TestCaseCustomFieldV2ControllerApi | None = None
208
229
  self._custom_field_api: CustomFieldControllerApi | None = None
209
230
  self._custom_field_project_api: CustomFieldProjectControllerApi | None = None
210
231
  self._custom_field_project_v2_api: CustomFieldProjectControllerV2Api | None = None
211
232
  self._custom_field_value_project_api: CustomFieldValueProjectControllerApi | None = None
233
+ self._test_layer_api: TestLayerControllerApi | None = None
234
+ self._test_layer_schema_api: TestLayerSchemaControllerApi | None = None
235
+ self._launch_api: LaunchControllerApi | None = None
236
+ self._launch_search_api: LaunchSearchControllerApi | None = None
212
237
  self._is_entered = False
213
238
 
214
239
  @classmethod
@@ -239,7 +264,7 @@ class AllureClient:
239
264
  if not isinstance(settings.ALLURE_PROJECT_ID, int) or settings.ALLURE_PROJECT_ID <= 0:
240
265
  raise ValueError("ALLURE_PROJECT_ID must be a positive integer")
241
266
 
242
- if project:
267
+ if project is not None:
243
268
  p = project
244
269
  else:
245
270
  p = settings.ALLURE_PROJECT_ID
@@ -345,11 +370,15 @@ class AllureClient:
345
370
  self._shared_step_scenario_api = SharedStepScenarioControllerApi(self._api_client)
346
371
  self._overview_api = TestCaseOverviewControllerApi(self._api_client)
347
372
  self._search_api = TestCaseSearchControllerApi(self._api_client)
348
- self._test_case_custom_field_api = TestCaseCustomFieldControllerApi(self._api_client)
373
+ self._test_case_custom_field_api = TestCaseCustomFieldV2ControllerApi(self._api_client)
349
374
  self._custom_field_api = CustomFieldControllerApi(self._api_client)
350
375
  self._custom_field_project_api = CustomFieldProjectControllerApi(self._api_client)
351
376
  self._custom_field_project_v2_api = CustomFieldProjectControllerV2Api(self._api_client)
352
377
  self._custom_field_value_project_api = CustomFieldValueProjectControllerApi(self._api_client)
378
+ self._test_layer_api = TestLayerControllerApi(self._api_client)
379
+ self._test_layer_schema_api = TestLayerSchemaControllerApi(self._api_client)
380
+ self._launch_api = LaunchControllerApi(self._api_client)
381
+ self._launch_search_api = LaunchSearchControllerApi(self._api_client)
353
382
 
354
383
  @property
355
384
  def api_client(self) -> ApiClient:
@@ -466,7 +495,7 @@ class AllureClient:
466
495
  @overload
467
496
  async def _get_api(
468
497
  self, attr_name: Literal["_test_case_custom_field_api"], *, error_name: str | None = None
469
- ) -> TestCaseCustomFieldControllerApi: ...
498
+ ) -> TestCaseCustomFieldV2ControllerApi: ...
470
499
 
471
500
  @overload
472
501
  async def _get_api(
@@ -488,6 +517,16 @@ class AllureClient:
488
517
  self, attr_name: Literal["_custom_field_value_project_api"], *, error_name: str | None = None
489
518
  ) -> CustomFieldValueProjectControllerApi: ...
490
519
 
520
+ @overload
521
+ async def _get_api(
522
+ self, attr_name: Literal["_launch_api"], *, error_name: str | None = None
523
+ ) -> LaunchControllerApi: ...
524
+
525
+ @overload
526
+ async def _get_api(
527
+ self, attr_name: Literal["_launch_search_api"], *, error_name: str | None = None
528
+ ) -> LaunchSearchControllerApi: ...
529
+
491
530
  async def _get_api(self, attr_name: str, *, error_name: str | None = None) -> ApiType:
492
531
  self._require_entered()
493
532
  await self._ensure_valid_token()
@@ -693,6 +732,188 @@ class AllureClient:
693
732
  )
694
733
  )
695
734
 
735
+ # ==========================================
736
+ # Launch operations
737
+ # ==========================================
738
+
739
+ async def create_launch(self, data: LaunchCreateDto) -> LaunchDto:
740
+ """Create a new launch in the specified project.
741
+
742
+ Args:
743
+ data: Launch definition (name, project_id, etc.).
744
+
745
+ Returns:
746
+ The created launch.
747
+
748
+ Raises:
749
+ AllureNotFoundError: If project doesn't exist.
750
+ AllureValidationError: If input data fails validation.
751
+ AllureAuthError: If unauthorized.
752
+ AllureAPIError: If the server returns an error.
753
+ """
754
+ api = await self._get_api("_launch_api")
755
+
756
+ if hasattr(data, "project_id") and not data.project_id:
757
+ data.project_id = self._project
758
+
759
+ return await self._call_api(api.create31(launch_create_dto=data, _request_timeout=self._timeout))
760
+
761
+ async def list_launches(
762
+ self,
763
+ project_id: int,
764
+ page: int = 0,
765
+ size: int = 20,
766
+ search: str | None = None,
767
+ filter_id: int | None = None,
768
+ sort: list[str] | None = None,
769
+ ) -> FindAll29200Response:
770
+ """List launches for a project.
771
+
772
+ Args:
773
+ project_id: Target project ID.
774
+ page: Zero-based page index.
775
+ size: Page size.
776
+ search: Optional name search.
777
+ filter_id: Optional filter ID.
778
+ sort: Optional sort criteria.
779
+
780
+ Returns:
781
+ Paginated launches or launch previews.
782
+
783
+ Raises:
784
+ AllureNotFoundError: If project doesn't exist.
785
+ AllureValidationError: If input data fails validation.
786
+ AllureAuthError: If unauthorized.
787
+ AllureAPIError: If the server returns an error.
788
+ """
789
+ api = await self._get_api("_launch_api", error_name="launch APIs")
790
+
791
+ if not isinstance(project_id, int) or project_id <= 0:
792
+ raise AllureValidationError("Project ID must be a positive integer")
793
+ if not isinstance(page, int) or page < 0:
794
+ raise AllureValidationError("Page must be a non-negative integer")
795
+ if not isinstance(size, int) or size <= 0 or size > 100:
796
+ raise AllureValidationError("Size must be between 1 and 100")
797
+
798
+ try:
799
+ return await self._call_api(
800
+ api.find_all29(
801
+ project_id=project_id,
802
+ search=search,
803
+ filter_id=filter_id,
804
+ page=page,
805
+ size=size,
806
+ sort=sort,
807
+ _request_timeout=self._timeout,
808
+ )
809
+ )
810
+ except ValueError:
811
+ response = await self._call_api_raw(
812
+ api.find_all29_without_preload_content(
813
+ project_id=project_id,
814
+ search=search,
815
+ filter_id=filter_id,
816
+ page=page,
817
+ size=size,
818
+ sort=sort,
819
+ _request_timeout=self._timeout,
820
+ )
821
+ )
822
+ data = self._extract_response_data(response)
823
+ try:
824
+ page_data = PageLaunchDto.from_dict(data)
825
+ if page_data is None:
826
+ raise AllureValidationError("Unexpected launch list response from API")
827
+ return FindAll29200Response(page_data)
828
+ except ValidationError as e:
829
+ preview_data = PageLaunchPreviewDto.from_dict(data)
830
+ if preview_data is None:
831
+ raise AllureValidationError("Unexpected launch list response from API") from e
832
+ return FindAll29200Response(preview_data)
833
+
834
+ async def search_launches_aql(
835
+ self,
836
+ project_id: int,
837
+ rql: str,
838
+ page: int = 0,
839
+ size: int = 20,
840
+ sort: list[str] | None = None,
841
+ ) -> PageLaunchDto:
842
+ """Search launches using raw AQL (Allure Query Language).
843
+
844
+ Args:
845
+ project_id: Target project ID.
846
+ rql: Raw AQL query string.
847
+ page: Zero-based page index.
848
+ size: Page size (max 100).
849
+ sort: Optional sort criteria (e.g., ["createdDate,DESC"]).
850
+
851
+ Returns:
852
+ Paginated launch results matching the AQL query.
853
+
854
+ Raises:
855
+ AllureValidationError: If AQL syntax is invalid or input fails validation.
856
+ AllureNotFoundError: If project doesn't exist.
857
+ AllureAuthError: If unauthorized.
858
+ AllureAPIError: If the server returns an error.
859
+ """
860
+ api = await self._get_api("_launch_search_api", error_name="launch search APIs")
861
+
862
+ if not isinstance(project_id, int) or project_id <= 0:
863
+ raise AllureValidationError("Project ID must be a positive integer")
864
+ if not isinstance(rql, str) or not rql.strip():
865
+ raise AllureValidationError("AQL query must be a non-empty string")
866
+ if not isinstance(page, int) or page < 0:
867
+ raise AllureValidationError("Page must be a non-negative integer")
868
+ if not isinstance(size, int) or size <= 0 or size > 100:
869
+ raise AllureValidationError("Size must be between 1 and 100")
870
+
871
+ return await self._call_api(
872
+ api.search2(
873
+ project_id=project_id,
874
+ rql=rql,
875
+ page=page,
876
+ size=size,
877
+ sort=sort,
878
+ _request_timeout=self._timeout,
879
+ )
880
+ )
881
+
882
+ async def validate_launch_query(
883
+ self,
884
+ project_id: int,
885
+ rql: str,
886
+ ) -> AqlValidateResponseDto:
887
+ """Validate an AQL query for launches without executing it.
888
+
889
+ Args:
890
+ project_id: Target project ID.
891
+ rql: Raw AQL query string to validate.
892
+
893
+ Returns:
894
+ Validation response with validity and count.
895
+
896
+ Raises:
897
+ AllureValidationError: If input fails basic validation.
898
+ AllureNotFoundError: If project doesn't exist.
899
+ AllureAuthError: If unauthorized.
900
+ AllureAPIError: If the server returns an error.
901
+ """
902
+ api = await self._get_api("_launch_search_api", error_name="launch search APIs")
903
+
904
+ if not isinstance(project_id, int) or project_id <= 0:
905
+ raise AllureValidationError("Project ID must be a positive integer")
906
+ if not isinstance(rql, str) or not rql.strip():
907
+ raise AllureValidationError("AQL query must be a non-empty string")
908
+
909
+ return await self._call_api(
910
+ api.validate_query2(
911
+ project_id=project_id,
912
+ rql=rql,
913
+ _request_timeout=self._timeout,
914
+ )
915
+ )
916
+
696
917
  async def search_test_cases_aql(
697
918
  self,
698
919
  project_id: int,
@@ -923,6 +1144,49 @@ class AllureClient:
923
1144
 
924
1145
  return results
925
1146
 
1147
+ async def get_test_case_custom_fields(
1148
+ self,
1149
+ test_case_id: int,
1150
+ project_id: int,
1151
+ ) -> list[CustomFieldProjectWithValuesDto]:
1152
+ """Fetch custom fields with values for a specific test case.
1153
+
1154
+ Args:
1155
+ test_case_id: Target test case ID.
1156
+ project_id: The project ID context.
1157
+
1158
+ Returns:
1159
+ List of custom field DTOs with their assigned values.
1160
+ """
1161
+ api = await self._get_api("_test_case_custom_field_api")
1162
+ return await self._call_api(
1163
+ api.get_custom_fields_with_values3(
1164
+ test_case_id=test_case_id,
1165
+ project_id=project_id,
1166
+ _request_timeout=self._timeout,
1167
+ )
1168
+ )
1169
+
1170
+ async def update_cfvs_of_test_case(
1171
+ self,
1172
+ test_case_id: int,
1173
+ custom_fields: list[CustomFieldValueWithCfDto],
1174
+ ) -> None:
1175
+ """Update custom field values for a test case.
1176
+
1177
+ Args:
1178
+ test_case_id: Target test case ID.
1179
+ custom_fields: List of custom field DTOs with new values.
1180
+ """
1181
+ api = await self._get_api("_test_case_custom_field_api")
1182
+ await self._call_api(
1183
+ api.update_cfvs_of_test_case(
1184
+ test_case_id=test_case_id,
1185
+ custom_field_with_values_dto=custom_fields,
1186
+ _request_timeout=self._timeout,
1187
+ )
1188
+ )
1189
+
926
1190
  async def delete_scenario_step(self, step_id: int) -> None:
927
1191
  """Delete a scenario step.
928
1192
 
@@ -1041,7 +1305,6 @@ class AllureClient:
1041
1305
  )
1042
1306
  )
1043
1307
  raw_data = self._extract_response_data(response)
1044
- # print(f"DEBUG Initial Raw Normalized Scenario: {raw_data}")
1045
1308
  return self._denormalize_to_v2_from_dict(raw_data)
1046
1309
 
1047
1310
  @staticmethod
@@ -1306,3 +1569,21 @@ class AllureClient:
1306
1569
  shared_step_api = await self._get_api("_shared_step_api")
1307
1570
  # Soft delete via archive
1308
1571
  await self._call_api(shared_step_api.archive(id=shared_step_id, _request_timeout=self._timeout))
1572
+
1573
+ async def update_test_case_custom_fields(
1574
+ self, test_case_id: int, custom_fields: list[CustomFieldValueWithCfDto]
1575
+ ) -> None:
1576
+ """Update custom field values of a test case using dedicated endpoint.
1577
+
1578
+ Args:
1579
+ test_case_id: Target test case ID.
1580
+ custom_fields: List of custom fields with values to set/clear.
1581
+ """
1582
+ api = await self._get_api("_test_case_custom_field_api")
1583
+ await self._call_api(
1584
+ api.update_cfvs_of_test_case(
1585
+ test_case_id=test_case_id,
1586
+ custom_field_with_values_dto=custom_fields,
1587
+ _request_timeout=self._timeout,
1588
+ )
1589
+ )
@@ -234,6 +234,17 @@ Class | Method | HTTP request | Description
234
234
  *TestCaseSearchControllerApi* | [**validate_query1**](src/client/generated/docs/TestCaseSearchControllerApi.md#validate_query1) | **GET** /api/testcase/query/validate | Find all test cases by given AQL
235
235
  *TestCaseTagControllerApi* | [**get_tags**](src/client/generated/docs/TestCaseTagControllerApi.md#get_tags) | **GET** /api/testcase/{testCaseId}/tag | Find tags for test case
236
236
  *TestCaseTagControllerApi* | [**set_tags**](src/client/generated/docs/TestCaseTagControllerApi.md#set_tags) | **POST** /api/testcase/{testCaseId}/tag | Set test tags for test case
237
+ *TestLayerControllerApi* | [**create9**](src/client/generated/docs/TestLayerControllerApi.md#create9) | **POST** /api/testlayer |
238
+ *TestLayerControllerApi* | [**delete9**](src/client/generated/docs/TestLayerControllerApi.md#delete9) | **DELETE** /api/testlayer/{id} |
239
+ *TestLayerControllerApi* | [**find_all7**](src/client/generated/docs/TestLayerControllerApi.md#find_all7) | **GET** /api/testlayer |
240
+ *TestLayerControllerApi* | [**find_one8**](src/client/generated/docs/TestLayerControllerApi.md#find_one8) | **GET** /api/testlayer/{id} |
241
+ *TestLayerControllerApi* | [**patch9**](src/client/generated/docs/TestLayerControllerApi.md#patch9) | **PATCH** /api/testlayer/{id} |
242
+ *TestLayerControllerApi* | [**suggest4**](src/client/generated/docs/TestLayerControllerApi.md#suggest4) | **GET** /api/testlayer/suggest |
243
+ *TestLayerSchemaControllerApi* | [**create8**](src/client/generated/docs/TestLayerSchemaControllerApi.md#create8) | **POST** /api/testlayerschema | Create a new test layer schema
244
+ *TestLayerSchemaControllerApi* | [**delete8**](src/client/generated/docs/TestLayerSchemaControllerApi.md#delete8) | **DELETE** /api/testlayerschema/{id} | Delete test layer schema by id
245
+ *TestLayerSchemaControllerApi* | [**find_all6**](src/client/generated/docs/TestLayerSchemaControllerApi.md#find_all6) | **GET** /api/testlayerschema | Find all test layer schemas for given project
246
+ *TestLayerSchemaControllerApi* | [**find_one7**](src/client/generated/docs/TestLayerSchemaControllerApi.md#find_one7) | **GET** /api/testlayerschema/{id} | Find test layer schema by id
247
+ *TestLayerSchemaControllerApi* | [**patch8**](src/client/generated/docs/TestLayerSchemaControllerApi.md#patch8) | **PATCH** /api/testlayerschema/{id} | Patch test layer schema
237
248
 
238
249
 
239
250
  ## Documentation For Models
@@ -39,6 +39,8 @@ __all__ = [
39
39
  "TestCaseScenarioControllerApi",
40
40
  "TestCaseSearchControllerApi",
41
41
  "TestCaseTagControllerApi",
42
+ "TestLayerControllerApi",
43
+ "TestLayerSchemaControllerApi",
42
44
  "ApiResponse",
43
45
  "ApiClient",
44
46
  "Configuration",
@@ -755,6 +757,8 @@ from src.client.generated.api.test_case_overview_controller_api import TestCaseO
755
757
  from src.client.generated.api.test_case_scenario_controller_api import TestCaseScenarioControllerApi as TestCaseScenarioControllerApi
756
758
  from src.client.generated.api.test_case_search_controller_api import TestCaseSearchControllerApi as TestCaseSearchControllerApi
757
759
  from src.client.generated.api.test_case_tag_controller_api import TestCaseTagControllerApi as TestCaseTagControllerApi
760
+ from src.client.generated.api.test_layer_controller_api import TestLayerControllerApi as TestLayerControllerApi
761
+ from src.client.generated.api.test_layer_schema_controller_api import TestLayerSchemaControllerApi as TestLayerSchemaControllerApi
758
762
 
759
763
  # import ApiClient
760
764
  from src.client.generated.api_response import ApiResponse as ApiResponse
@@ -22,4 +22,6 @@ from src.client.generated.api.test_case_overview_controller_api import TestCaseO
22
22
  from src.client.generated.api.test_case_scenario_controller_api import TestCaseScenarioControllerApi
23
23
  from src.client.generated.api.test_case_search_controller_api import TestCaseSearchControllerApi
24
24
  from src.client.generated.api.test_case_tag_controller_api import TestCaseTagControllerApi
25
+ from src.client.generated.api.test_layer_controller_api import TestLayerControllerApi
26
+ from src.client.generated.api.test_layer_schema_controller_api import TestLayerSchemaControllerApi
25
27