From aa236775bab6b8c2a9ec5e2e7f3f28cc4e75f70e Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 21 Mar 2025 15:26:46 +0100 Subject: [PATCH 01/10] tests: add some tests to test the methods of Haiku Refs: OPS-70 --- tests/test_tests.py | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tests/test_tests.py b/tests/test_tests.py index 58ab3fa..eac199e 100644 --- a/tests/test_tests.py +++ b/tests/test_tests.py @@ -4,12 +4,14 @@ from __future__ import annotations import os +import json # do not remove this import. This is needed for # pytest fixtures to work import pytest # noqa: F401 -import senju # noqa: F401 +import senju +from senju.haiku import Haiku # noqa: F401 # Note: these weird arguments are an indicator of what should be dome # before. For example, `temp_data_dir` is a function in `conftest.py`. If we @@ -28,3 +30,20 @@ def test_temp_data_dir(temp_data_dir): with open(testpath, "w") as f: f.write("that dir actually works") os.remove(testpath) + + +def test_create_haiku(): + haiku = Haiku(["line number 1", "line number 2", "line number 3"]) + print(haiku) + + +def test_get_haiku_json(): + haiku = Haiku(["line number 1", "line number 2", "line number 3"]) + data_raw: str = haiku.get_json() + data = json.loads(data_raw) + print(data) + + +def test_request_haiku(): + haiku = Haiku.request_haiku("apple banana papaya") + print(haiku) From 897bf80e38be5d94a7b88613dc923f3ea8099bbe Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 21 Mar 2025 16:27:34 +0100 Subject: [PATCH 02/10] tests: add test for request_haiku Refs: OPS-70 --- poetry.lock | 25 ++++++++++++++++++++----- pyproject.toml | 1 + senju/haiku.py | 4 ++-- tests/{test_tests.py => test_haiku.py} | 15 +++++++++++++-- 4 files changed, 36 insertions(+), 9 deletions(-) rename tests/{test_tests.py => test_haiku.py} (70%) diff --git a/poetry.lock b/poetry.lock index 0bd0101..33ca411 100644 --- a/poetry.lock +++ b/poetry.lock @@ -1,4 +1,4 @@ -# This file is automatically @generated by Poetry 2.1.1 and should not be changed by hand. +# This file is automatically @generated by Poetry 2.0.1 and should not be changed by hand. [[package]] name = "alabaster" @@ -25,7 +25,7 @@ files = [ ] [package.extras] -dev = ["backports.zoneinfo ; python_version < \"3.9\"", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata ; sys_platform == \"win32\""] +dev = ["backports.zoneinfo", "freezegun (>=1.0,<2.0)", "jinja2 (>=3.0)", "pytest (>=6.0)", "pytest-cov", "pytz", "setuptools", "tzdata"] [[package]] name = "blinker" @@ -255,7 +255,7 @@ files = [ ] [package.extras] -toml = ["tomli ; python_full_version <= \"3.11.0a6\""] +toml = ["tomli"] [[package]] name = "docutils" @@ -514,6 +514,21 @@ tomli = {version = ">=1", markers = "python_version < \"3.11\""} [package.extras] dev = ["argcomplete", "attrs (>=19.2)", "hypothesis (>=3.56)", "mock", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] +[[package]] +name = "pytest-httpserver" +version = "1.1.2" +description = "pytest-httpserver is a httpserver for pytest" +optional = false +python-versions = ">=3.9" +groups = ["main"] +files = [ + {file = "pytest_httpserver-1.1.2-py3-none-any.whl", hash = "sha256:93009d79574fc982301e8494fdea0884f21bb0caf3bcc719151dfbd1e3a943ea"}, + {file = "pytest_httpserver-1.1.2.tar.gz", hash = "sha256:38d0b726580d05c47cbd0ced1ecb36a51668ef1596cdc6d70a9cfa2b3cc00ebd"}, +] + +[package.dependencies] +Werkzeug = ">=2.0.0" + [[package]] name = "requests" version = "2.32.3" @@ -752,7 +767,7 @@ files = [ ] [package.extras] -brotli = ["brotli (>=1.0.9) ; platform_python_implementation == \"CPython\"", "brotlicffi (>=0.8.0) ; platform_python_implementation != \"CPython\""] +brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] h2 = ["h2 (>=4,<5)"] socks = ["pysocks (>=1.5.6,!=1.5.7,<2.0)"] zstd = ["zstandard (>=0.18.0)"] @@ -778,4 +793,4 @@ watchdog = ["watchdog (>=2.3)"] [metadata] lock-version = "2.1" python-versions = ">=3.10" -content-hash = "ce9cac7092447dc5d6b3920853bb10fcc166018bc4f48c50925ef09db74e7891" +content-hash = "76f74a5c2443d5c8e9db9433f0bdcc94a1d12c469f8b49dd325f3cc5a1b06f4e" diff --git a/pyproject.toml b/pyproject.toml index ec97022..688e449 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -12,6 +12,7 @@ dependencies = [ "tinydb (>=3.1.0,<4.0.0)", "requests (>=2.32.3,<3.0.0)", "coverage (>=7.6.12,<8.0.0)", + "pytest-httpserver (>=1.1.2,<2.0.0)", ] diff --git a/senju/haiku.py b/senju/haiku.py index f117a5f..b86951c 100644 --- a/senju/haiku.py +++ b/senju/haiku.py @@ -18,7 +18,7 @@ class Haiku: return json.dumps(self.lines) @staticmethod - def request_haiku(seed: str) -> Haiku: + def request_haiku(seed: str, url=AI_BASE_URL + AI_GEN_ENDPOINT) -> Haiku: """This function prompts the ai to generate the hauku based on the user input""" @@ -31,7 +31,7 @@ class Haiku: while True: try: - r = requests.post(url=AI_BASE_URL + AI_GEN_ENDPOINT, + r = requests.post(url=url, json=ai_gen_request) ai_response = str(r.json()["response"]) diff --git a/tests/test_tests.py b/tests/test_haiku.py similarity index 70% rename from tests/test_tests.py rename to tests/test_haiku.py index eac199e..d959278 100644 --- a/tests/test_tests.py +++ b/tests/test_haiku.py @@ -5,6 +5,8 @@ from __future__ import annotations import os import json +import logging +from pytest_httpserver import HTTPServer # do not remove this import. This is needed for # pytest fixtures to work @@ -44,6 +46,15 @@ def test_get_haiku_json(): print(data) -def test_request_haiku(): - haiku = Haiku.request_haiku("apple banana papaya") +def test_request_haiku(httpserver: HTTPServer): + + httpserver.expect_request( + "/testhaiku").respond_with_json({"response": + "The apparition of these\n" + "faces in a crowd; Petal\n" + "on a wet, black bough." + }) + + haiku = Haiku.request_haiku( + "apple banana papaya", url=httpserver.url_for("/testhaiku")) print(haiku) From 3889809d07cc7a53bf2a71ce8583a2258a18e546 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 21 Mar 2025 16:44:49 +0100 Subject: [PATCH 03/10] fix: request_haiku method did not work with a simple 3 line haiku Refs: OPS-70 --- senju/haiku.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/senju/haiku.py b/senju/haiku.py index b86951c..969eec0 100644 --- a/senju/haiku.py +++ b/senju/haiku.py @@ -29,23 +29,32 @@ class Haiku: "eval_count": 20 } + tries = 0 while True: + tries += 1 try: r = requests.post(url=url, json=ai_gen_request) ai_response = str(r.json()["response"]) - logging.warning(ai_response) + logging.debug(f"ai response: {ai_response}") lines = ai_response.split("\n") while len(lines) != 3: lines.pop() - logging.warning(lines) + logging.info(f"lines for haiku: {lines}") - if len(lines) != 3: - continue + if len(lines) < 3: + if tries < 20: + logging.warning(f"too few lines, trying again") + logging.debug(lines) + continue + else: + logging.warning(f"too many tries, aborting") + raise Exception( + "Generating the haiku took too many tries") haiku = Haiku( [ @@ -55,8 +64,9 @@ class Haiku: ]) break - except json.JSONDecodeError: - continue + except json.JSONDecodeError as e: + logging.error(f"error while reading json from LLM: {e}") + raise e return haiku From df6e964751b8de6a9fe3d94761ef18027ddbb1d5 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Fri, 21 Mar 2025 16:47:47 +0100 Subject: [PATCH 04/10] chore: fix flake8 warnings in haiku and test_haiku Refs: OPS-70 --- senju/haiku.py | 4 ++-- tests/test_haiku.py | 2 -- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/senju/haiku.py b/senju/haiku.py index 969eec0..4854319 100644 --- a/senju/haiku.py +++ b/senju/haiku.py @@ -48,11 +48,11 @@ class Haiku: if len(lines) < 3: if tries < 20: - logging.warning(f"too few lines, trying again") + logging.warning("too few lines, trying again") logging.debug(lines) continue else: - logging.warning(f"too many tries, aborting") + logging.warning("too many tries, aborting") raise Exception( "Generating the haiku took too many tries") diff --git a/tests/test_haiku.py b/tests/test_haiku.py index d959278..3bd9644 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -5,14 +5,12 @@ from __future__ import annotations import os import json -import logging from pytest_httpserver import HTTPServer # do not remove this import. This is needed for # pytest fixtures to work import pytest # noqa: F401 -import senju from senju.haiku import Haiku # noqa: F401 # Note: these weird arguments are an indicator of what should be dome From 4887047cbfe4e61340fe274eab87f56a6f7cda8c Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:26:31 +0100 Subject: [PATCH 05/10] test: add asserts to the haiku tests Refs: OPS-70 OPS-81 --- tests/test_haiku.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/tests/test_haiku.py b/tests/test_haiku.py index 3bd9644..ac6f063 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -30,18 +30,27 @@ def test_temp_data_dir(temp_data_dir): with open(testpath, "w") as f: f.write("that dir actually works") os.remove(testpath) + assert not os.path.exists(testpath) def test_create_haiku(): haiku = Haiku(["line number 1", "line number 2", "line number 3"]) - print(haiku) + assert haiku.lines[0] == "line number 1" + assert haiku.lines[1] == "line number 2" + assert haiku.lines[2] == "line number 3" + assert len(haiku.lines) == 3 def test_get_haiku_json(): haiku = Haiku(["line number 1", "line number 2", "line number 3"]) data_raw: str = haiku.get_json() + assert data_raw == '["line number 1", "line number 2", "line number 3"]' data = json.loads(data_raw) - print(data) + assert haiku.lines[0] == "line number 1" + assert haiku.lines[1] == "line number 2" + assert haiku.lines[2] == "line number 3" + assert len(haiku.lines) == 3 + assert data == ['line number 1', 'line number 2', 'line number 3'] def test_request_haiku(httpserver: HTTPServer): @@ -49,10 +58,13 @@ def test_request_haiku(httpserver: HTTPServer): httpserver.expect_request( "/testhaiku").respond_with_json({"response": "The apparition of these\n" - "faces in a crowd; Petal\n" + "faces in a crowd; Petal\n" "on a wet, black bough." }) haiku = Haiku.request_haiku( "apple banana papaya", url=httpserver.url_for("/testhaiku")) - print(haiku) + assert haiku.lines[0] == "The apparition of these" + assert haiku.lines[1] == "faces in a crowd; Petal" + assert haiku.lines[2] == "on a wet, black bough." + assert len(haiku.lines) == 3 From 2fa911c6657ea2f402d136f8c0ca6b1a1ea2b728 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:31:46 +0100 Subject: [PATCH 06/10] test: add test_request_haiku_respondse_bad Refs: OPS-70 OPS-81 --- tests/test_haiku.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/test_haiku.py b/tests/test_haiku.py index ac6f063..b1a8f73 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -6,6 +6,7 @@ from __future__ import annotations import os import json from pytest_httpserver import HTTPServer +import requests # do not remove this import. This is needed for # pytest fixtures to work @@ -68,3 +69,13 @@ def test_request_haiku(httpserver: HTTPServer): assert haiku.lines[1] == "faces in a crowd; Petal" assert haiku.lines[2] == "on a wet, black bough." assert len(haiku.lines) == 3 + + +def test_request_haiku_respondse_bad(httpserver: HTTPServer): + with pytest.raises(requests.exceptions.JSONDecodeError): + + httpserver.expect_request( + "/testhaiku").respond_with_data("this is completely wrong" + ("A" * 50 + "\n") * 20) + + _haiku = Haiku.request_haiku( + "apple banana papaya", url=httpserver.url_for("/testhaiku")) From e700c0d4cbce909c0b62995fa742eb2fa1ffb7c1 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:32:37 +0100 Subject: [PATCH 07/10] chore: remove trivial test_tests_are_loaded test Refs: Ops-70 --- tests/test_haiku.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/test_haiku.py b/tests/test_haiku.py index b1a8f73..7735923 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -21,10 +21,6 @@ from senju.haiku import Haiku # noqa: F401 # Rust's libtest -def test_tests_are_loaded(): - assert True # if we make it here, they are - - def test_temp_data_dir(temp_data_dir): print(temp_data_dir) testpath = temp_data_dir / "__test" From 0c0554e9316b342e4140f7e66f225cfff548e0bb Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:33:15 +0100 Subject: [PATCH 08/10] test: move test_temp_data_dir to store tests Refs: OPS-70 --- tests/test_haiku.py | 10 ---------- tests/test_store.py | 10 ++++++++++ 2 files changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_haiku.py b/tests/test_haiku.py index 7735923..c1b03ed 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -3,7 +3,6 @@ from __future__ import annotations -import os import json from pytest_httpserver import HTTPServer import requests @@ -21,15 +20,6 @@ from senju.haiku import Haiku # noqa: F401 # Rust's libtest -def test_temp_data_dir(temp_data_dir): - print(temp_data_dir) - testpath = temp_data_dir / "__test" - with open(testpath, "w") as f: - f.write("that dir actually works") - os.remove(testpath) - assert not os.path.exists(testpath) - - def test_create_haiku(): haiku = Haiku(["line number 1", "line number 2", "line number 3"]) assert haiku.lines[0] == "line number 1" diff --git a/tests/test_store.py b/tests/test_store.py index ac8aac4..f4337c6 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -3,11 +3,21 @@ from __future__ import annotations import pytest # noqa: F401 +import os from senju.haiku import DEFAULT_HAIKU, Haiku from senju.store_manager import StoreManager # noqa: F401 +def test_temp_data_dir(temp_data_dir): + print(temp_data_dir) + testpath = temp_data_dir / "__test" + with open(testpath, "w") as f: + f.write("that dir actually works") + os.remove(testpath) + assert not os.path.exists(testpath) + + def test_save_and_load_any(store_manager: StoreManager): thing = { "color": "blue", From 08a7bd45eca0eb3c9202d1ad6979624e85107ca4 Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:35:20 +0100 Subject: [PATCH 09/10] chore: fix flake8 warnings in test_haiku.py Refs: OPS-70 OPS-81 --- tests/test_haiku.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/tests/test_haiku.py b/tests/test_haiku.py index c1b03ed..b970751 100644 --- a/tests/test_haiku.py +++ b/tests/test_haiku.py @@ -61,7 +61,8 @@ def test_request_haiku_respondse_bad(httpserver: HTTPServer): with pytest.raises(requests.exceptions.JSONDecodeError): httpserver.expect_request( - "/testhaiku").respond_with_data("this is completely wrong" + ("A" * 50 + "\n") * 20) + "/testhaiku").respond_with_data( + "this is completely wrong" + ("A" * 50 + "\n") * 20) - _haiku = Haiku.request_haiku( + Haiku.request_haiku( "apple banana papaya", url=httpserver.url_for("/testhaiku")) From 7df683f3fe307777a48656111dfab01cde96d43d Mon Sep 17 00:00:00 2001 From: "Christoph J. Scherr" Date: Sun, 23 Mar 2025 13:45:34 +0100 Subject: [PATCH 10/10] test: add more edgecase tests for the store manager Refs: OPS-70 OPS-82 --- tests/test_store.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/tests/test_store.py b/tests/test_store.py index f4337c6..05d03b1 100644 --- a/tests/test_store.py +++ b/tests/test_store.py @@ -68,3 +68,28 @@ def test_load_latest_or_default_with_non_empty(temp_data_dir): haiku = store.load_haiku(store.get_id_of_latest_haiku()) assert haiku != DEFAULT_HAIKU assert haiku == nonsense_test_haiku + + +def test_load_latest_with_non_empty_store(temp_data_dir): + store = StoreManager(temp_data_dir / "empty_store.json") + store.save_haiku(Haiku(["hello", "world", "bananenkrokodil"])) + h = store.get_id_of_latest_haiku() + assert h is not None + assert h > 0 + + +def test_create_store_with_bad_file(temp_data_dir): + with pytest.raises(Exception): + testpath = temp_data_dir / "non_empty.json" + with open(testpath, "w") as f: + f.write("BUT IT DOES NOT ACTUALLY HAVE JSON") + store = StoreManager(testpath) + store._save({"hello": 19}) + + +def test_create_store_with_non_empty(temp_data_dir): + testpath = temp_data_dir / "non_empty.json" + with open(testpath, "w") as f: + f.write('{"this": ["is","valid","json"]}') + store = StoreManager(testpath) + store._save({"hello": 19})