bug? timeout when asyncio tests for FastAPI backend. #4959

Closed
opened 2026-02-16 17:47:05 -05:00 by yindo · 0 comments
Owner

Originally created by @mizaibear on GitHub (Jan 12, 2026).

Originally assigned to: @rekram1-node on GitHub.

Description

when opencode starts a pytest by itself ,a pytest with pytest-asyncio for FastAPI backend.
tests actually run for 0.23seconds
but , process continue to run until <bash_metadata> show up and it takes 120seconds real-time to timeout.
when opencode running ,not in the shell.
i try another project's tests without asyncio, it doesn't wait to timeout.
is it a bug?

$ cd backend && uv run pytest -v
============================= test session starts =============================
platform win32 -- Python 3.13.9, pytest-9.0.2, pluggy-1.6.0 -- D:\workspace\my_projects\bearpos\backend.venv\Scripts\python.exe
cachedir: .pytest_cache
rootdir: D:\workspace\my_projects\bearpos\backend
configfile: pytest.ini
testpaths: tests
plugins: anyio-4.12.1, asyncio-1.3.0, cov-7.0.0
asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function
collecting ... collected 14 items
tests/test_accounts.py::TestCreateAccount::test_create_account_with_required_fields_only PASSED [ 7%]
tests/test_accounts.py::TestCreateAccount::test_create_account_with_all_fields PASSED [ 14%]
tests/test_accounts.py::TestCreateAccount::test_create_account_with_empty_name PASSED [ 21%]
tests/test_accounts.py::TestCreateAccount::test_create_account_with_whitespace_name PASSED [ 28%]
tests/test_accounts.py::TestCreateAccount::test_update_account_duplicate_name PASSED [ 35%]
tests/test_accounts.py::TestCreateAccount::test_update_account_tags_not_array PASSED [ 42%]
tests/test_accounts.py::TestCreateAccount::test_update_account_name_trim_whitespace PASSED [ 50%]
tests/test_accounts.py::TestCreateAccount::test_update_account_not_found PASSED [ 57%]
tests/test_accounts.py::TestCreateAccount::test_update_account_empty_body PASSED [ 64%]
tests/test_accounts.py::TestArchiveAccount::test_archive_account_success PASSED [ 71%]
tests/test_accounts.py::TestArchiveAccount::test_archive_account_already_archived PASSED [ 78%]
tests/test_accounts.py::TestArchiveAccount::test_archive_account_not_found PASSED [ 85%]
tests/test_accounts.py::TestIntegration::test_complete_crud_workflow PASSED [ 92%]
tests/test_accounts.py::TestIntegration::test_unique_constraint PASSED [100%]
============================= 14 passed in 0.23s ==============================
<bash_metadata>
bash tool terminated command after exceeding timeout 120000 ms
</bash_metadata>

===============
more info about tests code:

from httpx import AsyncClient

class TestCreateAccount: 
"""Test the account creation endpoint."""
async def test_create_account_with_required_fields_only(self, client: AsyncClient) -> None:
    """Test successful account creation (only required fields)."""
    response = await client.post("/accounts", json={"name": "My Main Account"})

    assert response.status_code == 201
    data = response.json()
    assert data["id"] == 1
    assert data["name"] == "My Main Account"
    assert data["description"] is None
    assert data["is_archived"] is False
    assert data["tags"] == []
    assert "created_at" in data
    assert "updated_at" in data

async def test_create_account_with_all_fields(self, client: AsyncClient) -> None:
    """Test successful account creation (all fields included)."""
    response = await client.post(
        "/accounts",
        json={
            "name": "Long-Term Investment",
            "description": "A portfolio for long-term stock holdings",
            "tags": ["Long-Term", "Value Investing"],
        },
    )

    assert response.status_code == 201
    data = response.json()
    assert data["name"] == "Long-Term Investment"
    assert data["description"] == "A portfolio for long-term stock holdings"
    assert data["is_archived"] is False
    assert data["tags"] == ["Long-Term", "Value Investing"]

async def test_create_account_with_empty_name(self, client: AsyncClient) -> None:
    """Test account creation with an empty name."""
    response = await client.post("/accounts", json={"name": ""})

    assert response.status_code == 422
    # Pydantic's Field(min_length=1) will validate first
    assert "at least 1 character" in str(response.json()["detail"]).lower()

async def test_create_account_with_whitespace_name(self, client: AsyncClient) -> None:
    """Test account creation with a name consisting of only spaces."""
    response = await client.post("/accounts", json={"name": "   "})

    assert response.status_code == 422
    # Pydantic's field validator will validate the empty string
    assert "Account name cannot be empty" in str(response.json()["detail"])

async def test_update_account_duplicate_name(self, client: AsyncClient) -> None:
    """Test account update with a duplicate name."""
    await client.post("/accounts", json={"name": "Account 1"})
    create_response = await client.post("/accounts", json={"name": "Account 2"})
    account_id = create_response.json()["id"]

    response = await client.patch(f"/accounts/{account_id}", json={"name": "Account 1"})

    assert response.status_code == 409
    assert "Account name already exists" in response.json()["detail"]

async def test_update_account_tags_not_array(self, client: AsyncClient) -> None:
    """Test account update with tags not being an array."""
    create_response = await client.post("/accounts", json={"name": "Test Account"})
    account_id = create_response.json()["id"]

    response = await client.patch(f"/accounts/{account_id}", json={"tags": "tag1"})

    assert response.status_code == 422

async def test_update_account_name_trim_whitespace(self, client: AsyncClient) -> None:
    """Test account update with leading and trailing whitespace in the name automatically trimmed."""
    create_response = await client.post("/accounts", json={"name": "My Account"})
    account_id = create_response.json()["id"]

    response = await client.patch(f"/accounts/{account_id}", json={"name": "  New Name  "})

    assert response.status_code == 200
    assert response.json()["name"] == "New Name"

async def test_update_account_not_found(self, client: AsyncClient) -> None:
    """Test updating a non-existent account."""
    response = await client.patch("/accounts/999", json={"name": "New Name"})

    assert response.status_code == 404
    assert response.json()["detail"] == "Account does not exist"

async def test_update_account_empty_body(self, client: AsyncClient) -> None:
    """Test account update with an empty request body."""
    create_response = await client.post("/accounts", json={"name": "Test Account"})
    account_id = create_response.json()["id"]

    response = await client.patch(f"/accounts/{account_id}", json={})

    assert response.status_code == 422
    # Pydantic's model validator will check
    detail_str = str(response.json()["detail"])
    assert "value" in detail_str.lower() and "error" in detail_str.lower()

Plugins

No response

OpenCode version

1.1.14

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

win10 22h2

Terminal

Windows Terminal

