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.
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.coveragerc +11 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/.gitignore +4 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/column/add_column_task.py +4 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/config.py +5 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_task.py +107 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +67 -4
- 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
- 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
- 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
- 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
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/template/gateway_subroute.py +57 -13
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +2 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/gateway/subroute/my_module.py +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/module_task_definition.py +10 -6
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task.py +56 -12
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/task_util.py +10 -4
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/base_service.py +136 -52
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/util/parser.py +1 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/config.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/3093c7336477_add_auth_tables.py +46 -43
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/migration/versions/8ed025bcc845_create_permissions.py +69 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/repository/user_db_repository.py +5 -2
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/user_service.py +16 -21
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/subroute/auth.py +193 -43
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/gateway/util/auth.py +57 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/requirements.txt +6 -1
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/permission.py +1 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/user.py +9 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/_util/access_token.py +19 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_create_permission.py +59 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_delete_permission.py +68 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_read_permission.py +71 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/permission/test_update_permission.py +66 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/auth/test_user_session.py +195 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py +28 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_homepage.py +17 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_not_found_error.py +16 -0
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test.sh +7 -0
- zrb/task/base_task.py +10 -10
- zrb/util/codemod/modification_mode.py +3 -0
- zrb/util/codemod/modify_class.py +58 -0
- zrb/util/codemod/modify_class_parent.py +68 -0
- zrb/util/codemod/modify_class_property.py +128 -0
- zrb/util/codemod/modify_dict.py +75 -0
- zrb/util/codemod/modify_function.py +65 -0
- zrb/util/codemod/modify_function_call.py +68 -0
- zrb/util/codemod/modify_method.py +88 -0
- zrb/util/codemod/{prepend_code_to_module.py → modify_module.py} +2 -3
- zrb/util/file.py +3 -2
- {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/METADATA +1 -1
- {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/RECORD +53 -36
- zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/migrate.py +0 -3
- zrb/util/codemod/append_code_to_class.py +0 -35
- zrb/util/codemod/append_code_to_function.py +0 -38
- zrb/util/codemod/append_code_to_method.py +0 -55
- zrb/util/codemod/append_key_to_dict.py +0 -51
- zrb/util/codemod/append_param_to_function_call.py +0 -39
- zrb/util/codemod/prepend_parent_to_class.py +0 -38
- zrb/util/codemod/prepend_property_to_class.py +0 -55
- {zrb-1.0.0b9.dist-info → zrb-1.0.0b10.dist-info}/WHEEL +0 -0
- {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
|
zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/test/test_health_and_readiness.py
ADDED
@@ -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")
|
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,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
|