[OND211-2329]: Added helper methods for department tests.
This commit is contained in:
parent
c38ec24b08
commit
6ef0668f0c
2 changed files with 203 additions and 39 deletions
|
|
@ -14,9 +14,18 @@
|
||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
#
|
#
|
||||||
|
|
||||||
|
import base64
|
||||||
|
import os
|
||||||
|
import time
|
||||||
|
from typing import Any, Dict
|
||||||
|
|
||||||
import pytest
|
import pytest
|
||||||
import requests
|
import requests
|
||||||
|
from Cryptodome.Cipher import PKCS1_v1_5 as Cipher_pkcs1_v1_5
|
||||||
|
from Cryptodome.PublicKey import RSA
|
||||||
|
|
||||||
from configs import EMAIL, HOST_ADDRESS, PASSWORD, VERSION, ZHIPU_AI_API_KEY
|
from configs import EMAIL, HOST_ADDRESS, PASSWORD, VERSION, ZHIPU_AI_API_KEY
|
||||||
|
from libs.auth import RAGFlowWebApiAuth
|
||||||
|
|
||||||
MARKER_EXPRESSIONS = {
|
MARKER_EXPRESSIONS = {
|
||||||
"p1": "p1",
|
"p1": "p1",
|
||||||
|
|
@ -50,53 +59,120 @@ def pytest_configure(config: pytest.Config) -> None:
|
||||||
print(f"\n[CONFIG] Active test level: {level}")
|
print(f"\n[CONFIG] Active test level: {level}")
|
||||||
|
|
||||||
|
|
||||||
|
def encrypt_password(password: str) -> str:
|
||||||
|
"""Encrypt password for API calls.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
password: Plain text password to encrypt.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Encrypted password as a base64-encoded string.
|
||||||
|
"""
|
||||||
|
current_dir: str = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
project_base: str = os.path.abspath(os.path.join(current_dir, "..", ".."))
|
||||||
|
file_path: str = os.path.join(project_base, "conf", "public.pem")
|
||||||
|
|
||||||
|
with open(file_path, encoding="utf-8") as pem_file:
|
||||||
|
rsa_key: RSA.RsaKey = RSA.import_key(pem_file.read(), passphrase="Welcome")
|
||||||
|
|
||||||
|
cipher: Cipher_pkcs1_v1_5.PKCS115_Cipher = Cipher_pkcs1_v1_5.new(rsa_key)
|
||||||
|
password_base64: str = base64.b64encode(password.encode()).decode()
|
||||||
|
encrypted_password: bytes = cipher.encrypt(password_base64.encode())
|
||||||
|
return base64.b64encode(encrypted_password).decode()
|
||||||
|
|
||||||
|
|
||||||
def register():
|
def register():
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/user/register"
|
url: str = HOST_ADDRESS + f"/{VERSION}/user/register"
|
||||||
name = "qa"
|
name: str = "qa"
|
||||||
register_data = {"email": EMAIL, "nickname": name, "password": PASSWORD}
|
# Encrypt the plain password "123" before sending
|
||||||
res = requests.post(url=url, json=register_data)
|
plain_password: str = "123"
|
||||||
res = res.json()
|
encrypted_password: str = encrypt_password(plain_password)
|
||||||
|
register_data = {"email": EMAIL, "nickname": name, "password": encrypted_password}
|
||||||
|
res: requests.Response = requests.post(url=url, json=register_data)
|
||||||
|
res: Dict[str, Any] = res.json()
|
||||||
if res.get("code") != 0 and "has already registered" not in res.get("message"):
|
if res.get("code") != 0 and "has already registered" not in res.get("message"):
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
|
|
||||||
|
|
||||||
def login():
|
def login():
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/user/login"
|
url: str = HOST_ADDRESS + f"/{VERSION}/user/login"
|
||||||
login_data = {"email": EMAIL, "password": PASSWORD}
|
# Encrypt the plain password "123" before sending
|
||||||
response = requests.post(url=url, json=login_data)
|
plain_password: str = "123"
|
||||||
res = response.json()
|
encrypted_password: str = encrypt_password(plain_password)
|
||||||
|
login_data = {"email": EMAIL, "password": encrypted_password}
|
||||||
|
response: requests.Response = requests.post(url=url, json=login_data)
|
||||||
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
auth = response.headers["Authorization"]
|
auth: str = response.headers["Authorization"]
|
||||||
return auth
|
return auth
|
||||||
|
|
||||||
|
|
||||||
|
def login_as_user(email: str, password: str) -> RAGFlowWebApiAuth:
|
||||||
|
"""Login as a user and return authentication object.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
email: User email address.
|
||||||
|
password: Plain text password.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
RAGFlowWebApiAuth object for authenticated requests.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
Exception: If login fails.
|
||||||
|
"""
|
||||||
|
# Small delay to ensure user creation is committed to database
|
||||||
|
time.sleep(0.1)
|
||||||
|
|
||||||
|
url: str = HOST_ADDRESS + f"/{VERSION}/user/login"
|
||||||
|
encrypted_password: str = encrypt_password(password)
|
||||||
|
login_data: Dict[str, str] = {"email": email, "password": encrypted_password}
|
||||||
|
response: requests.Response = requests.post(url=url, json=login_data)
|
||||||
|
res: Dict[str, Any] = response.json()
|
||||||
|
if res.get("code") != 0:
|
||||||
|
message: str = str(res.get("message", "Login failed"))
|
||||||
|
raise Exception(f"Login failed: {message}")
|
||||||
|
auth_token: str = response.headers.get("Authorization", "")
|
||||||
|
if not auth_token:
|
||||||
|
raise Exception("No authorization token received from login response")
|
||||||
|
return RAGFlowWebApiAuth(auth_token)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def auth():
|
def auth():
|
||||||
try:
|
try:
|
||||||
register()
|
register()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
print(e)
|
print(e)
|
||||||
auth = login()
|
try:
|
||||||
return auth
|
auth: str = login()
|
||||||
|
return auth
|
||||||
|
except Exception as e:
|
||||||
|
error_msg = str(e)
|
||||||
|
if "Email and password do not match" in error_msg:
|
||||||
|
raise Exception(
|
||||||
|
f"Login failed: User {EMAIL} exists but password doesn't match. "
|
||||||
|
f"Please ensure the user has the correct password or delete the user first."
|
||||||
|
) from e
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session")
|
@pytest.fixture(scope="session")
|
||||||
def token(auth):
|
def token(auth):
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/system/new_token"
|
url: str = HOST_ADDRESS + f"/{VERSION}/system/new_token"
|
||||||
auth = {"Authorization": auth}
|
auth: Dict[str, str] = {"Authorization": auth}
|
||||||
response = requests.post(url=url, headers=auth)
|
response: requests.Response = requests.post(url=url, headers=auth)
|
||||||
res = response.json()
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
return res["data"].get("token")
|
return res["data"].get("token")
|
||||||
|
|
||||||
|
|
||||||
def get_my_llms(auth, name):
|
def get_my_llms(auth: str, name: str) -> bool:
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/llm/my_llms"
|
url: str = HOST_ADDRESS + f"/{VERSION}/llm/my_llms"
|
||||||
authorization = {"Authorization": auth}
|
authorization: Dict[str, str] = {"Authorization": auth}
|
||||||
response = requests.get(url=url, headers=authorization)
|
response: requests.Response = requests.get(url=url, headers=authorization)
|
||||||
res = response.json()
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
if name in res.get("data"):
|
if name in res.get("data"):
|
||||||
|
|
@ -104,41 +180,41 @@ def get_my_llms(auth, name):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
def add_models(auth):
|
def add_models(auth: str):
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/llm/set_api_key"
|
url: str = HOST_ADDRESS + f"/{VERSION}/llm/set_api_key"
|
||||||
authorization = {"Authorization": auth}
|
authorization: Dict[str, str] = {"Authorization": auth}
|
||||||
models_info = {
|
models_info: Dict[str, Dict[str, str]] = {
|
||||||
"ZHIPU-AI": {"llm_factory": "ZHIPU-AI", "api_key": ZHIPU_AI_API_KEY},
|
"ZHIPU-AI": {"llm_factory": "ZHIPU-AI", "api_key": ZHIPU_AI_API_KEY},
|
||||||
}
|
}
|
||||||
|
|
||||||
for name, model_info in models_info.items():
|
for name, model_info in models_info.items():
|
||||||
if not get_my_llms(auth, name):
|
if not get_my_llms(auth, name):
|
||||||
response = requests.post(url=url, headers=authorization, json=model_info)
|
response: requests.Response = requests.post(url=url, headers=authorization, json=model_info)
|
||||||
res = response.json()
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
pytest.exit(f"Critical error in add_models: {res.get('message')}")
|
pytest.exit(f"Critical error in add_models: {res.get('message')}")
|
||||||
|
|
||||||
|
|
||||||
def get_tenant_info(auth):
|
def get_tenant_info(auth: str) -> str:
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/user/tenant_info"
|
url: str = HOST_ADDRESS + f"/{VERSION}/user/tenant_info"
|
||||||
authorization = {"Authorization": auth}
|
authorization: Dict[str, str] = {"Authorization": auth}
|
||||||
response = requests.get(url=url, headers=authorization)
|
response: requests.Response = requests.get(url=url, headers=authorization)
|
||||||
res = response.json()
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
return res["data"].get("tenant_id")
|
return res["data"].get("tenant_id")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture(scope="session", autouse=True)
|
@pytest.fixture(scope="session", autouse=True)
|
||||||
def set_tenant_info(auth):
|
def set_tenant_info(auth: str):
|
||||||
try:
|
try:
|
||||||
add_models(auth)
|
add_models(auth)
|
||||||
tenant_id = get_tenant_info(auth)
|
tenant_id: str = get_tenant_info(auth)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
pytest.exit(f"Error in set_tenant_info: {str(e)}")
|
pytest.exit(f"Error in set_tenant_info: {str(e)}")
|
||||||
url = HOST_ADDRESS + f"/{VERSION}/user/set_tenant_info"
|
url: str = HOST_ADDRESS + f"/{VERSION}/user/set_tenant_info"
|
||||||
authorization = {"Authorization": auth}
|
authorization: Dict[str, str] = {"Authorization": auth}
|
||||||
tenant_info = {
|
tenant_info: Dict[str, Any] = {
|
||||||
"tenant_id": tenant_id,
|
"tenant_id": tenant_id,
|
||||||
"llm_id": "glm-4-flash@ZHIPU-AI",
|
"llm_id": "glm-4-flash@ZHIPU-AI",
|
||||||
"embd_id": "BAAI/bge-small-en-v1.5@Builtin",
|
"embd_id": "BAAI/bge-small-en-v1.5@Builtin",
|
||||||
|
|
@ -146,7 +222,7 @@ def set_tenant_info(auth):
|
||||||
"asr_id": "",
|
"asr_id": "",
|
||||||
"tts_id": None,
|
"tts_id": None,
|
||||||
}
|
}
|
||||||
response = requests.post(url=url, headers=authorization, json=tenant_info)
|
response: requests.Response = requests.post(url=url, headers=authorization, json=tenant_info)
|
||||||
res = response.json()
|
res: Dict[str, Any] = response.json()
|
||||||
if res.get("code") != 0:
|
if res.get("code") != 0:
|
||||||
raise Exception(res.get("message"))
|
raise Exception(res.get("message"))
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,14 @@ from requests.auth import AuthBase
|
||||||
from requests_toolbelt import MultipartEncoder
|
from requests_toolbelt import MultipartEncoder
|
||||||
from utils.file_utils import create_txt_file
|
from utils.file_utils import create_txt_file
|
||||||
|
|
||||||
|
# Import login_as_user from root conftest
|
||||||
|
import importlib.util
|
||||||
|
_root_conftest_path = Path(__file__).parent.parent / "conftest.py"
|
||||||
|
_root_spec = importlib.util.spec_from_file_location("root_conftest", _root_conftest_path)
|
||||||
|
_root_conftest_module = importlib.util.module_from_spec(_root_spec)
|
||||||
|
_root_spec.loader.exec_module(_root_conftest_module)
|
||||||
|
login_as_user = _root_conftest_module.login_as_user
|
||||||
|
|
||||||
HEADERS = {"Content-Type": "application/json"}
|
HEADERS = {"Content-Type": "application/json"}
|
||||||
DATASETS_API_URL = f"/api/{VERSION}/datasets"
|
DATASETS_API_URL = f"/api/{VERSION}/datasets"
|
||||||
FILE_API_URL = f"/api/{VERSION}/datasets/{{dataset_id}}/documents"
|
FILE_API_URL = f"/api/{VERSION}/datasets/{{dataset_id}}/documents"
|
||||||
|
|
@ -553,3 +561,83 @@ def remove_department_member(
|
||||||
url=url, headers=headers, auth=auth
|
url=url, headers=headers, auth=auth
|
||||||
)
|
)
|
||||||
return res.json()
|
return res.json()
|
||||||
|
|
||||||
|
|
||||||
|
def update_department(
|
||||||
|
auth: Union[AuthBase, str, None],
|
||||||
|
department_id: str,
|
||||||
|
payload: Optional[Dict[str, Any]] = None,
|
||||||
|
*,
|
||||||
|
headers: Dict[str, str] = HEADERS,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Update a department's details.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
auth: Authentication object (AuthBase subclass), token string, or None.
|
||||||
|
department_id: The department ID to update.
|
||||||
|
payload: Optional JSON payload containing update data (e.g., name, description).
|
||||||
|
headers: Optional HTTP headers. Defaults to HEADERS.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response as a dictionary containing the updated department data.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
requests.RequestException: If the HTTP request fails.
|
||||||
|
"""
|
||||||
|
url: str = f"{HOST_ADDRESS}{DEPARTMENT_API_URL}/{department_id}"
|
||||||
|
res: requests.Response = requests.put(
|
||||||
|
url=url, headers=headers, auth=auth, json=payload
|
||||||
|
)
|
||||||
|
return res.json()
|
||||||
|
|
||||||
|
|
||||||
|
def delete_department(
|
||||||
|
auth: Union[AuthBase, str, None],
|
||||||
|
department_id: str,
|
||||||
|
*,
|
||||||
|
headers: Dict[str, str] = HEADERS,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""Delete a department.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
auth: Authentication object (AuthBase subclass), token string, or None.
|
||||||
|
department_id: The department ID to delete.
|
||||||
|
headers: Optional HTTP headers. Defaults to HEADERS.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response as a dictionary containing the deletion result.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
requests.RequestException: If the HTTP request fails.
|
||||||
|
"""
|
||||||
|
url: str = f"{HOST_ADDRESS}{DEPARTMENT_API_URL}/{department_id}"
|
||||||
|
res: requests.Response = requests.delete(
|
||||||
|
url=url, headers=headers, auth=auth
|
||||||
|
)
|
||||||
|
return res.json()
|
||||||
|
|
||||||
|
|
||||||
|
def list_department_members(
|
||||||
|
auth: Union[AuthBase, str, None],
|
||||||
|
department_id: str,
|
||||||
|
*,
|
||||||
|
headers: Dict[str, str] = HEADERS,
|
||||||
|
) -> Dict[str, Any]:
|
||||||
|
"""List all members in a department.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
auth: Authentication object (AuthBase subclass), token string, or None.
|
||||||
|
department_id: The department ID to list members from.
|
||||||
|
headers: Optional HTTP headers. Defaults to HEADERS.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
JSON response as a dictionary containing the list of department members.
|
||||||
|
|
||||||
|
Raises:
|
||||||
|
requests.RequestException: If the HTTP request fails.
|
||||||
|
"""
|
||||||
|
url: str = f"{HOST_ADDRESS}{DEPARTMENT_API_URL}/{department_id}/members"
|
||||||
|
res: requests.Response = requests.get(
|
||||||
|
url=url, headers=headers, auth=auth
|
||||||
|
)
|
||||||
|
return res.json()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue