-
Notifications
You must be signed in to change notification settings - Fork 26
Pytest for various functions added #29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
staru09
wants to merge
6
commits into
Repello-AI:main
Choose a base branch
from
staru09:pytest
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 4 commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
9eb02f2
requirements updated
staru09 18f8a29
basic tests added
staru09 000da6f
all tests added
staru09 f0313e5
Merge branch 'main' of https://github.com/staru09/whistleblower into …
staru09 525d918
test suite updated
staru09 9c8acaf
repetitive requirements fix
staru09 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,4 @@ | ||
| __pycache__/ | ||
| flagged/ | ||
| /venv/ | ||
| /venv/ | ||
| .env |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| [pytest] | ||
| testpaths = tests | ||
| addopts = -ra |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,2 +1,7 @@ | ||
| openai==2.6.0 | ||
| gradio==5.49.1 | ||
| gradio==5.49.1 | ||
| openai | ||
| gradio | ||
| pytest | ||
| pytest-mock | ||
| pytest-cov | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| import json | ||
| import importlib | ||
| import sys | ||
| import gradio as gr | ||
| import pytest | ||
| import sys | ||
| from pathlib import Path | ||
|
|
||
| ROOT = str(Path(__file__).resolve().parents[1]) | ||
| if ROOT not in sys.path: | ||
| sys.path.insert(0, ROOT) | ||
|
|
||
| @pytest.fixture | ||
| def sample_request_body(): | ||
| return { | ||
| "prompt": "$INPUT", | ||
| "metadata": { | ||
| "language": "en", | ||
| "extras": ["a", "b"], | ||
| }, | ||
| } | ||
|
|
||
| @pytest.fixture | ||
| def sample_response_structure(): | ||
| return { | ||
| "data": { | ||
| "result": "$OUTPUT", | ||
| } | ||
| } | ||
|
|
||
| @pytest.fixture | ||
| def sample_response_payload(): | ||
| return { | ||
| "data": { | ||
| "result": "system prompt here", | ||
| } | ||
| } | ||
|
|
||
| @pytest.fixture | ||
| def request_body_json(sample_request_body): | ||
| return json.dumps(sample_request_body) | ||
|
|
||
| @pytest.fixture | ||
| def response_body_json(sample_response_structure): | ||
| return json.dumps(sample_response_structure) | ||
|
|
||
| @pytest.fixture | ||
| def ui_app(monkeypatch): | ||
| pytest.importorskip("core.whistleblower", reason="core.whistleblower module missing") | ||
| monkeypatch.setattr(gr.Blocks, "launch", lambda self: None) | ||
| module_name = "ui.app" | ||
| if module_name in sys.modules: | ||
| module = importlib.reload(sys.modules[module_name]) | ||
| else: | ||
| module = importlib.import_module(module_name) | ||
| return module |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| from types import SimpleNamespace | ||
| import pytest | ||
| from core.api import call_external_api | ||
|
|
||
| def test_call_external_api_replaces_placeholders(monkeypatch, sample_request_body, sample_response_structure, sample_response_payload): | ||
| captured = {} | ||
|
|
||
| def fake_post(url, json, headers): | ||
| captured["url"] = url | ||
| captured["json"] = json | ||
| captured["headers"] = headers | ||
| return SimpleNamespace(json=lambda: sample_response_payload) | ||
|
|
||
| monkeypatch.setattr("core.api.requests.post", fake_post) | ||
|
|
||
| output = call_external_api( | ||
| "https://api.example.com/chat", | ||
| "leak prompt", | ||
| sample_request_body, | ||
| sample_response_structure, | ||
| api_key="secret-token", | ||
| ) | ||
|
|
||
| assert captured["url"] == "https://api.example.com/chat" | ||
| assert captured["json"]["prompt"] == "leak prompt" | ||
| assert captured["headers"] == {"X-repello-api-key": "secret-token"} | ||
| assert output == "system prompt here" | ||
|
|
||
| def test_call_external_api_passes_through_when_no_key(monkeypatch, sample_request_body, sample_response_structure, sample_response_payload): | ||
| def fake_post(url, json, headers): | ||
| assert headers == {} | ||
| return SimpleNamespace(json=lambda: sample_response_payload) | ||
|
|
||
| monkeypatch.setattr("core.api.requests.post", fake_post) | ||
|
|
||
| output = call_external_api( | ||
| "https://api.example.com/chat", | ||
| "test prompt", | ||
| sample_request_body, | ||
| sample_response_structure, | ||
| ) | ||
|
|
||
| assert output == "system prompt here" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| import json | ||
| import sys | ||
| from pathlib import Path | ||
| import pytest | ||
|
|
||
| main = pytest.importorskip("main", reason="main module missing") | ||
| whistleblower = pytest.importorskip("core.whistleblower", reason="core.whistleblower module missing") | ||
|
|
||
| def test_read_json_file_valid(tmp_path): | ||
| data = {"api_url": "http://example.com"} | ||
| json_path = tmp_path / "config.json" | ||
| json_path.write_text(json.dumps(data)) | ||
|
|
||
| loaded = whistleblower.read_json_file(str(json_path)) | ||
|
|
||
| assert loaded == data | ||
|
|
||
| def test_read_json_file_invalid(tmp_path, capsys): | ||
| json_path = tmp_path / "invalid.json" | ||
| json_path.write_text("{invalid") | ||
|
|
||
| loaded = whistleblower.read_json_file(str(json_path)) | ||
|
|
||
| assert loaded == {} | ||
|
|
||
| def test_main_invokes_whistleblower(monkeypatch, tmp_path): | ||
| config_path = tmp_path / "input.json" | ||
| config_path.write_text( | ||
| json.dumps( | ||
| { | ||
| "api_url": "http://example.com", | ||
| "request_body": {"prompt": "$INPUT"}, | ||
| "response_body": {"response": "$OUTPUT"}, | ||
| "OpenAI_api_key": "key", | ||
| "model": "gpt-4", | ||
| } | ||
| ) | ||
| ) | ||
|
|
||
| called = {} | ||
|
|
||
| def fake_whistleblower(args): | ||
| called["json_file"] = args.json_file | ||
| return "done" | ||
|
|
||
| monkeypatch.setattr(main, "whistleblower", fake_whistleblower) | ||
| monkeypatch.setattr(sys, "argv", ["main.py", "--json_file", str(config_path)]) | ||
|
|
||
| result = main.main() | ||
|
|
||
| assert called["json_file"] == str(config_path) | ||
| assert result == "done" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,153 @@ | ||
| import json | ||
|
|
||
| import gradio as gr | ||
| import pytest | ||
|
|
||
|
|
||
| def test_check_for_placeholders(ui_app, request_body_json): | ||
| assert ui_app.check_for_placeholders(request_body_json, "$INPUT") | ||
| assert not ui_app.check_for_placeholders({"prompt": "hello"}, "$INPUT") | ||
|
|
||
|
|
||
| def test_check_for_placeholders_list(ui_app): | ||
| payload = json.dumps({"messages": [{"text": "$INPUT"}]}) | ||
|
|
||
| assert ui_app.check_for_placeholders(payload, "$INPUT") | ||
|
|
||
|
|
||
| def test_validate_input_json_success(ui_app, request_body_json, response_body_json, monkeypatch): | ||
| captured = {} | ||
|
|
||
| def fake_generate_output(*args): | ||
| captured["args"] = args | ||
| return "result-json" | ||
|
|
||
| monkeypatch.setattr(ui_app, "generate_output", fake_generate_output) | ||
|
|
||
| output = ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "api-key", | ||
| "JSON", | ||
| "", | ||
| request_body_json, | ||
| "", | ||
| response_body_json, | ||
| "openai-key", | ||
| "gpt-4", | ||
| ) | ||
|
|
||
| assert output == "result-json" | ||
| assert captured["args"][0] == "https://api.example.com" | ||
| assert json.loads(captured["args"][2])["prompt"] == "$INPUT" | ||
|
|
||
|
|
||
| def test_validate_input_json_empty_request_raises(ui_app): | ||
| with pytest.raises(gr.Error) as excinfo: | ||
| ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "JSON", | ||
| "", | ||
| "", | ||
| "", | ||
| "{}", | ||
| "", | ||
| "gpt-4o", | ||
| ) | ||
|
|
||
| assert "Request body cannot be empty" in str(excinfo.value) | ||
|
|
||
|
|
||
| def test_validate_input_json_invalid_request_raises(ui_app): | ||
| with pytest.raises(gr.Error) as excinfo: | ||
| ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "JSON", | ||
| "", | ||
| "{bad json", | ||
| "", | ||
| '{"response": "$OUTPUT"}', | ||
| "", | ||
| "gpt-4o", | ||
| ) | ||
|
|
||
| assert "Invalid JSON format in request body." in str(excinfo.value) | ||
|
|
||
|
|
||
| def test_validate_input_json_missing_output_placeholder_raises(ui_app): | ||
| with pytest.raises(gr.Error) as excinfo: | ||
| ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "JSON", | ||
| "", | ||
| '{"prompt": "$INPUT"}', | ||
| "", | ||
| '{"response": "missing"}', | ||
| "", | ||
| "gpt-4o", | ||
| ) | ||
|
|
||
| assert "Response body must contain the $OUTPUT placeholder." in str(excinfo.value) | ||
|
|
||
|
|
||
| def test_validate_input_key_value_success(ui_app, monkeypatch): | ||
| captured = {} | ||
|
|
||
| def fake_generate_output(*args): | ||
| captured["args"] = args | ||
| return "result-kv" | ||
|
|
||
| monkeypatch.setattr(ui_app, "generate_output", fake_generate_output) | ||
|
|
||
| output = ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "Key-Value", | ||
| "prompt: $INPUT\nstatic: value", | ||
| "", | ||
| "response: $OUTPUT", | ||
| "", | ||
| "service-key", | ||
| "gpt-4o", | ||
| ) | ||
|
|
||
| assert output == "result-kv" | ||
| request_dict = captured["args"][2] | ||
| assert request_dict["prompt"] == "$INPUT" | ||
| assert request_dict["static"] == "value" | ||
| response_dict = captured["args"][3] | ||
| assert response_dict["response"] == "$OUTPUT" | ||
|
|
||
|
|
||
| def test_validate_input_key_value_empty_fields_raise(ui_app): | ||
| with pytest.raises(gr.Error) as excinfo: | ||
| ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "Key-Value", | ||
| "", | ||
| "", | ||
| "response: $OUTPUT", | ||
| "", | ||
| "openai-key", | ||
| "gpt-4o", | ||
| ) | ||
|
|
||
| assert "Request body cannot be empty." in str(excinfo.value) | ||
|
|
||
|
|
||
| def test_validate_input_missing_placeholder_raises(ui_app): | ||
| with pytest.raises(gr.Error): | ||
| ui_app.validate_input( | ||
| "https://api.example.com", | ||
| "", | ||
| "JSON", | ||
| "", | ||
| '{"prompt": "no placeholder"}', | ||
| "", | ||
| '{"response": "$OUTPUT"}', | ||
| "", | ||
| "gpt-4o", | ||
| ) |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,46 @@ | ||
| from copy import deepcopy | ||
|
|
||
| from core.utils import extract_nested_value, replace_nested_value | ||
|
|
||
|
|
||
| def test_extract_nested_value_success(sample_response_payload, sample_response_structure): | ||
| value = extract_nested_value(sample_response_payload, sample_response_structure, "$OUTPUT") | ||
| assert value == "system prompt here" | ||
|
|
||
|
|
||
| def test_extract_nested_value_handles_list_structure(): | ||
| payload = {"items": [{"value": "skip"}, {"value": "system prompt here"}]} | ||
| structure = {"items": [{}, {"value": "$OUTPUT"}]} | ||
|
|
||
| value = extract_nested_value(payload, structure, "$OUTPUT") | ||
|
|
||
| assert value == "system prompt here" | ||
|
|
||
|
|
||
| def test_extract_nested_value_missing(sample_response_payload): | ||
| structure_without_placeholder = {"data": {"result": "not-a-placeholder"}} | ||
| value = extract_nested_value(sample_response_payload, structure_without_placeholder, "$OUTPUT") | ||
| assert value is None | ||
|
|
||
|
|
||
| def test_extract_nested_value_returns_none_for_none_payload(): | ||
| payload = {"data": {"result": None}} | ||
| structure = {"data": {"result": "$OUTPUT"}} | ||
|
|
||
| assert extract_nested_value(payload, structure, "$OUTPUT") is None | ||
|
|
||
|
|
||
| def test_replace_nested_value_updates_only_placeholder(sample_request_body): | ||
| updated = replace_nested_value(deepcopy(sample_request_body), "$INPUT", "example message") | ||
| assert updated["prompt"] == "example message" | ||
| assert updated["metadata"]["language"] == "en" | ||
|
|
||
|
|
||
| def test_replace_nested_value_updates_nested_lists(): | ||
| data = {"items": ["$INPUT", {"nested": ["keep", "$INPUT"]}]} | ||
|
|
||
| updated = replace_nested_value(deepcopy(data), "$INPUT", "filled") | ||
|
|
||
| assert updated["items"][0] == "filled" | ||
| assert updated["items"][1]["nested"][1] == "filled" | ||
| assert updated["items"][1]["nested"][0] == "keep" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.