zrb 1.0.0b9__py3-none-any.whl → 1.0.0b10__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 (61) hide show
  1. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
  2. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
  3. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +4 -4
  4. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
  5. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +107 -1
  6. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +67 -4
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_create_my_entity.py +53 -0
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_delete_my_entity.py +62 -0
  9. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_read_my_entity.py +65 -0
  10. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/app_template/test/my_module/my_entity/test_update_my_entity.py +61 -0
  11. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +57 -13
  12. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +2 -2
  13. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +6 -1
  14. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
  15. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
  16. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
  17. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
  18. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +1 -1
  19. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +1 -0
  20. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
  21. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
  22. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
  23. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
  24. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +193 -43
  25. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +57 -0
  26. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
  27. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
  28. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
  29. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
  30. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
  31. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
  32. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
  33. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
  34. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
  35. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
  36. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +17 -0
  37. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
  38. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
  39. zrb/task/base_task.py +10 -10
  40. zrb/util/codemod/modification_mode.py +3 -0
  41. zrb/util/codemod/modify_class.py +58 -0
  42. zrb/util/codemod/modify_class_parent.py +68 -0
  43. zrb/util/codemod/modify_class_property.py +128 -0
  44. zrb/util/codemod/modify_dict.py +75 -0
  45. zrb/util/codemod/modify_function.py +65 -0
  46. zrb/util/codemod/modify_function_call.py +68 -0
  47. zrb/util/codemod/modify_method.py +88 -0
  48. zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
  49. zrb/util/file.py +3 -2
  50. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/METADATA +1 -1
  51. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/RECORD +53 -36
  52. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
  53. zrb/util/codemod/append_code_to_class.py +0 -35
  54. zrb/util/codemod/append_code_to_function.py +0 -38
  55. zrb/util/codemod/append_code_to_method.py +0 -55
  56. zrb/util/codemod/append_key_to_dict.py +0 -51
  57. zrb/util/codemod/append_param_to_function_call.py +0 -39
  58. zrb/util/codemod/prepend_parent_to_class.py +0 -38
  59. zrb/util/codemod/prepend_property_to_class.py +0 -55
  60. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/WHEEL +0 -0
  61. {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,19 @@
1
+ import time
2
+
3
+ from fastapi.testclient import TestClient
4
+ from my_app_name.config import APP_AUTH_SUPER_USER, APP_AUTH_SUPER_USER_PASSWORD
5
+ from my_app_name.main import app
6
+
7
+
8
+ def get_admin_access_token():
9
+ client = TestClient(app, base_url="http://localhost")
10
+ # Create new admin user session and check the response
11
+ session_response = client.post(
12
+ "/api/v1/user-sessions",
13
+ data={
14
+ "username": APP_AUTH_SUPER_USER,
15
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
16
+ },
17
+ )
18
+ session_data = session_response.json()
19
+ return session_data.get("access_token")
@@ -0,0 +1,59 @@
1
+ from fastapi.testclient import TestClient
2
+ from my_app_name.main import app
3
+ from my_app_name.test._util.access_token import get_admin_access_token
4
+
5
+
6
+ def test_create_permission():
7
+ client = TestClient(app, base_url="http://localhost")
8
+ access_token = get_admin_access_token()
9
+ new_permission_data = {
10
+ "name": "new-permission",
11
+ "description": "new-permission-description",
12
+ }
13
+ response = client.post(
14
+ "/api/v1/permissions",
15
+ json=new_permission_data,
16
+ headers={"Authorization": f"Bearer {access_token}"},
17
+ )
18
+ assert response.status_code == 200
19
+ response_data = response.json()
20
+ assert response_data.get("id") is not None
21
+ assert response_data.get("id") != ""
22
+ assert response_data.get("name") == "new-permission"
23
+ assert response_data.get("description") == "new-permission-description"
24
+
25
+
26
+ def test_create_permission_bulk():
27
+ client = TestClient(app, base_url="http://localhost")
28
+ access_token = get_admin_access_token()
29
+ new_first_permission_data = {
30
+ "name": "new-permission-bulk-1",
31
+ "description": "new-permission-bulk-description-1",
32
+ }
33
+ new_second_permission_data = {
34
+ "name": "new-permission-bulk-2",
35
+ "description": "new-permission-bulk-description-2",
36
+ }
37
+ new_permission_data = [new_first_permission_data, new_second_permission_data]
38
+ response = client.post(
39
+ "/api/v1/permissions/bulk",
40
+ json=new_permission_data,
41
+ headers={"Authorization": f"Bearer {access_token}"},
42
+ )
43
+ assert response.status_code == 200
44
+ response_data = response.json()
45
+ assert len(response_data) == 2
46
+ # Id should not be empty
47
+ assert response_data[0] is not None
48
+ assert response_data[0] != ""
49
+ assert response_data[1] is not None
50
+ assert response_data[1] != ""
51
+ # Data should match
52
+ assert new_first_permission_data["name"] in [row["name"] for row in response_data]
53
+ assert new_second_permission_data["name"] in [row["name"] for row in response_data]
54
+ assert new_first_permission_data["description"] in [
55
+ row["description"] for row in response_data
56
+ ]
57
+ assert new_second_permission_data["description"] in [
58
+ row["description"] for row in response_data
59
+ ]
@@ -0,0 +1,68 @@
1
+ from fastapi.testclient import TestClient
2
+ from my_app_name.main import app
3
+ from my_app_name.test._util.access_token import get_admin_access_token
4
+
5
+
6
+ def test_delete_permission():
7
+ client = TestClient(app, base_url="http://localhost")
8
+ access_token = get_admin_access_token()
9
+ new_permission_data = {
10
+ "name": "to-be-deleted-permission",
11
+ "description": "to-be-deleted-permission-description",
12
+ }
13
+ insert_response = client.post(
14
+ "/api/v1/permissions",
15
+ json=new_permission_data,
16
+ headers={"Authorization": f"Bearer {access_token}"},
17
+ )
18
+ assert insert_response.status_code == 200
19
+ id = insert_response.json().get("id")
20
+ # deleting
21
+ response = client.delete(
22
+ f"/api/v1/permissions/{id}", headers={"Authorization": f"Bearer {access_token}"}
23
+ )
24
+ assert response.status_code == 200
25
+ response_data = response.json()
26
+ assert response_data.get("id") == id
27
+ assert response_data.get("name") == "to-be-deleted-permission"
28
+ assert response_data.get("description") == "to-be-deleted-permission-description"
29
+
30
+
31
+ def test_delete_permission_bulk():
32
+ client = TestClient(app, base_url="http://localhost")
33
+ access_token = get_admin_access_token()
34
+ new_first_permission_data = {
35
+ "name": "to-be-deleted-permission-bulk-1",
36
+ "description": "to-be-deleted-permission-bulk-description-1",
37
+ }
38
+ new_second_permission_data = {
39
+ "name": "to-be-deleted-permission-bulk-2",
40
+ "description": "to-be-deleted-permission-bulk-description-2",
41
+ }
42
+ new_permission_data = [new_first_permission_data, new_second_permission_data]
43
+ insert_response = client.post(
44
+ "/api/v1/permissions/bulk",
45
+ json=new_permission_data,
46
+ headers={"Authorization": f"Bearer {access_token}"},
47
+ )
48
+ assert insert_response.status_code == 200
49
+ ids = [row["id"] for row in insert_response.json()]
50
+ # deleting (use client.request since client.delete doesn't support json param)
51
+ response = client.request(
52
+ "DELETE",
53
+ f"/api/v1/permissions/bulk",
54
+ json=ids,
55
+ headers={"Authorization": f"Bearer {access_token}"},
56
+ )
57
+ assert response.status_code == 200
58
+ response_data = response.json()
59
+ # Data should match
60
+ assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
61
+ assert new_first_permission_data["name"] in [row["name"] for row in response_data]
62
+ assert new_second_permission_data["name"] in [row["name"] for row in response_data]
63
+ assert new_first_permission_data["description"] in [
64
+ row["description"] for row in response_data
65
+ ]
66
+ assert new_second_permission_data["description"] in [
67
+ row["description"] for row in response_data
68
+ ]
@@ -0,0 +1,71 @@
1
+ from fastapi.testclient import TestClient
2
+ from my_app_name.main import app
3
+ from my_app_name.test._util.access_token import get_admin_access_token
4
+
5
+
6
+ def test_read_permission_by_id():
7
+ client = TestClient(app, base_url="http://localhost")
8
+ access_token = get_admin_access_token()
9
+ new_permission_data = {
10
+ "name": "to-be-read-permission",
11
+ "description": "to-be-read-permission-description",
12
+ }
13
+ insert_response = client.post(
14
+ "/api/v1/permissions",
15
+ json=new_permission_data,
16
+ headers={"Authorization": f"Bearer {access_token}"},
17
+ )
18
+ assert insert_response.status_code == 200
19
+ id = insert_response.json().get("id")
20
+ # fetching
21
+ response = client.get(
22
+ f"/api/v1/permissions/{id}", headers={"Authorization": f"Bearer {access_token}"}
23
+ )
24
+ assert response.status_code == 200
25
+ response_data = response.json()
26
+ assert response_data.get("id") == id
27
+ assert response_data.get("name") == "to-be-read-permission"
28
+ assert response_data.get("description") == "to-be-read-permission-description"
29
+
30
+
31
+ def test_read_permission_bulk():
32
+ client = TestClient(app, base_url="http://localhost")
33
+ access_token = get_admin_access_token()
34
+ new_first_permission_data = {
35
+ "name": "to-be-read-permission-bulk-1",
36
+ "description": "to-be-read-permission-bulk-description-1",
37
+ }
38
+ new_second_permission_data = {
39
+ "name": "to-be-read-permission-bulk-2",
40
+ "description": "to-be-read-permission-bulk-description-2",
41
+ }
42
+ new_permission_data = [new_first_permission_data, new_second_permission_data]
43
+ insert_response = client.post(
44
+ "/api/v1/permissions/bulk",
45
+ json=new_permission_data,
46
+ headers={"Authorization": f"Bearer {access_token}"},
47
+ )
48
+ assert insert_response.status_code == 200
49
+ ids = [row["id"] for row in insert_response.json()]
50
+ # fetching
51
+ response = client.get(
52
+ f"/api/v1/permissions",
53
+ params={
54
+ "filter": "name:like:to-be-read-permission-bulk-%",
55
+ },
56
+ headers={"Authorization": f"Bearer {access_token}"},
57
+ )
58
+ assert response.status_code == 200
59
+ response_data_count = response.json()["count"]
60
+ assert response_data_count == 2
61
+ response_data = response.json()["data"]
62
+ # Data should match
63
+ assert len([row["id"] for row in response_data if row["id"] in ids]) == 2
64
+ assert new_first_permission_data["name"] in [row["name"] for row in response_data]
65
+ assert new_second_permission_data["name"] in [row["name"] for row in response_data]
66
+ assert new_first_permission_data["description"] in [
67
+ row["description"] for row in response_data
68
+ ]
69
+ assert new_second_permission_data["description"] in [
70
+ row["description"] for row in response_data
71
+ ]
@@ -0,0 +1,66 @@
1
+ from fastapi.testclient import TestClient
2
+ from my_app_name.main import app
3
+ from my_app_name.test._util.access_token import get_admin_access_token
4
+
5
+
6
+ def test_update_permission():
7
+ client = TestClient(app, base_url="http://localhost")
8
+ access_token = get_admin_access_token()
9
+ new_permission_data = {
10
+ "name": "to-be-updated-permission",
11
+ "description": "to-be-updated-permission-description",
12
+ }
13
+ insert_response = client.post(
14
+ "/api/v1/permissions",
15
+ json=new_permission_data,
16
+ headers={"Authorization": f"Bearer {access_token}"},
17
+ )
18
+ assert insert_response.status_code == 200
19
+ id = insert_response.json().get("id")
20
+ # updating
21
+ updated_permission_data = {
22
+ "name": "updated-permission",
23
+ "description": "updated-permission-description",
24
+ }
25
+ response = client.put(
26
+ f"/api/v1/permissions/{id}",
27
+ json=updated_permission_data,
28
+ headers={"Authorization": f"Bearer {access_token}"},
29
+ )
30
+ assert response.status_code == 200
31
+ response_data = response.json()
32
+ assert response_data.get("id") == id
33
+ assert response_data.get("name") == "updated-permission"
34
+ assert response_data.get("description") == "updated-permission-description"
35
+
36
+
37
+ def test_update_permission_bulk():
38
+ client = TestClient(app, base_url="http://localhost")
39
+ access_token = get_admin_access_token()
40
+ new_first_permission_data = {
41
+ "name": "to-be-updated-permission-bulk-1",
42
+ "description": "to-be-updated-permission-bulk-description-1",
43
+ }
44
+ insert_response = client.post(
45
+ "/api/v1/permissions/bulk",
46
+ json=[new_first_permission_data],
47
+ headers={"Authorization": f"Bearer {access_token}"},
48
+ )
49
+ assert insert_response.status_code == 200
50
+ ids = [row["id"] for row in insert_response.json()]
51
+ # updating (we only test with one data)
52
+ updated_permission_data = {"description": "updated-permission-description"}
53
+ response = client.put(
54
+ f"/api/v1/permissions/bulk",
55
+ json={
56
+ "permission_ids": ids,
57
+ "data": updated_permission_data,
58
+ },
59
+ headers={"Authorization": f"Bearer {access_token}"},
60
+ )
61
+ assert response.status_code == 200
62
+ response_data = response.json()
63
+ assert len(response_data) == 1
64
+ response_data[0].get("id") == ids[0]
65
+ response_data[0].get("name") == new_first_permission_data["name"]
66
+ response_data[0].get("description") == new_first_permission_data["description"]
@@ -0,0 +1,195 @@
1
+ import time
2
+ from typing import Annotated
3
+
4
+ from fastapi import Depends
5
+ from fastapi.testclient import TestClient
6
+ from my_app_name.config import (
7
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME,
8
+ APP_AUTH_GUEST_USER,
9
+ APP_AUTH_GUEST_USER_PERMISSIONS,
10
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME,
11
+ APP_AUTH_SUPER_USER,
12
+ APP_AUTH_SUPER_USER_PASSWORD,
13
+ )
14
+ from my_app_name.main import app
15
+ from my_app_name.module.gateway.util.auth import get_current_user
16
+ from my_app_name.schema.user import AuthUserResponse
17
+
18
+
19
+ @app.get("/test/current-user", response_model=AuthUserResponse)
20
+ async def serve_get_current_user(
21
+ current_user: Annotated[AuthUserResponse, Depends(get_current_user)],
22
+ ) -> AuthUserResponse:
23
+ return current_user
24
+
25
+
26
+ def test_create_invalid_user_session():
27
+ client = TestClient(app, base_url="http://localhost")
28
+ response = client.post(
29
+ "/api/v1/user-sessions",
30
+ data={
31
+ "username": "nonExistingUserFromSevenKingdom",
32
+ "password": "nonExistingSecurity",
33
+ },
34
+ )
35
+ assert response.status_code == 401
36
+
37
+
38
+ def test_get_guest_user():
39
+ client = TestClient(app, base_url="http://localhost")
40
+ # Fetch current user
41
+ response = client.get("/test/current-user")
42
+ assert response.status_code == 200
43
+ user_data = response.json()
44
+ assert user_data.get("username") == APP_AUTH_GUEST_USER
45
+ assert not user_data.get("is_super_user")
46
+ assert user_data.get("is_guest")
47
+ assert user_data.get("permission_names") == APP_AUTH_GUEST_USER_PERMISSIONS
48
+
49
+
50
+ def test_create_admin_user_session():
51
+ client = TestClient(app, base_url="http://localhost")
52
+ # Create new admin user session and check the response
53
+ session_response = client.post(
54
+ "/api/v1/user-sessions",
55
+ data={
56
+ "username": APP_AUTH_SUPER_USER,
57
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
58
+ },
59
+ )
60
+ assert session_response.status_code == 200
61
+ session_data = session_response.json()
62
+ assert session_data.get("user_id") == APP_AUTH_SUPER_USER
63
+ assert session_data.get("token_type") == "bearer"
64
+ assert "access_token" in session_data
65
+ assert "access_token_expired_at" in session_data
66
+ assert "refresh_token" in session_data
67
+ assert "refresh_token_expired_at" in session_data
68
+ assert session_response.cookies.get(
69
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
70
+ ) == session_data.get("access_token")
71
+ assert session_response.cookies.get(
72
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
73
+ ) == session_data.get("refresh_token")
74
+
75
+
76
+ def test_get_admin_user_with_bearer():
77
+ client = TestClient(app, base_url="http://localhost")
78
+ session_response = client.post(
79
+ "/api/v1/user-sessions",
80
+ data={
81
+ "username": APP_AUTH_SUPER_USER,
82
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
83
+ },
84
+ )
85
+ assert session_response.status_code == 200
86
+ access_token = session_response.json()["access_token"]
87
+ # Fetch current user with bearer token
88
+ response = client.get(
89
+ "/test/current-user", headers={"Authorization": f"Bearer {access_token}"}
90
+ )
91
+ assert response.status_code == 200
92
+ user_data = response.json()
93
+ assert user_data.get("username") == APP_AUTH_SUPER_USER
94
+ assert user_data.get("is_super_user")
95
+ assert not user_data.get("is_guest")
96
+
97
+
98
+ def test_get_admin_user_with_cookie():
99
+ login_client = TestClient(app, base_url="http://localhost")
100
+ session_response = login_client.post(
101
+ "/api/v1/user-sessions",
102
+ data={
103
+ "username": APP_AUTH_SUPER_USER,
104
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
105
+ },
106
+ )
107
+ assert session_response.status_code == 200
108
+ # Fetch current user with cookies
109
+ cookies = {
110
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME: session_response.cookies.get(
111
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
112
+ )
113
+ }
114
+ # re-initiate client with cookies
115
+ client = TestClient(app, base_url="http://localhost", cookies=cookies)
116
+ response = client.get("/test/current-user")
117
+ assert response.status_code == 200
118
+ user_data = response.json()
119
+ assert user_data.get("username") == APP_AUTH_SUPER_USER
120
+ assert user_data.get("is_super_user")
121
+ assert not user_data.get("is_guest")
122
+
123
+
124
+ def test_update_user_session():
125
+ login_client = TestClient(app, base_url="http://localhost")
126
+ old_session_response = login_client.post(
127
+ "/api/v1/user-sessions",
128
+ data={
129
+ "username": APP_AUTH_SUPER_USER,
130
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
131
+ },
132
+ )
133
+ assert old_session_response.status_code == 200
134
+ old_session_data = old_session_response.json()
135
+ # re-initiate client with cookies and delete user session
136
+ client = TestClient(app, base_url="http://localhost")
137
+ time.sleep(1)
138
+ new_session_response = client.put(
139
+ "/api/v1/user-sessions",
140
+ params={"refresh_token": old_session_data.get("refresh_token")},
141
+ )
142
+ assert new_session_response.status_code == 200
143
+ new_session_data = new_session_response.json()
144
+ # Cookies and response should match
145
+ assert new_session_response.cookies.get(
146
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
147
+ ) == new_session_data.get("access_token")
148
+ assert new_session_response.cookies.get(
149
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
150
+ ) == new_session_data.get("refresh_token")
151
+ # New session should has longer TTL than old session
152
+ assert old_session_data.get("access_token") != new_session_data.get("access_token")
153
+ assert old_session_data.get("access_token_expired_at") < new_session_data.get(
154
+ "access_token_expired_at"
155
+ )
156
+ assert old_session_data.get("refresh_token") != new_session_data.get(
157
+ "refresh_token"
158
+ )
159
+ assert old_session_data.get("refresh_token_expired_at") < new_session_data.get(
160
+ "refresh_token_expired_at"
161
+ )
162
+
163
+
164
+ def test_delete_user_session():
165
+ login_client = TestClient(app, base_url="http://localhost")
166
+ session_response = login_client.post(
167
+ "/api/v1/user-sessions",
168
+ data={
169
+ "username": APP_AUTH_SUPER_USER,
170
+ "password": APP_AUTH_SUPER_USER_PASSWORD,
171
+ },
172
+ )
173
+ assert session_response.status_code == 200
174
+ # Initiate cookies that should be deleted when user session is deleted.
175
+ cookies = {
176
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME: session_response.cookies.get(
177
+ APP_AUTH_ACCESS_TOKEN_COOKIE_NAME
178
+ ),
179
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME: session_response.cookies.get(
180
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
181
+ ),
182
+ }
183
+ # re-initiate client with cookies and delete user session
184
+ client = TestClient(app, base_url="http://localhost", cookies=cookies)
185
+ response = client.delete(
186
+ "/api/v1/user-sessions",
187
+ params={
188
+ "refresh_token": session_response.cookies.get(
189
+ APP_AUTH_REFRESH_TOKEN_COOKIE_NAME
190
+ ),
191
+ },
192
+ )
193
+ assert response.status_code == 200
194
+ assert response.cookies.get(APP_AUTH_ACCESS_TOKEN_COOKIE_NAME, None) is None
195
+ assert response.cookies.get(APP_AUTH_REFRESH_TOKEN_COOKIE_NAME, None) is None
@@ -0,0 +1,28 @@
1
+ from fastapi.testclient import TestClient
2
+ from my_app_name.main import app
3
+
4
+
5
+ def test_get_health():
6
+ client = TestClient(app, base_url="http://localhost")
7
+ response = client.get("/health")
8
+ assert response.status_code == 200
9
+ assert response.json() == {"message": "ok"}
10
+
11
+
12
+ def test_head_health():
13
+ client = TestClient(app, base_url="http://localhost")
14
+ response = client.head("/health")
15
+ assert response.status_code == 200
16
+
17
+
18
+ def test_get_readiness():
19
+ client = TestClient(app, base_url="http://localhost")
20
+ response = client.get("/readiness")
21
+ assert response.status_code == 200
22
+ assert response.json() == {"message": "ok"}
23
+
24
+
25
+ def test_head_readiness():
26
+ client = TestClient(app, base_url="http://localhost")
27
+ response = client.head("/readiness")
28
+ assert response.status_code == 200
@@ -0,0 +1,17 @@
1
+ import os
2
+
3
+ from fastapi.testclient import TestClient
4
+ from my_app_name.main import app
5
+ from my_app_name.module.gateway.util.view import render
6
+
7
+
8
+ def test_homepage():
9
+ client = TestClient(app, base_url="http://localhost")
10
+ response = client.get("/")
11
+ assert response.status_code == 200
12
+ view_path = os.path.join(
13
+ os.path.dirname(os.path.dirname(__file__)), "module", "gateway", "view"
14
+ )
15
+ assert response.text == render(
16
+ os.path.join(view_path, "content", "homepage.html")
17
+ ).body.decode("utf-8")
@@ -0,0 +1,16 @@
1
+ import os
2
+
3
+ from fastapi.testclient import TestClient
4
+ from my_app_name.main import app
5
+ from my_app_name.module.gateway.util.view import render_error
6
+
7
+
8
+ def test_not_found_error():
9
+ client = TestClient(app, base_url="http://localhost")
10
+ response = client.get(
11
+ "/holly-grail/not-found/philosopher-stone/not-found/hope/inexist"
12
+ )
13
+ assert response.status_code == 404
14
+ assert response.text == render_error(
15
+ error_message="Not found", status_code=404
16
+ ).body.decode("utf-8")
@@ -0,0 +1,7 @@
1
+ pytest -vv \
2
+ --cov=my_app_name \
3
+ --cov-config=.coveragerc \
4
+ --cov-report=html \
5
+ --cov-report=term \
6
+ --cov-report=term-missing \
7
+ --ignore=_zrb
zrb/task/base_task.py CHANGED
@@ -295,16 +295,16 @@ class BaseTask(AnyTask):
295
295
  async def exec_root_tasks(self, session: AnySession):
296
296
  session.set_main_task(self)
297
297
  session.state_logger.write(session.as_state_log())
298
- log_state = asyncio.create_task(self._log_session_state(session))
299
- root_tasks = [
300
- task
301
- for task in session.get_root_tasks(self)
302
- if session.is_allowed_to_run(task)
303
- ]
304
- root_task_coros = [
305
- run_async(root_task.exec_chain(session)) for root_task in root_tasks
306
- ]
307
298
  try:
299
+ log_state = asyncio.create_task(self._log_session_state(session))
300
+ root_tasks = [
301
+ task
302
+ for task in session.get_root_tasks(self)
303
+ if session.is_allowed_to_run(task)
304
+ ]
305
+ root_task_coros = [
306
+ run_async(root_task.exec_chain(session)) for root_task in root_tasks
307
+ ]
308
308
  await asyncio.gather(*root_task_coros)
309
309
  await session.wait_deferred()
310
310
  session.terminate()
@@ -312,7 +312,7 @@ class BaseTask(AnyTask):
312
312
  return session.final_result
313
313
  except IndexError:
314
314
  return None
315
- except asyncio.CancelledError:
315
+ except (asyncio.CancelledError, KeyboardInterrupt):
316
316
  ctx = self.get_ctx(session)
317
317
  ctx.log_info("Session terminated")
318
318
  finally:
@@ -0,0 +1,3 @@
1
+ PREPEND = 0
2
+ APPEND = 1
3
+ REPLACE = 2
@@ -0,0 +1,58 @@
1
+ import libcst as cst
2
+
3
+ from zrb.util.codemod.modification_mode import APPEND, PREPEND, REPLACE
4
+
5
+
6
+ def replace_class_code(original_code: str, class_name: str, new_code: str) -> str:
7
+ return _modify_code(original_code, class_name, new_code, REPLACE)
8
+
9
+
10
+ def prepend_code_to_class(original_code: str, class_name: str, new_code: str) -> str:
11
+ return _modify_code(original_code, class_name, new_code, PREPEND)
12
+
13
+
14
+ def append_code_to_class(original_code: str, class_name: str, new_code: str) -> str:
15
+ return _modify_code(original_code, class_name, new_code, APPEND)
16
+
17
+
18
+ def _modify_code(original_code: str, class_name: str, new_code: str, mode: int) -> str:
19
+ # Parse the original code into a module
20
+ module = cst.parse_module(original_code)
21
+ # Initialize transformer with the class name and method code
22
+ transformer = _ClassCodeModifier(class_name, new_code, mode)
23
+ # Apply the transformation
24
+ modified_module = module.visit(transformer)
25
+ # Check if the class was found
26
+ if not transformer.class_found:
27
+ raise ValueError(f"Class {class_name} not found in the provided code.")
28
+ # Return the modified code
29
+ return modified_module.code
30
+
31
+
32
+ class _ClassCodeModifier(cst.CSTTransformer):
33
+ def __init__(self, class_name: str, new_code: str, mode: int):
34
+ self.class_name = class_name
35
+ self.new_code = cst.parse_module(new_code).body
36
+ self.class_found = False
37
+ self.mode = mode
38
+
39
+ def leave_ClassDef(
40
+ self, original_node: cst.ClassDef, updated_node: cst.ClassDef
41
+ ) -> cst.ClassDef:
42
+ # Check if this is the target class
43
+ if original_node.name.value == self.class_name:
44
+ self.class_found = True
45
+ if self.mode == REPLACE:
46
+ new_body = updated_node.body.with_changes(body=tuple(self.new_code))
47
+ return updated_node.with_changes(body=new_body)
48
+ if self.mode == PREPEND:
49
+ new_body = updated_node.body.with_changes(
50
+ body=tuple(self.new_code) + updated_node.body.body
51
+ )
52
+ return updated_node.with_changes(body=new_body)
53
+ if self.mode == APPEND:
54
+ new_body = updated_node.body.with_changes(
55
+ body=updated_node.body.body + tuple(self.new_code)
56
+ )
57
+ return updated_node.with_changes(body=new_body)
58
+ return updated_node