From a2d8d066647a6a323adb07fdd04eaf0ce5a200d0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 28 Dec 2023 21:19:38 +0800 Subject: [PATCH 1/5] feat: +unit test --- metagpt/actions/write_docstring.py | 26 +++++---- tests/data/demo_project/prd.json | 1 + tests/metagpt/actions/test_write_docstring.py | 10 ++++ .../metagpt/actions/test_write_prd_review.py | 6 ++- .../actions/test_write_teaching_plan.py | 54 ++++--------------- tests/metagpt/learn/test_text_to_image.py | 31 ++++------- .../metagpt/provider/test_azure_openai_api.py | 20 +++++++ tests/metagpt/provider/test_metagpt_api.py | 14 +++++ tests/metagpt/provider/test_open_llm_api.py | 25 +++++++++ tests/metagpt/utils/test_s3.py | 2 + 10 files changed, 114 insertions(+), 75 deletions(-) create mode 100644 tests/data/demo_project/prd.json create mode 100644 tests/metagpt/provider/test_azure_openai_api.py create mode 100644 tests/metagpt/provider/test_metagpt_api.py create mode 100644 tests/metagpt/provider/test_open_llm_api.py diff --git a/metagpt/actions/write_docstring.py b/metagpt/actions/write_docstring.py index 68856c360..728b49fab 100644 --- a/metagpt/actions/write_docstring.py +++ b/metagpt/actions/write_docstring.py @@ -21,7 +21,10 @@ Example: This script uses the 'fire' library to create a command-line interface. It generates docstrings for the given Python code using the specified docstring style and adds them to the code. """ +from __future__ import annotations + import ast +from pathlib import Path from typing import Literal, Optional from pydantic import Field @@ -29,7 +32,7 @@ from pydantic import Field from metagpt.actions.action import Action from metagpt.llm import LLM from metagpt.provider.base_llm import BaseLLM -from metagpt.utils.common import OutputParser +from metagpt.utils.common import OutputParser, aread, awrite from metagpt.utils.pycst import merge_docstring PYTHON_DOCSTRING_SYSTEM = """### Requirements @@ -187,6 +190,16 @@ class WriteDocstring(Action): documented_code = OutputParser.parse_python_code(documented_code) return merge_docstring(code, documented_code) + @staticmethod + async def write_docstring( + filename: str | Path, overwrite: bool = False, style: Literal["google", "numpy", "sphinx"] = "google" + ) -> str: + data = await aread(str(filename)) + code = await WriteDocstring().run(data, style=style) + if overwrite: + await awrite(filename, code) + return code + def _simplify_python_code(code: str) -> None: """Simplifies the given Python code by removing expressions and the last if statement. @@ -207,13 +220,4 @@ def _simplify_python_code(code: str) -> None: if __name__ == "__main__": import fire - async def run(filename: str, overwrite: bool = False, style: Literal["google", "numpy", "sphinx"] = "google"): - with open(filename) as f: - code = f.read() - code = await WriteDocstring().run(code, style=style) - if overwrite: - with open(filename, "w") as f: - f.write(code) - return code - - fire.Fire(run) + fire.Fire(WriteDocstring.write_docstring) diff --git a/tests/data/demo_project/prd.json b/tests/data/demo_project/prd.json new file mode 100644 index 000000000..2dd26b384 --- /dev/null +++ b/tests/data/demo_project/prd.json @@ -0,0 +1 @@ +{"Language": "en_us", "Programming Language": "Python", "Original Requirements": "write a 2048 game", "Project Name": "game_2048", "Product Goals": ["Create an addictive and engaging gaming experience", "Ensure smooth performance and responsiveness", "Offer customizable game settings and features"], "User Stories": ["As a player, I want to be able to play the game on different devices and screen sizes", "As a gamer, I want to be challenged with increasing difficulty levels as I progress", "As a user, I want to be able to undo my last move in the game"], "Competitive Analysis": ["2048 Game by Gabriele Cirulli: Popular and addictive, lacks advanced customization options"], "Competitive Quadrant Chart": "quadrantChart\n title \"Engagement and Customization of 2048 Games\"\n x-axis \"Low Customization\" --> \"High Customization\"\n y-axis \"Low Engagement\" --> \"High Engagement\"\n quadrant-1 \"Enhance Customization\"\n quadrant-2 \"Improve Engagement\"\n quadrant-3 \"Maintain Customization, Enhance Engagement\"\n quadrant-4 \"Highly Engaging and Customizable\"\n \"2048 Game by Gabriele Cirulli\": [0.4, 0.7]\n \"Our Target Product\": [0.6, 0.8]", "Requirement Analysis": "The product should provide an intuitive and seamless gaming experience with customizable features to enhance user engagement.", "Requirement Pool": [["P0", "Implement game logic and user interface"], ["P1", "Incorporate multiple difficulty levels and scoring system"], ["P2", "Integrate customizable game settings and undo feature"]], "UI Design draft": "The UI should have a clean and modern design with intuitive game controls and customizable settings for difficulty levels and game themes.", "Anything UNCLEAR": "..."} \ No newline at end of file diff --git a/tests/metagpt/actions/test_write_docstring.py b/tests/metagpt/actions/test_write_docstring.py index a8a80b36d..a0fc46ebd 100644 --- a/tests/metagpt/actions/test_write_docstring.py +++ b/tests/metagpt/actions/test_write_docstring.py @@ -30,3 +30,13 @@ class Person: async def test_write_docstring(style: str, part: str): ret = await WriteDocstring().run(code, style=style) assert part in ret + + +@pytest.mark.asyncio +async def test_write(): + code = await WriteDocstring.write_docstring(__file__) + assert code + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/actions/test_write_prd_review.py b/tests/metagpt/actions/test_write_prd_review.py index 5077fa465..9b3f0a285 100644 --- a/tests/metagpt/actions/test_write_prd_review.py +++ b/tests/metagpt/actions/test_write_prd_review.py @@ -23,10 +23,14 @@ async def test_write_prd_review(): Timeline: The feature should be ready for testing in 1.5 months. """ - write_prd_review = WritePRDReview("write_prd_review") + write_prd_review = WritePRDReview(name="write_prd_review") prd_review = await write_prd_review.run(prd) # We cannot exactly predict the generated PRD review, but we can check if it is a string and if it is not empty assert isinstance(prd_review, str) assert len(prd_review) > 0 + + +if __name__ == "__main__": + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/actions/test_write_teaching_plan.py b/tests/metagpt/actions/test_write_teaching_plan.py index 3f25b2167..57a4f5eb0 100644 --- a/tests/metagpt/actions/test_write_teaching_plan.py +++ b/tests/metagpt/actions/test_write_teaching_plan.py @@ -6,53 +6,21 @@ @File : test_write_teaching_plan.py """ -import asyncio -from typing import Optional - -from langchain.llms.base import LLM -from pydantic import BaseModel +import pytest from metagpt.actions.write_teaching_plan import WriteTeachingPlanPart -from metagpt.config import Config -from metagpt.schema import Message -class MockWriteTeachingPlanPart(WriteTeachingPlanPart): - def __init__(self, options, name: str = "", context=None, llm: LLM = None, topic="", language="Chinese"): - super().__init__(options, name, context, llm, topic, language) - - async def _aask(self, prompt: str, system_msgs: Optional[list[str]] = None) -> str: - return f"{WriteTeachingPlanPart.DATA_BEGIN_TAG}\nprompt\n{WriteTeachingPlanPart.DATA_END_TAG}" - - -async def mock_write_teaching_plan_part(): - class Inputs(BaseModel): - input: str - name: str - topic: str - language: str - - inputs = [ - {"input": "AABBCC", "name": "A", "topic": WriteTeachingPlanPart.COURSE_TITLE, "language": "C"}, - {"input": "DDEEFFF", "name": "A1", "topic": "B1", "language": "C1"}, - ] - - for i in inputs: - seed = Inputs(**i) - options = Config().runtime_options - act = MockWriteTeachingPlanPart(options=options, name=seed.name, topic=seed.topic, language=seed.language) - await act.run([Message(content="")]) - assert act.topic == seed.topic - assert str(act) == seed.topic - assert act.name == seed.name - assert act.rsp == "# prompt" if seed.topic == WriteTeachingPlanPart.COURSE_TITLE else "prompt" - - -def test_suite(): - loop = asyncio.get_event_loop() - task = loop.create_task(mock_write_teaching_plan_part()) - loop.run_until_complete(task) +@pytest.mark.asyncio +@pytest.mark.parametrize( + ("topic", "context"), + [("Title", "Lesson 1: Learn to draw an apple."), ("Teaching Content", "Lesson 1: Learn to draw an apple.")], +) +async def test_write_teaching_plan_part(topic, context): + action = WriteTeachingPlanPart(topic=topic, context=context) + rsp = await action.run() + assert rsp if __name__ == "__main__": - test_suite() + pytest.main([__file__, "-s"]) diff --git a/tests/metagpt/learn/test_text_to_image.py b/tests/metagpt/learn/test_text_to_image.py index a6cbc45bf..626945218 100644 --- a/tests/metagpt/learn/test_text_to_image.py +++ b/tests/metagpt/learn/test_text_to_image.py @@ -7,35 +7,26 @@ @Desc : Unit tests. """ -import base64 import pytest -from pydantic import BaseModel +from metagpt.config import CONFIG from metagpt.learn.text_to_image import text_to_image @pytest.mark.asyncio async def test(): - class Input(BaseModel): - input: str - size_type: str + # Prerequisites + assert CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL + assert CONFIG.OPENAI_API_KEY - inputs = [{"input": "Panda emoji", "size_type": "512x512"}] - - for i in inputs: - seed = Input(**i) - base64_data = await text_to_image(seed.input) - assert base64_data != "" - print(f"{seed.input} -> {base64_data}") - flags = ";base64," - assert flags in base64_data - ix = base64_data.find(flags) + len(flags) - declaration = base64_data[0:ix] - assert declaration - data = base64_data[ix:] - assert data - assert base64.b64decode(data, validate=True) + data = await text_to_image("Panda emoji", size_type="512x512") + assert "base64" in data or "http" in data + key = CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL + CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL = None + data = await text_to_image("Panda emoji", size_type="512x512") + assert "base64" in data or "http" in data + CONFIG.METAGPT_TEXT_TO_IMAGE_MODEL_URL = key if __name__ == "__main__": diff --git a/tests/metagpt/provider/test_azure_openai_api.py b/tests/metagpt/provider/test_azure_openai_api.py new file mode 100644 index 000000000..a1f1effeb --- /dev/null +++ b/tests/metagpt/provider/test_azure_openai_api.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/12/28 +@Author : mashenquan +@File : test_azure_openai.py +""" +from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.llm import LLM + + +def test_llm(): + # Prerequisites + assert CONFIG.DEPLOYMENT_NAME and CONFIG.DEPLOYMENT_NAME != "YOUR_DEPLOYMENT_NAME" + assert CONFIG.OPENAI_API_KEY and CONFIG.OPENAI_API_KEY != "YOUR_AZURE_API_KEY" + assert CONFIG.OPENAI_API_VERSION + assert CONFIG.OPENAI_BASE_URL + + llm = LLM(provider=LLMProviderEnum.AZURE_OPENAI) + assert llm diff --git a/tests/metagpt/provider/test_metagpt_api.py b/tests/metagpt/provider/test_metagpt_api.py new file mode 100644 index 000000000..1f00cb653 --- /dev/null +++ b/tests/metagpt/provider/test_metagpt_api.py @@ -0,0 +1,14 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/12/28 +@Author : mashenquan +@File : test_metagpt_api.py +""" +from metagpt.config import LLMProviderEnum +from metagpt.llm import LLM + + +def test_llm(): + llm = LLM(provider=LLMProviderEnum.METAGPT) + assert llm diff --git a/tests/metagpt/provider/test_open_llm_api.py b/tests/metagpt/provider/test_open_llm_api.py new file mode 100644 index 000000000..b8be68504 --- /dev/null +++ b/tests/metagpt/provider/test_open_llm_api.py @@ -0,0 +1,25 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/12/28 +@Author : mashenquan +@File : test_open_llm_api.py +""" +from metagpt.config import CONFIG, LLMProviderEnum +from metagpt.llm import LLM +from metagpt.provider.open_llm_api import OpenLLMCostManager + + +def test_llm(): + llm = LLM(provider=LLMProviderEnum.OPEN_LLM) + assert llm + + +def test_cost(): + # Prerequisites + CONFIG.max_budget = 10 + + cost = OpenLLMCostManager() + cost.update_cost(prompt_tokens=10, completion_tokens=1, model="gpt-35-turbo") + assert cost.get_total_prompt_tokens() > 0 + assert cost.get_total_completion_tokens() > 0 diff --git a/tests/metagpt/utils/test_s3.py b/tests/metagpt/utils/test_s3.py index e4154b957..0a654f2da 100644 --- a/tests/metagpt/utils/test_s3.py +++ b/tests/metagpt/utils/test_s3.py @@ -45,9 +45,11 @@ async def test_s3(): @pytest.mark.asyncio async def test_s3_no_error(): conn = S3() + key = conn.auth_config["aws_secret_access_key"] conn.auth_config["aws_secret_access_key"] = "" res = await conn.cache("ABC", ".bak", "script") assert not res + conn.auth_config["aws_secret_access_key"] = key if __name__ == "__main__": From 5c152a0b50ced6b91f265b83b8213b7148d5e4f9 Mon Sep 17 00:00:00 2001 From: geekan Date: Thu, 28 Dec 2023 18:02:55 +0800 Subject: [PATCH 2/5] fix fireworks --- tests/metagpt/provider/test_fireworks_api.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/metagpt/provider/test_fireworks_api.py b/tests/metagpt/provider/test_fireworks_api.py index 00b3c716a..ebedb8000 100644 --- a/tests/metagpt/provider/test_fireworks_api.py +++ b/tests/metagpt/provider/test_fireworks_api.py @@ -57,10 +57,10 @@ async def mock_llm_achat_completion_stream(self, messgaes: list[dict]) -> str: @pytest.mark.asyncio async def test_fireworks_acompletion(mocker): - mocker.patch("metagpt.provider.fireworks_api.FireWorksGPTAPI.acompletion", mock_llm_acompletion) - mocker.patch("metagpt.provider.fireworks_api.FireWorksGPTAPI._achat_completion", mock_llm_acompletion) + mocker.patch("metagpt.provider.fireworks_api.FireworksLLM.acompletion", mock_llm_acompletion) + mocker.patch("metagpt.provider.fireworks_api.FireworksLLM._achat_completion", mock_llm_acompletion) mocker.patch( - "metagpt.provider.fireworks_api.FireWorksGPTAPI._achat_completion_stream", mock_llm_achat_completion_stream + "metagpt.provider.fireworks_api.FireworksLLM._achat_completion_stream", mock_llm_achat_completion_stream ) fireworks_gpt = FireworksLLM() From 7145f7dcf82693ffa0f4163c38a122a6a9dc5b41 Mon Sep 17 00:00:00 2001 From: geekan Date: Thu, 28 Dec 2023 18:06:02 +0800 Subject: [PATCH 3/5] fix tests --- metagpt/provider/google_gemini_api.py | 2 +- metagpt/strategy/tot.py | 4 +-- tests/metagpt/actions/test_research.py | 10 +++---- tests/metagpt/provider/test_base_gpt_api.py | 30 ++++++++++----------- tests/metagpt/roles/test_researcher.py | 2 +- 5 files changed, 24 insertions(+), 24 deletions(-) diff --git a/metagpt/provider/google_gemini_api.py b/metagpt/provider/google_gemini_api.py index 5683095c7..f862e8084 100644 --- a/metagpt/provider/google_gemini_api.py +++ b/metagpt/provider/google_gemini_api.py @@ -58,7 +58,7 @@ class GeminiGPTAPI(BaseLLM): genai.configure(api_key=config.gemini_api_key) def _user_msg(self, msg: str) -> dict[str, str]: - # Not to change BaseGPTAPI default functions but update with Gemini's conversation format. + # Not to change BaseLLM default functions but update with Gemini's conversation format. # You should follow the format. return {"role": "user", "parts": [msg]} diff --git a/metagpt/strategy/tot.py b/metagpt/strategy/tot.py index 7f080fa69..a32cfdf40 100644 --- a/metagpt/strategy/tot.py +++ b/metagpt/strategy/tot.py @@ -9,7 +9,7 @@ from pydantic import BaseModel, Field from metagpt.llm import LLM from metagpt.logs import logger -from metagpt.provider.base_gpt_api import BaseGPTAPI +from metagpt.provider.base_llm import BaseLLM from metagpt.strategy.base import ThoughtNode, ThoughtTree from metagpt.strategy.tot_schema import MethodSelect, Strategy, ThoughtSolverConfig from metagpt.utils.common import CodeParser @@ -30,7 +30,7 @@ Output a list of jsons following the format: class ThoughtSolverBase(BaseModel): thought_tree: str = "" - llm: BaseGPTAPI = Field(default_factory=LLM, exclude=True) + llm: BaseLLM = Field(default_factory=LLM, exclude=True) config: ThoughtSolverConfig = Field(default_factory=ThoughtSolverConfig) def __init__(self, **kwargs: Any): diff --git a/tests/metagpt/actions/test_research.py b/tests/metagpt/actions/test_research.py index aeab99e87..06c5860de 100644 --- a/tests/metagpt/actions/test_research.py +++ b/tests/metagpt/actions/test_research.py @@ -32,7 +32,7 @@ async def test_collect_links(mocker): elif "sort the remaining search results" in prompt: return "[1,2]" - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) resp = await research.CollectLinks().run("The application of MetaGPT") for i in ["MetaGPT use cases", "The roadmap of MetaGPT", "The function of MetaGPT", "What llm MetaGPT support"]: assert i in resp @@ -51,7 +51,7 @@ async def test_collect_links_with_rank_func(mocker): rank_after.append(results) return results - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_collect_links_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_collect_links_llm_ask) resp = await research.CollectLinks(rank_func=rank_func).run("The application of MetaGPT") for x, y, z in zip(rank_before, rank_after, resp.values()): assert x[::-1] == y @@ -63,7 +63,7 @@ async def test_web_browse_and_summarize(mocker): async def mock_llm_ask(*args, **kwargs): return "metagpt" - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) url = "https://github.com/geekan/MetaGPT" url2 = "https://github.com/trending" query = "What's new in metagpt" @@ -79,7 +79,7 @@ async def test_web_browse_and_summarize(mocker): async def mock_llm_ask(*args, **kwargs): return "Not relevant." - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) resp = await research.WebBrowseAndSummarize().run(url, query=query) assert len(resp) == 1 @@ -96,7 +96,7 @@ async def test_conduct_research(mocker): data = f"# Research Report\n## Introduction\n{args} {kwargs}" return data - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) content = ( "MetaGPT takes a one line requirement as input and " "outputs user stories / competitive analysis / requirements / data structures / APIs / documents, etc." diff --git a/tests/metagpt/provider/test_base_gpt_api.py b/tests/metagpt/provider/test_base_gpt_api.py index be2c0ea7a..3443b5078 100644 --- a/tests/metagpt/provider/test_base_gpt_api.py +++ b/tests/metagpt/provider/test_base_gpt_api.py @@ -3,7 +3,7 @@ """ @Time : 2023/5/7 17:40 @Author : alexanderwu -@File : test_base_gpt_api.py +@File : test_base_llm.py """ import pytest @@ -27,7 +27,7 @@ prompt_msg = "who are you" resp_content = default_chat_resp["choices"][0]["message"]["content"] -class MockBaseGPTAPI(BaseLLM): +class MockBaseLLM(BaseLLM): def completion(self, messages: list[dict], timeout=3): return default_chat_resp @@ -41,12 +41,12 @@ class MockBaseGPTAPI(BaseLLM): return default_chat_resp -def test_base_gpt_api(): +def test_base_llm(): message = Message(role="user", content="hello") assert "role" in message.to_dict() assert "user" in str(message) - base_gpt_api = MockBaseGPTAPI() + base_llm = MockBaseLLM() openai_funccall_resp = { "choices": [ @@ -70,37 +70,37 @@ def test_base_gpt_api(): } ] } - func: dict = base_gpt_api.get_choice_function(openai_funccall_resp) + func: dict = base_llm.get_choice_function(openai_funccall_resp) assert func == { "name": "execute", "arguments": '{\n "language": "python",\n "code": "print(\'Hello, World!\')"\n}', } - func_args: dict = base_gpt_api.get_choice_function_arguments(openai_funccall_resp) + func_args: dict = base_llm.get_choice_function_arguments(openai_funccall_resp) assert func_args == {"language": "python", "code": "print('Hello, World!')"} - choice_text = base_gpt_api.get_choice_text(openai_funccall_resp) + choice_text = base_llm.get_choice_text(openai_funccall_resp) assert choice_text == openai_funccall_resp["choices"][0]["message"]["content"] - # resp = base_gpt_api.ask(prompt_msg) + # resp = base_llm.ask(prompt_msg) # assert resp == resp_content - # resp = base_gpt_api.ask_batch([prompt_msg]) + # resp = base_llm.ask_batch([prompt_msg]) # assert resp == resp_content - # resp = base_gpt_api.ask_code([prompt_msg]) + # resp = base_llm.ask_code([prompt_msg]) # assert resp == resp_content @pytest.mark.asyncio -async def test_async_base_gpt_api(): - base_gpt_api = MockBaseGPTAPI() +async def test_async_base_llm(): + base_llm = MockBaseLLM() - resp = await base_gpt_api.aask(prompt_msg) + resp = await base_llm.aask(prompt_msg) assert resp == resp_content - resp = await base_gpt_api.aask_batch([prompt_msg]) + resp = await base_llm.aask_batch([prompt_msg]) assert resp == resp_content - resp = await base_gpt_api.aask_code([prompt_msg]) + resp = await base_llm.aask_code([prompt_msg]) assert resp == resp_content diff --git a/tests/metagpt/roles/test_researcher.py b/tests/metagpt/roles/test_researcher.py index 83e90de66..a1d731d0c 100644 --- a/tests/metagpt/roles/test_researcher.py +++ b/tests/metagpt/roles/test_researcher.py @@ -28,7 +28,7 @@ async def mock_llm_ask(self, prompt: str, system_msgs): async def test_researcher(mocker): with TemporaryDirectory() as dirname: topic = "dataiku vs. datarobot" - mocker.patch("metagpt.provider.base_gpt_api.BaseGPTAPI.aask", mock_llm_ask) + mocker.patch("metagpt.provider.base_llm.BaseLLM.aask", mock_llm_ask) researcher.RESEARCH_PATH = Path(dirname) await researcher.Researcher().run(topic) assert (researcher.RESEARCH_PATH / f"{topic}.md").read_text().startswith("# Research Report") From f861d4be1f9195128012fe7b4be06dc4d89e8834 Mon Sep 17 00:00:00 2001 From: voidking Date: Thu, 28 Dec 2023 17:37:56 +0800 Subject: [PATCH 4/5] bugfix: mermaid unittest --- tests/metagpt/utils/test_mermaid.py | 26 ++++++++++++++------------ 1 file changed, 14 insertions(+), 12 deletions(-) diff --git a/tests/metagpt/utils/test_mermaid.py b/tests/metagpt/utils/test_mermaid.py index 912453aaf..b7b97a3f1 100644 --- a/tests/metagpt/utils/test_mermaid.py +++ b/tests/metagpt/utils/test_mermaid.py @@ -10,29 +10,31 @@ import pytest from metagpt.config import CONFIG from metagpt.utils.common import check_cmd_exists -from metagpt.utils.mermaid import MMC1, MMC2, mermaid_to_file +from metagpt.utils.mermaid import MMC1, mermaid_to_file @pytest.mark.asyncio -@pytest.mark.parametrize("engine", ["nodejs", "playwright", "pyppeteer", "ink"]) +@pytest.mark.parametrize("engine", ["nodejs", "ink"]) # TODO: playwright and pyppeteer async def test_mermaid(engine): - # Prerequisites - # npm install -g @mermaid-js/mermaid-cli + # nodejs prerequisites: npm install -g @mermaid-js/mermaid-cli + # ink prerequisites: connected to internet + # playwright prerequisites: playwright install --with-deps chromium assert check_cmd_exists("npm") == 0 assert CONFIG.PYPPETEER_EXECUTABLE_PATH CONFIG.mermaid_engine = engine save_to = CONFIG.git_repo.workdir / f"{CONFIG.mermaid_engine}/1" await mermaid_to_file(MMC1, save_to) - for ext in [".pdf", ".svg", ".png"]: - assert save_to.with_suffix(ext).exists() - save_to.with_suffix(ext).unlink(missing_ok=True) - save_to = CONFIG.git_repo.workdir / f"{CONFIG.mermaid_engine}/2" - await mermaid_to_file(MMC2, save_to) - for ext in [".pdf", ".svg", ".png"]: - assert save_to.with_suffix(ext).exists() - save_to.with_suffix(ext).unlink(missing_ok=True) + # ink does not support pdf + if engine == "ink": + for ext in [".svg", ".png"]: + assert save_to.with_suffix(ext).exists() + save_to.with_suffix(ext).unlink(missing_ok=True) + else: + for ext in [".pdf", ".svg", ".png"]: + assert save_to.with_suffix(ext).exists() + save_to.with_suffix(ext).unlink(missing_ok=True) if __name__ == "__main__": From 884bac758a431202632d41526bb379184727c19c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Thu, 28 Dec 2023 22:20:48 +0800 Subject: [PATCH 5/5] feat: +unit test --- .gitignore | 1 + tests/metagpt/roles/test_assistant.py | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/.gitignore b/.gitignore index 67c2fa316..05158cca2 100644 --- a/.gitignore +++ b/.gitignore @@ -167,3 +167,4 @@ tmp.png .dependencies.json tests/metagpt/utils/file_repo_git *.tmp +*.png diff --git a/tests/metagpt/roles/test_assistant.py b/tests/metagpt/roles/test_assistant.py index 164aba5dc..4d426ff45 100644 --- a/tests/metagpt/roles/test_assistant.py +++ b/tests/metagpt/roles/test_assistant.py @@ -36,7 +36,7 @@ async def test_run(): { "content": "who is tulin", "role": "user", - "id": 1, + "id": "1", }, {"content": "The one who eaten a poison apple.", "role": "assistant"}, ], @@ -53,7 +53,7 @@ async def test_run(): { "content": "can you draw me an picture?", "role": "user", - "id": 1, + "id": "1", }, {"content": "Yes, of course. What do you want me to draw", "role": "assistant"}, ],