Originally created by @mizaibear on GitHub (Jan 12, 2026). Originally assigned to: @rekram1-node on GitHub. ### Description when opencode starts a pytest by itself ,a pytest with pytest-asyncio for FastAPI backend. tests actually run for 0.23seconds but , process continue to run until <bash_metadata> show up and it takes 120seconds real-time to timeout. when opencode running ,not in the shell. i try another project's tests without asyncio, it doesn't wait to timeout. is it a bug? $ cd backend && uv run pytest -v ============================= test session starts ============================= platform win32 -- Python 3.13.9, pytest-9.0.2, pluggy-1.6.0 -- D:\workspace\my_projects\bearpos\backend\.venv\Scripts\python.exe cachedir: .pytest_cache rootdir: D:\workspace\my_projects\bearpos\backend configfile: pytest.ini testpaths: tests plugins: anyio-4.12.1, asyncio-1.3.0, cov-7.0.0 asyncio: mode=Mode.AUTO, debug=False, asyncio_default_fixture_loop_scope=function, asyncio_default_test_loop_scope=function collecting ... collected 14 items tests/test_accounts.py::TestCreateAccount::test_create_account_with_required_fields_only PASSED [ 7%] tests/test_accounts.py::TestCreateAccount::test_create_account_with_all_fields PASSED [ 14%] tests/test_accounts.py::TestCreateAccount::test_create_account_with_empty_name PASSED [ 21%] tests/test_accounts.py::TestCreateAccount::test_create_account_with_whitespace_name PASSED [ 28%] tests/test_accounts.py::TestCreateAccount::test_update_account_duplicate_name PASSED [ 35%] tests/test_accounts.py::TestCreateAccount::test_update_account_tags_not_array PASSED [ 42%] tests/test_accounts.py::TestCreateAccount::test_update_account_name_trim_whitespace PASSED [ 50%] tests/test_accounts.py::TestCreateAccount::test_update_account_not_found PASSED [ 57%] tests/test_accounts.py::TestCreateAccount::test_update_account_empty_body PASSED [ 64%] tests/test_accounts.py::TestArchiveAccount::test_archive_account_success PASSED [ 71%] tests/test_accounts.py::TestArchiveAccount::test_archive_account_already_archived PASSED [ 78%] tests/test_accounts.py::TestArchiveAccount::test_archive_account_not_found PASSED [ 85%] tests/test_accounts.py::TestIntegration::test_complete_crud_workflow PASSED [ 92%] tests/test_accounts.py::TestIntegration::test_unique_constraint PASSED [100%] ============================= 14 passed in 0.23s ============================== <bash_metadata> bash tool terminated command after exceeding timeout 120000 ms </bash_metadata> =============== more info about tests code: ``` from httpx import AsyncClient class TestCreateAccount: """Test the account creation endpoint.""" async def test_create_account_with_required_fields_only(self, client: AsyncClient) -> None: """Test successful account creation (only required fields).""" response = await client.post("/accounts", json={"name": "My Main Account"}) assert response.status_code == 201 data = response.json() assert data["id"] == 1 assert data["name"] == "My Main Account" assert data["description"] is None assert data["is_archived"] is False assert data["tags"] == [] assert "created_at" in data assert "updated_at" in data async def test_create_account_with_all_fields(self, client: AsyncClient) -> None: """Test successful account creation (all fields included).""" response = await client.post( "/accounts", json={ "name": "Long-Term Investment", "description": "A portfolio for long-term stock holdings", "tags": ["Long-Term", "Value Investing"], }, ) assert response.status_code == 201 data = response.json() assert data["name"] == "Long-Term Investment" assert data["description"] == "A portfolio for long-term stock holdings" assert data["is_archived"] is False assert data["tags"] == ["Long-Term", "Value Investing"] async def test_create_account_with_empty_name(self, client: AsyncClient) -> None: """Test account creation with an empty name.""" response = await client.post("/accounts", json={"name": ""}) assert response.status_code == 422 # Pydantic's Field(min_length=1) will validate first assert "at least 1 character" in str(response.json()["detail"]).lower() async def test_create_account_with_whitespace_name(self, client: AsyncClient) -> None: """Test account creation with a name consisting of only spaces.""" response = await client.post("/accounts", json={"name": " "}) assert response.status_code == 422 # Pydantic's field validator will validate the empty string assert "Account name cannot be empty" in str(response.json()["detail"]) async def test_update_account_duplicate_name(self, client: AsyncClient) -> None: """Test account update with a duplicate name.""" await client.post("/accounts", json={"name": "Account 1"}) create_response = await client.post("/accounts", json={"name": "Account 2"}) account_id = create_response.json()["id"] response = await client.patch(f"/accounts/{account_id}", json={"name": "Account 1"}) assert response.status_code == 409 assert "Account name already exists" in response.json()["detail"] async def test_update_account_tags_not_array(self, client: AsyncClient) -> None: """Test account update with tags not being an array.""" create_response = await client.post("/accounts", json={"name": "Test Account"}) account_id = create_response.json()["id"] response = await client.patch(f"/accounts/{account_id}", json={"tags": "tag1"}) assert response.status_code == 422 async def test_update_account_name_trim_whitespace(self, client: AsyncClient) -> None: """Test account update with leading and trailing whitespace in the name automatically trimmed.""" create_response = await client.post("/accounts", json={"name": "My Account"}) account_id = create_response.json()["id"] response = await client.patch(f"/accounts/{account_id}", json={"name": " New Name "}) assert response.status_code == 200 assert response.json()["name"] == "New Name" async def test_update_account_not_found(self, client: AsyncClient) -> None: """Test updating a non-existent account.""" response = await client.patch("/accounts/999", json={"name": "New Name"}) assert response.status_code == 404 assert response.json()["detail"] == "Account does not exist" async def test_update_account_empty_body(self, client: AsyncClient) -> None: """Test account update with an empty request body.""" create_response = await client.post("/accounts", json={"name": "Test Account"}) account_id = create_response.json()["id"] response = await client.patch(f"/accounts/{account_id}", json={}) assert response.status_code == 422 # Pydantic's model validator will check detail_str = str(response.json()["detail"]) assert "value" in detail_str.lower() and "error" in detail_str.lower() ``` ### Plugins _No response_ ### OpenCode version 1.1.14 ### Steps to reproduce _No response_ ### Screenshot and/or share link _No response_ ### Operating System win10 22h2 ### Terminal Windows Terminal
yindo added the windowsbugperf labels 2026-02-16 17:47:05 -05:00
yindo closed this issue 2026-02-16 17:47:05 -05:00
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: anomalyco/opencode#4959