Feat: read web api testcases (#12383)

### What problem does this PR solve?

Web API testcase for list_messages, get_recent_message.

### Type of change

- [x] New Feature (non-breaking change which adds functionality)

---------

Co-authored-by: Kevin Hu <kevinhu.sh@gmail.com>
This commit is contained in:
Lynn
2026-01-01 12:52:40 +08:00
committed by GitHub
parent 4d3a3a97ef
commit 1f4a17863f
5 changed files with 343 additions and 0 deletions

View File

@ -29,6 +29,7 @@ DIALOG_APP_URL = f"/{VERSION}/dialog"
# SESSION_WITH_CHAT_ASSISTANT_API_URL = "/api/v1/chats/{chat_id}/sessions"
# SESSION_WITH_AGENT_API_URL = "/api/v1/agents/{agent_id}/sessions"
MEMORY_API_URL = f"/{VERSION}/memories"
MESSAGE_API_URL = f"/{VERSION}/messages"
# KB APP
@ -299,3 +300,76 @@ def get_memory_config(auth, memory_id:str):
url = f"{HOST_ADDRESS}{MEMORY_API_URL}/{memory_id}/config"
res = requests.get(url=url, headers=HEADERS, auth=auth)
return res.json()
def list_memory_message(auth, memory_id, params=None):
url = f"{HOST_ADDRESS}{MEMORY_API_URL}/{memory_id}"
if params:
query_parts = []
for key, value in params.items():
if isinstance(value, list):
for item in value:
query_parts.append(f"{key}={item}")
else:
query_parts.append(f"{key}={value}")
query_string = "&".join(query_parts)
url = f"{url}?{query_string}"
res = requests.get(url=url, headers=HEADERS, auth=auth)
return res.json()
def add_message(auth, payload=None):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}"
res = requests.post(url=url, headers=HEADERS, auth=auth, json=payload)
return res.json()
def forget_message(auth, memory_id: str, message_id: int):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}/{memory_id}:{message_id}"
res = requests.delete(url=url, headers=HEADERS, auth=auth)
return res.json()
def update_message_status(auth, memory_id: str, message_id: int, status: bool):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}/{memory_id}:{message_id}"
payload = {"status": status}
res = requests.put(url=url, headers=HEADERS, auth=auth, json=payload)
return res.json()
def search_message(auth, params=None):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}/search"
if params:
query_parts = []
for key, value in params.items():
if isinstance(value, list):
for item in value:
query_parts.append(f"{key}={item}")
else:
query_parts.append(f"{key}={value}")
query_string = "&".join(query_parts)
url = f"{url}?{query_string}"
res = requests.get(url=url, headers=HEADERS, auth=auth)
return res.json()
def get_recent_message(auth, params=None):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}"
if params:
query_parts = []
for key, value in params.items():
if isinstance(value, list):
for item in value:
query_parts.append(f"{key}={item}")
else:
query_parts.append(f"{key}={value}")
query_string = "&".join(query_parts)
url = f"{url}?{query_string}"
res = requests.get(url=url, headers=HEADERS, auth=auth)
return res.json()
def get_message_content(auth, memory_id: str, message_id: int):
url = f"{HOST_ADDRESS}{MESSAGE_API_URL}/{memory_id}:{message_id}/content"
res = requests.get(url=url, headers=HEADERS, auth=auth)
return res.json()

View File

