Merge branch 'main' into llm_mock

This commit is contained in:
yzlin 2024-01-04 17:19:09 +08:00
commit 271ecc30a2
23 changed files with 575 additions and 114 deletions

View file

@ -11,6 +11,7 @@ import json
import logging
import os
import re
import uuid
import pytest
@ -123,9 +124,9 @@ def loguru_caplog(caplog):
# init & dispose git repo
@pytest.fixture(scope="session", autouse=True)
@pytest.fixture(scope="function", autouse=True)
def setup_and_teardown_git_repo(request):
CONFIG.git_repo = GitRepository(local_path=DEFAULT_WORKSPACE_ROOT / "unittest")
CONFIG.git_repo = GitRepository(local_path=DEFAULT_WORKSPACE_ROOT / f"unittest/{uuid.uuid4().hex}")
CONFIG.git_reinit = True
# Destroy git repo at the end of the test session.

View file

@ -11,13 +11,19 @@ from pathlib import Path
import pytest
from metagpt.actions.rebuild_class_view import RebuildClassView
from metagpt.config import CONFIG
from metagpt.const import GRAPH_REPO_FILE_REPO
from metagpt.llm import LLM
@pytest.mark.asyncio
async def test_rebuild():
action = RebuildClassView(name="RedBean", context=Path(__file__).parent.parent, llm=LLM())
action = RebuildClassView(
name="RedBean", context=str(Path(__file__).parent.parent.parent.parent / "metagpt"), llm=LLM()
)
await action.run()
graph_file_repo = CONFIG.git_repo.new_file_repository(relative_path=GRAPH_REPO_FILE_REPO)
assert graph_file_repo.changed_files
if __name__ == "__main__":

View file

