Skip to content

Add test suite for ddss/output.py - #12

Merged
hzhangxyz merged 8 commits into
mainfrom
copilot/add-test-for-output-file
Dec 22, 2025
Merged

Add test suite for ddss/output.py#12
hzhangxyz merged 8 commits into
mainfrom
copilot/add-test-for-output-file

Conversation

Copilot AI commented Dec 22, 2025

Copy link
Copy Markdown
Contributor

The output.py module lacked test coverage. It continuously polls the database for Facts and Ideas entries, formats them with unparse() (e.g., a\n----\nba => b), and prints them. Testing was non-trivial due to the infinite loop.

Changes

  • Test structure: Created tests/ directory with test_output.py using pytest and pytest-asyncio
  • Test coverage:
    • Facts and Ideas formatting verification
    • Multiple entry handling
    • Graceful cancellation without hanging
  • Test approach: Uses pytest features including async fixtures (@pytest_asyncio.fixture), async test support (@pytest.mark.asyncio), and the capsys fixture for stdout capture. Temporary databases and asyncio.Task.cancel() safely test the infinite loop.
# Example test pattern with pytest
@pytest.mark.asyncio
async def test_output_formats_facts_correctly(temp_db, capsys):
    addr, engine, session = temp_db
    async with session() as sess:
        sess.add(Facts(data="a\n----\nb"))
        await sess.commit()
    
    task = asyncio.create_task(main(addr, engine, session))
    await asyncio.sleep(0.2)
    task.cancel()
    
    captured = capsys.readouterr()
    assert "fact: a => b" in captured.out
Original prompt

This section details on the original issue you should resolve

<issue_title>给ddss/output.py写一个测试</issue_title>
<issue_description>其他文件的测试在后续的pr里,先不管。

比如在sql中写入一个

a

b

这样的东西

output会输出

a => b

这样的东西。

需要注意output是一个死循环,平时需要ctrl c之类的东西才会中断,写测试的时候注意别死循环了。最好是看python -m ddss.output sqlite+aiosqlite:///./data.db 这样的命令的输出</issue_description>

Comments on the Issue (you are @copilot in this section)


💡 You can make Copilot smarter by setting up custom instructions, customizing its development environment and configuring Model Context Protocol (MCP) servers. Learn more Copilot coding agent tips in the docs.

Copilot AI and others added 4 commits December 22, 2025 15:24
Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>
Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>
Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>
Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>
Copilot AI changed the title [WIP] Add test for ddss/output.py Add test suite for ddss/output.py Dec 22, 2025
Copilot AI requested a review from hzhangxyz December 22, 2025 15:32

@hzhangxyz hzhangxyz left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

改用pytest吧,如果可以用pytest的特性就用其高级特性,不行就直接用pytest

Co-authored-by: hzhangxyz <11623447+hzhangxyz@users.noreply.github.com>

Copilot AI commented Dec 22, 2025

Copy link
Copy Markdown
Contributor Author

改用pytest吧,如果可以用pytest的特性就用其高级特性,不行就直接用pytest

Converted tests to pytest with pytest-asyncio. The changes leverage:

  • @pytest_asyncio.fixture for async database setup
  • @pytest.mark.asyncio for async test functions
  • capsys fixture for stdout capture (replacing manual StringIO)
  • Native async/await support

Commit: a07d2fe

@hzhangxyz
hzhangxyz marked this pull request as ready for review December 22, 2025 15:55
Copilot AI review requested due to automatic review settings December 22, 2025 15:55
@hzhangxyz
hzhangxyz merged commit 69b4958 into main Dec 22, 2025
8 checks passed

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds a comprehensive test suite for the ddss/output.py module, which continuously polls the database for Facts and Ideas entries and outputs them in a formatted way. The testing approach successfully handles the challenge of testing an infinite loop by using asyncio task cancellation.

Key Changes

  • Created tests/ directory with test_output.py containing 4 test cases covering Facts/Ideas formatting, multiple entries, and graceful cancellation
  • Added development dependencies (pytest, pytest-asyncio, pytest-cov, ruff) to pyproject.toml and uv.lock
  • Tests use async fixtures and task cancellation to safely test the infinite polling loop without hanging

Reviewed changes

Copilot reviewed 2 out of 3 changed files in this pull request and generated 6 comments.

File Description
tests/test_output.py New test file with 4 test cases for output formatting and cancellation behavior
pyproject.toml Added dev optional dependencies for testing and linting tools
uv.lock Updated lock file with new test dependencies (pytest, pytest-asyncio, pytest-cov, ruff, colorama, coverage, iniconfig, packaging, pluggy, pygments, tomli)

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/test_output.py
addr = f"sqlite+aiosqlite:///{db_path.as_posix()}"
engine, session = await initialize_database(addr)
yield addr, engine, session
await engine.dispose()

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The test fixture disposes the engine after yielding, but the main() function being tested also disposes the engine in its finally block (line 37 of output.py). This could lead to a "double dispose" situation where the engine is disposed twice, potentially causing warnings or errors.

Consider either:

  1. Not disposing the engine in the fixture since main() will handle it
  2. Adding a flag to main() to skip engine disposal when running in test mode
  3. Catching and ignoring any disposal errors in the fixture cleanup
Suggested change
await engine.dispose()
try:
await engine.dispose()
except Exception:
# Ignore errors if the engine was already disposed (e.g., by main()).
pass

Copilot uses AI. Check for mistakes.
Comment thread tests/test_output.py

# Run the main function with a timeout to avoid infinite loop
task = asyncio.create_task(main(addr, engine, session))
await asyncio.sleep(0.2) # Give it time to process

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The tests rely on timing with asyncio.sleep(0.2) to allow the output loop to process entries. This makes tests non-deterministic and potentially flaky, especially on slower systems or under load. The tests could fail if the system is too slow to process within 200ms, or could waste time if processing completes faster.

Consider using a more reliable synchronization mechanism, such as:

  1. Adding a callback or event mechanism to main() for testing
  2. Using a mock/spy on the print function to detect when output occurs
  3. Polling the output buffer until expected content appears (with a timeout)

Copilot uses AI. Check for mistakes.
Comment thread pyproject.toml
Comment on lines +34 to +40
[project.optional-dependencies]
dev = [
"pytest~=9.0.2",
"pytest-asyncio~=1.3.0",
"pytest-cov~=7.0.0",
"ruff~=0.14.10",
]

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Missing pytest-asyncio configuration in pyproject.toml. pytest-asyncio requires explicit configuration to handle async fixtures and tests properly. Without configuration, you may encounter warnings or unexpected behavior.

Add a [tool.pytest.ini_options] section with asyncio_mode configuration, for example:

[tool.pytest.ini_options]
asyncio_mode = "auto"

or

asyncio_default_fixture_loop_scope = "function"

This ensures pytest-asyncio operates in the expected mode.

Copilot uses AI. Check for mistakes.
Comment thread tests/test_output.py
task.cancel()
try:
await task
except asyncio.CancelledError:

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Comment thread tests/test_output.py
task.cancel()
try:
await task
except asyncio.CancelledError:

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Comment thread tests/test_output.py
task.cancel()
try:
await task
except asyncio.CancelledError:

Copilot AI Dec 22, 2025

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

'except' clause does nothing but pass and there is no explanatory comment.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

给ddss/output.py写一个测试

3 participants