@ -0,0 +1,101 @@
#
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import time
import uuid
import pytest
import random
from test_web_api.common import create_memory, list_memory, add_message, delete_memory
@pytest.fixture(scope="class")
def add_memory_with_multiple_message_func(request, WebApiAuth):
def cleanup():
memory_list_res = list_memory(WebApiAuth)
exist_memory_ids = [memory["id"] for memory in memory_list_res["data"]["memory_list"]]
for _memory_id in exist_memory_ids:
delete_memory(WebApiAuth, _memory_id)
request.addfinalizer(cleanup)
payload = {
"name": "test_memory_0",
"memory_type": ["raw"] + random.choices(["semantic", "episodic", "procedural"], k=random.randint(1, 3)),
"embd_id": "BAAI/bge-small-en-v1.5@Builtin",
"llm_id": "glm-4-flash@ZHIPU-AI"
}
res = create_memory(WebApiAuth, payload)
memory_id = res["data"]["id"]
agent_id = uuid.uuid4().hex
message_payload = {
"memory_id": [memory_id],
"agent_id": agent_id,
"session_id": uuid.uuid4().hex,
"user_id": "",
"user_input": "what is coriander?",
"agent_response": """
Coriander is a versatile herb with two main edible parts, and its name can refer to both:
1. Leaves and Stems (often called Cilantro or Fresh Coriander): These are the fresh, green, fragrant leaves and tender stems of the plant Coriandrum sativum. They have a bright, citrusy, and sometimes pungent flavor. Cilantro is widely used as a garnish or key ingredient in cuisines like Mexican, Indian, Thai, and Middle Eastern.
2. Seeds (called Coriander Seeds): These are the dried, golden-brown seeds of the same plant. When ground, they become coriander powder. The seeds have a warm, nutty, floral, and slightly citrusy taste, completely different from the fresh leaves. They are a fundamental spice in curries, stews, pickles, and baking.
Key Point of Confusion: The naming differs by region. In North America, "coriander" typically refers to the seeds, while "cilantro" refers to the fresh leaves. In the UK, Europe, and many other parts of the world, "coriander" refers to the fresh herb, and the seeds are called "coriander seeds."
"""
}
add_message(WebApiAuth, message_payload)
request.cls.memory_id = memory_id
request.cls.agent_id = agent_id
return memory_id
@pytest.fixture(scope="class")
def add_memory_with_5_raw_message_func(request, WebApiAuth):
def cleanup():
memory_list_res = list_memory(WebApiAuth)
exist_memory_ids = [memory["id"] for memory in memory_list_res["data"]["memory_list"]]
for _memory_id in exist_memory_ids:
delete_memory(WebApiAuth, _memory_id)
request.addfinalizer(cleanup)
payload = {
"name": "test_memory_1",
"memory_type": ["raw"],
"embd_id": "BAAI/bge-small-en-v1.5@Builtin",
"llm_id": "glm-4-flash@ZHIPU-AI"
}
res = create_memory(WebApiAuth, payload)
memory_id = res["data"]["id"]
agent_ids = [uuid.uuid4().hex for _ in range(2)]
session_ids = [uuid.uuid4().hex for _ in range(5)]
for i in range(5):
message_payload = {
"memory_id": [memory_id],
"agent_id": agent_ids[i % 2],
"session_id": session_ids[i],
"user_id": "",
"user_input": "what is coriander?",
"agent_response": """
Coriander is a versatile herb with two main edible parts, and its name can refer to both:
1. Leaves and Stems (often called Cilantro or Fresh Coriander): These are the fresh, green, fragrant leaves and tender stems of the plant Coriandrum sativum. They have a bright, citrusy, and sometimes pungent flavor. Cilantro is widely used as a garnish or key ingredient in cuisines like Mexican, Indian, Thai, and Middle Eastern.
2. Seeds (called Coriander Seeds): These are the dried, golden-brown seeds of the same plant. When ground, they become coriander powder. The seeds have a warm, nutty, floral, and slightly citrusy taste, completely different from the fresh leaves. They are a fundamental spice in curries, stews, pickles, and baking.
Key Point of Confusion: The naming differs by region. In North America, "coriander" typically refers to the seeds, while "cilantro" refers to the fresh leaves. In the UK, Europe, and many other parts of the world, "coriander" refers to the fresh herb, and the seeds are called "coriander seeds."
"""
}
add_message(WebApiAuth, message_payload)
request.cls.memory_id = memory_id
request.cls.agent_ids = agent_ids
request.cls.session_ids = session_ids
time.sleep(2) # make sure refresh to index before search
return memory_id

View File

