diff --git a/tests/test_app.py b/tests/test_app.py new file mode 100644 index 0000000..eb4d9b9 --- /dev/null +++ b/tests/test_app.py @@ -0,0 +1,161 @@ +import pytest +import asyncio +from fastapi import FastAPI +from asyncio import Queue, Event +from agentic_security.core.app import create_app, get_tools_inbox, get_stop_event, get_current_run, set_current_run + +class TestApp: + """Test suite for agentic_security.core.app module.""" + + def test_create_app(self): + """Test that create_app returns a FastAPI instance.""" + app = create_app() + assert isinstance(app, FastAPI) + + @pytest.mark.asyncio + async def test_get_tools_inbox(self): + """Test that get_tools_inbox returns the global Queue instance.""" + queue1 = get_tools_inbox() + await queue1.put("test item") + queue2 = get_tools_inbox() + result = queue2.get_nowait() + assert result == "test item" + + def test_get_stop_event(self): + """Test that get_stop_event returns the global Event instance and is not set initially.""" + event = get_stop_event() + assert isinstance(event, Event) + assert not event.is_set() + + def test_current_run_initial(self): + """Test that get_current_run returns the global current_run with default values initially.""" + run = get_current_run() + # Default values should be empty strings + assert run["spec"] == "" + assert run["id"] == "" + + def test_set_current_run(self): + """Test that set_current_run correctly updates current_run.""" + spec = "test run" + result = set_current_run(spec) + expected_id = hash(id(spec)) + # Verify that spec is set correctly + assert result["spec"] == spec + assert result["id"] == expected_id + + def test_current_run_after_set(self): + """Test that get_current_run returns the updated current_run after set_current_run is called.""" + spec = "another test run" + set_current_run(spec) + current = get_current_run() + assert current["spec"] == spec + assert current["id"] == hash(id(spec)) + def test_tools_inbox_same_instance(self): + """Test that get_tools_inbox returns the same Queue instance by default.""" + queue1 = get_tools_inbox() + queue2 = get_tools_inbox() + assert queue1 is queue2 + + def test_stop_event_set(self): + """Test that setting the stop event is reflected in subsequent calls.""" + event = get_stop_event() + event.set() # set the global event + # Now, subsequent calls should return the same event which is set. + event2 = get_stop_event() + assert event2.is_set() + + def test_set_current_run_with_none(self): + """Test that set_current_run handles None as a valid input and updates current_run accordingly.""" + result = set_current_run(None) + expected_id = hash(id(None)) + assert result["spec"] is None + assert result["id"] == expected_id + + def test_multiple_current_run_assignments(self): + """Test multiple assignments to current_run to ensure it always updates correctly.""" + first_spec = "first run" + result1 = set_current_run(first_spec) + expected_id1 = hash(id(first_spec)) + assert result1["spec"] == first_spec + assert result1["id"] == expected_id1 + + second_spec = "second run" + result2 = set_current_run(second_spec) + expected_id2 = hash(id(second_spec)) + assert result2["spec"] == second_spec + assert result2["id"] == expected_id2 + + current = get_current_run() + # The current_run should reflect the latest assignment. + assert current["spec"] == second_spec + assert current["id"] == expected_id2 + @pytest.mark.asyncio + async def test_empty_tools_inbox_exception(self): + """Test that calling get_nowait on an empty tools_inbox raises QueueEmpty.""" + from asyncio import QueueEmpty + queue = get_tools_inbox() + # Clear any existing items in the queue + while True: + try: + queue.get_nowait() + except QueueEmpty: + break + with pytest.raises(QueueEmpty): + queue.get_nowait() + + def test_set_current_run_with_dict(self): + """Test that set_current_run correctly handles a dictionary input as spec.""" + spec = {"key": "value"} + result = set_current_run(spec) + expected_id = hash(id(spec)) + assert result["spec"] == spec + assert result["id"] == expected_id + @pytest.mark.asyncio + async def test_stop_event_wait(self): + """Test that waiting on the stop event returns once the event is set.""" + event = get_stop_event() + event.clear() # ensure event is not set + async def waiter(): + await event.wait() + return True + waiter_task = asyncio.create_task(waiter()) + # Wait a moment to ensure the waiter is pending + await asyncio.sleep(0.1) + assert not waiter_task.done() + event.set() + result = await waiter_task + assert result is True + + def test_set_current_run_with_int(self): + """Test that set_current_run handles an integer input as spec.""" + spec = 12345 + result = set_current_run(spec) + expected_id = hash(id(spec)) + assert result["spec"] == spec + assert result["id"] == expected_id + + def test_create_app_routes(self): + """Test that create_app returns a FastAPI instance with default routes available.""" + app = create_app() + paths = [route.path for route in app.routes] + # Check that the default OpenAPI route exists + assert "/openapi.json" in paths + + @pytest.mark.asyncio + async def test_tools_inbox_async_put_get_order(self): + """Test that tools_inbox preserves order when items are added and retrieved asynchronously.""" + queue = get_tools_inbox() + # Clear any existing items in the queue + from asyncio import QueueEmpty + while True: + try: + queue.get_nowait() + except QueueEmpty: + break + items = ["first", "second", "third"] + for item in items: + await queue.put(item) + result_items = [] + for _ in items: + result_items.append(await queue.get()) + assert result_items == items \ No newline at end of file