@ -19,6 +19,9 @@ from metagpt.config import CONFIG
from metagpt.const import SYSTEM_DESIGN_FILE_REPO, TASK_FILE_REPO
from metagpt.schema import (
AIMessage,
ClassAttribute,
ClassMethod,
ClassView,
CodeSummarizeContext,
Document,
Message,
@ -156,5 +159,30 @@ def test_CodeSummarizeContext(file_list, want):
assert want in m
def test_class_view():
attr_a = ClassAttribute(name="a", value_type="int", default_value="0", visibility="+", abstraction=True)
assert attr_a.get_mermaid(align=1) == "\t+int a=0*"
attr_b = ClassAttribute(name="b", value_type="str", default_value="0", visibility="#", static=True)
assert attr_b.get_mermaid(align=0) == '#str b="0"$'
class_view = ClassView(name="A")
class_view.attributes = [attr_a, attr_b]
method_a = ClassMethod(name="run", visibility="+", abstraction=True)
assert method_a.get_mermaid(align=1) == "\t+run()*"
method_b = ClassMethod(
name="_test",
visibility="#",
static=True,
args=[ClassAttribute(name="a", value_type="str"), ClassAttribute(name="b", value_type="int")],
return_type="str",
)
assert method_b.get_mermaid(align=0) == "#_test(str a,int b):str$"
class_view.methods = [method_a, method_b]
assert (
class_view.get_mermaid(align=0)
== 'class A{\n\t+int a=0*\n\t#str b="0"$\n\t+run()*\n\t#_test(str a,int b):str$\n}\n'
)
if __name__ == "__main__":
pytest.main([__file__, "-s"])

View file

@ -24,13 +24,14 @@ async def test_oas2_svc():
process = subprocess.Popen(["python", str(script_pathname)], cwd=str(workdir), env=env)
await asyncio.sleep(5)
url = "http://localhost:8080/openapi/greeting/dave"
headers = {"accept": "text/plain", "Content-Type": "application/json"}
data = {}
response = requests.post(url, headers=headers, json=data)
assert response.text == "Hello dave\n"
process.terminate()
try:
url = "http://localhost:8080/openapi/greeting/dave"
headers = {"accept": "text/plain", "Content-Type": "application/json"}
data = {}
response = requests.post(url, headers=headers, json=data)
assert response.text == "Hello dave\n"
finally:
process.terminate()
if __name__ == "__main__":

View file

@ -3,7 +3,7 @@
"""
@Time : 2023/12/26
@Author : mashenquan
@File : test_hello.py
@File : test_openapi_v3_hello.py
"""
import asyncio
import subprocess
@ -24,13 +24,14 @@ async def test_hello():
process = subprocess.Popen(["python", str(script_pathname)], cwd=workdir, env=env)
await asyncio.sleep(5)
url = "http://localhost:8082/openapi/greeting/dave"
headers = {"accept": "text/plain", "Content-Type": "application/json"}
data = {}
response = requests.post(url, headers=headers, json=data)
assert response.text == "Hello dave\n"
process.terminate()
try:
url = "http://localhost:8082/openapi/greeting/dave"
headers = {"accept": "text/plain", "Content-Type": "application/json"}
data = {}
response = requests.post(url, headers=headers, json=data)
assert response.text == "Hello dave\n"
finally:
process.terminate()
if __name__ == "__main__":

View file

@ -36,6 +36,7 @@ from metagpt.utils.common import (
read_file_block,
read_json_file,
require_python_version,
split_namespace,
)
@ -163,6 +164,23 @@ class TestGetProjectRoot:
assert concat_namespace("a", "b", "c", "e") == "a:b:c:e"
assert concat_namespace("a", "b", "c", "e", "f") == "a:b:c:e:f"
@pytest.mark.parametrize(
("val", "want"),
[
(
"tests/metagpt/test_role.py:test_react:Input:subscription",
["tests/metagpt/test_role.py", "test_react", "Input", "subscription"],
),
(
"tests/metagpt/test_role.py:test_react:Input:goal",
["tests/metagpt/test_role.py", "test_react", "Input", "goal"],
),
],
)
def test_split_namespace(self, val, want):
res = split_namespace(val)
assert res == want
def test_read_json_file(self):
assert read_json_file(str(Path(__file__).parent / "../../data/ut_writer/yft_swaggerApi.json"), encoding="utf-8")
with pytest.raises(FileNotFoundError):

View file

@ -6,20 +6,34 @@
@File : test_redis.py
"""
import mock
import pytest
from metagpt.config import CONFIG
from metagpt.utils.redis import Redis
async def async_mock_from_url(*args, **kwargs):
mock_client = mock.AsyncMock()
mock_client.set.return_value = None
mock_client.get.side_effect = [b"test", b""]
return mock_client
@pytest.mark.asyncio
async def test_redis():
@mock.patch("aioredis.from_url", return_value=async_mock_from_url())
async def test_redis(mock_from_url):
# Mock
# mock_client = mock.AsyncMock()
# mock_client.set.return_value=None
# mock_client.get.side_effect = [b'test', b'']
# mock_from_url.return_value = mock_client
# Prerequisites
assert CONFIG.REDIS_HOST and CONFIG.REDIS_HOST != "YOUR_REDIS_HOST"
assert CONFIG.REDIS_PORT and CONFIG.REDIS_PORT != "YOUR_REDIS_PORT"
# assert CONFIG.REDIS_USER
assert CONFIG.REDIS_PASSWORD is not None and CONFIG.REDIS_PASSWORD != "YOUR_REDIS_PASSWORD"
assert CONFIG.REDIS_DB is not None and CONFIG.REDIS_DB != "YOUR_REDIS_DB_INDEX, str, 0-based"
CONFIG.REDIS_HOST = "MOCK_REDIS_HOST"
CONFIG.REDIS_PORT = "MOCK_REDIS_PORT"
CONFIG.REDIS_PASSWORD = "MOCK_REDIS_PASSWORD"
CONFIG.REDIS_DB = 0
conn = Redis()
assert not conn.is_valid

View file

@ -2,20 +2,18 @@
# -*- coding: utf-8 -*-
# @Desc : unittest of repair_llm_raw_output
from metagpt.config import CONFIG
from metagpt.utils.repair_llm_raw_output import (
RepairType,
extract_content_from_output,
repair_invalid_json,
repair_llm_raw_output,
retry_parse_json_text,
)
"""
CONFIG.repair_llm_output should be True before retry_parse_json_text imported.
so we move `from ... impot ...` into each `test_xx` to avoid `Module level import not at top of file` format warning.
"""
CONFIG.repair_llm_output = True
def test_repair_case_sensitivity():
from metagpt.utils.repair_llm_raw_output import repair_llm_raw_output
raw_output = """{
"Original requirements": "Write a 2048 game",
"search Information": "",
@ -36,6 +34,8 @@ def test_repair_case_sensitivity():
def test_repair_special_character_missing():
from metagpt.utils.repair_llm_raw_output import repair_llm_raw_output
raw_output = """[CONTENT]
"Anything UNCLEAR": "No unclear requirements or information."
[CONTENT]"""
@ -66,11 +66,12 @@ def test_repair_special_character_missing():
target_output = '[CONTENT] {"a": "b"} [/CONTENT]'
output = repair_llm_raw_output(output=raw_output, req_keys=["[/CONTENT]"])
print("output\n", output)
assert output == target_output
def test_required_key_pair_missing():
from metagpt.utils.repair_llm_raw_output import repair_llm_raw_output
raw_output = '[CONTENT] {"a": "b"}'
target_output = '[CONTENT] {"a": "b"}\n[/CONTENT]'
@ -107,6 +108,8 @@ xxx
def test_repair_json_format():
from metagpt.utils.repair_llm_raw_output import RepairType, repair_llm_raw_output
raw_output = "{ xxx }]"
target_output = "{ xxx }"
@ -127,6 +130,8 @@ def test_repair_json_format():
def test_repair_invalid_json():
from metagpt.utils.repair_llm_raw_output import repair_invalid_json
raw_output = """{
"key": "value"
},
@ -169,6 +174,8 @@ value
def test_retry_parse_json_text():
from metagpt.utils.repair_llm_raw_output import retry_parse_json_text
invalid_json_text = """{
"Original Requirements": "Create a 2048 game",
"Competitive Quadrant Chart": "quadrantChart\n\ttitle Reach and engagement of campaigns\n\t\tx-axis"
@ -205,6 +212,7 @@ def test_extract_content_from_output():
xxx [CONTENT] xxx [CONTENT] xxxx [/CONTENT]
xxx [CONTENT] xxxx [/CONTENT] xxx [CONTENT][/CONTENT] xxx [CONTENT][/CONTENT] # target pair is the last one
"""
from metagpt.utils.repair_llm_raw_output import extract_content_from_output
output = (
'Sure! Here is the properly formatted JSON output based on the given context:\n\n[CONTENT]\n{\n"'

View file

@ -9,20 +9,36 @@ import uuid
from pathlib import Path
import aiofiles
import mock
import pytest
from metagpt.config import CONFIG
from metagpt.utils.common import aread
from metagpt.utils.s3 import S3
@pytest.mark.asyncio
async def test_s3():
@mock.patch("aioboto3.Session")
async def test_s3(mock_session_class):
# Set up the mock response
data = await aread(__file__, "utf-8")
mock_session_object = mock.Mock()
reader_mock = mock.AsyncMock()
reader_mock.read.side_effect = [data.encode("utf-8"), b"", data.encode("utf-8")]
type(reader_mock).url = mock.PropertyMock(return_value="https://mock")
mock_client = mock.AsyncMock()
mock_client.put_object.return_value = None
mock_client.get_object.return_value = {"Body": reader_mock}
mock_client.__aenter__.return_value = mock_client
mock_client.__aexit__.return_value = None
mock_session_object.client.return_value = mock_client
mock_session_class.return_value = mock_session_object
# Prerequisites
assert CONFIG.S3_ACCESS_KEY and CONFIG.S3_ACCESS_KEY != "YOUR_S3_ACCESS_KEY"
assert CONFIG.S3_SECRET_KEY and CONFIG.S3_SECRET_KEY != "YOUR_S3_SECRET_KEY"
assert CONFIG.S3_ENDPOINT_URL and CONFIG.S3_ENDPOINT_URL != "YOUR_S3_ENDPOINT_URL"
# assert CONFIG.S3_SECURE: true # true/false
assert CONFIG.S3_BUCKET and CONFIG.S3_BUCKET != "YOUR_S3_BUCKET"
# assert CONFIG.S3_ACCESS_KEY and CONFIG.S3_ACCESS_KEY != "YOUR_S3_ACCESS_KEY"
# assert CONFIG.S3_SECRET_KEY and CONFIG.S3_SECRET_KEY != "YOUR_S3_SECRET_KEY"
# assert CONFIG.S3_ENDPOINT_URL and CONFIG.S3_ENDPOINT_URL != "YOUR_S3_ENDPOINT_URL"
# assert CONFIG.S3_BUCKET and CONFIG.S3_BUCKET != "YOUR_S3_BUCKET"
conn = S3()
assert conn.is_valid
@ -42,6 +58,7 @@ async def test_s3():
assert "http" in res
# Mock session env
type(reader_mock).url = mock.PropertyMock(return_value="")
old_options = CONFIG.options.copy()
new_options = old_options.copy()
new_options["S3_ACCESS_KEY"] = "YOUR_S3_ACCESS_KEY"
@ -54,6 +71,8 @@ async def test_s3():
finally:
CONFIG.set_context(old_options)
await reader.close()
if __name__ == "__main__":
pytest.main([__file__, "-s"])

View file

@ -0,0 +1,13 @@
#!/usr/bin/env python3
# _*_ coding: utf-8 _*_
import pytest
def test_nodeid(request):
print(request.node.nodeid)
assert request.node.nodeid
if __name__ == "__main__":
pytest.main([__file__, "-s"])