@ -0,0 +1,68 @@
#
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import random
import pytest
from test_web_api.common import get_recent_message
from configs import INVALID_API_TOKEN
from libs.auth import RAGFlowWebApiAuth
class TestAuthorization:
@pytest.mark.p1
@pytest.mark.parametrize(
"invalid_auth, expected_code, expected_message",
[
(None, 401, "<Unauthorized '401: Unauthorized'>"),
(RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, "<Unauthorized '401: Unauthorized'>"),
],
)
def test_auth_invalid(self, invalid_auth, expected_code, expected_message):
res = get_recent_message(invalid_auth)
assert res["code"] == expected_code, res
assert res["message"] == expected_message, res
@pytest.mark.usefixtures("add_memory_with_5_raw_message_func")
class TestGetRecentMessage:
@pytest.mark.p1
def test_get_recent_messages(self, WebApiAuth):
memory_id = self.memory_id
res = get_recent_message(WebApiAuth, params={"memory_id": memory_id})
assert res["code"] == 0, res
assert len(res["data"]) == 5, res
@pytest.mark.p2
def test_filter_recent_messages_by_agent(self, WebApiAuth):
memory_id = self.memory_id
agent_ids = self.agent_ids
agent_id = random.choice(agent_ids)
res = get_recent_message(WebApiAuth, params={"agent_id": agent_id, "memory_id": memory_id})
assert res["code"] == 0, res
for message in res["data"]:
assert message["agent_id"] == agent_id, message
@pytest.mark.p2
def test_filter_recent_messages_by_session(self, WebApiAuth):
memory_id = self.memory_id
session_ids = self.session_ids
session_id = random.choice(session_ids)
res = get_recent_message(WebApiAuth, params={"session_id": session_id, "memory_id": memory_id})
assert res["code"] == 0, res
for message in res["data"]:
assert message["session_id"] == session_id, message

View File

@ -0,0 +1,100 @@
#
# Copyright 2025 The InfiniFlow Authors. All Rights Reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
import os
import random
import pytest
from test_web_api.common import list_memory_message
from configs import INVALID_API_TOKEN
from libs.auth import RAGFlowWebApiAuth
class TestAuthorization:
@pytest.mark.p1
@pytest.mark.parametrize(
"invalid_auth, expected_code, expected_message",
[
(None, 401, "<Unauthorized '401: Unauthorized'>"),
(RAGFlowWebApiAuth(INVALID_API_TOKEN), 401, "<Unauthorized '401: Unauthorized'>"),
],
)
def test_auth_invalid(self, invalid_auth, expected_code, expected_message):
res = list_memory_message(invalid_auth, "")
assert res["code"] == expected_code, res
assert res["message"] == expected_message, res
@pytest.mark.usefixtures("add_memory_with_5_raw_message_func")
class TestMessageList:
@pytest.mark.p1
def test_params_unset(self, WebApiAuth):
memory_id = self.memory_id
res = list_memory_message(WebApiAuth, memory_id, params=None)
assert res["code"] == 0, res
assert len(res["data"]["messages"]["message_list"]) == 5, res
@pytest.mark.p1
def test_params_empty(self, WebApiAuth):
memory_id = self.memory_id
res = list_memory_message(WebApiAuth, memory_id, params={})
assert res["code"] == 0, res
assert len(res["data"]["messages"]["message_list"]) == 5, res
@pytest.mark.p1
@pytest.mark.parametrize(
"params, expected_page_size",
[
({"page": 1, "page_size": 10}, 5),
({"page": 2, "page_size": 10}, 0),
({"page": 1, "page_size": 2}, 2),
({"page": 3, "page_size": 2}, 1),
({"page": 5, "page_size": 10}, 0),
],
ids=["normal_first_page", "beyond_max_page", "normal_last_partial_page", "normal_middle_page",
"full_data_single_page"],
)
def test_page_size(self, WebApiAuth, params, expected_page_size):
# have added 5 messages in fixture
memory_id = self.memory_id
res = list_memory_message(WebApiAuth, memory_id, params=params)
assert res["code"] == 0, res
assert len(res["data"]["messages"]["message_list"]) == expected_page_size, res
@pytest.mark.p2
def test_filter_agent_id(self, WebApiAuth):
memory_id = self.memory_id
agent_ids = self.agent_ids
agent_id = random.choice(agent_ids)
res = list_memory_message(WebApiAuth, memory_id, params={"agent_id": agent_id})
assert res["code"] == 0, res
for message in res["data"]["messages"]["message_list"]:
assert message["agent_id"] == agent_id, message
@pytest.mark.p2
@pytest.mark.skipif(os.getenv("DOC_ENGINE") == "infinity", reason="Not support.")
def test_search_keyword(self, WebApiAuth):
memory_id = self.memory_id
session_ids = self.session_ids
session_id = random.choice(session_ids)
slice_start = random.randint(0, len(session_id) - 2)
slice_end = random.randint(slice_start + 1, len(session_id) - 1)
keyword = session_id[slice_start:slice_end]
res = list_memory_message(WebApiAuth, memory_id, params={"keywords": keyword})
assert res["code"] == 0, res
assert len(res["data"]["messages"]["message_list"]) > 0, res
for message in res["data"]["messages"]["message_list"]:
assert keyword in message["session_id"], message