Coverage for /home/crpier/Projects/snektest/snektest/agent_docs.py: 35%

20 statements  

« prev     ^ index     » next       coverage.py v7.14.1, created at 2026-06-07 00:30 +0300

1"""Embedded documentation and examples for coding agents.""" 

2 

3from importlib.resources import files 

4 

5from snektest.models import BadRequestError 

6 

7AGENT_DOCS = """# snektest agent guide 

8 

9Snektest is a Python testing framework with first-class async and typing support. 

10 

11## Quick start 

12 

13Create a `test_*.py` file and decorate test functions with `@test(mark=...)`. Mark every test with the resources it may use: 

14 

15```python 

16from snektest import assert_eq, test 

17 

18@test(mark="fast") 

19def test_addition() -> None: 

20 assert_eq(1 + 1, 2) 

21``` 

22 

23Run tests with: 

24 

25```bash 

26snektest 

27python -m snektest 

28``` 

29 

30Useful commands: 

31 

32```bash 

33snektest --help 

34snektest --agent-docs 

35snektest --examples 

36snektest --example async 

37snektest examples 

38snektest example async 

39``` 

40 

41## Core patterns 

42 

43- Import assertions from `snektest`; prefer `assert_eq()` over bare `assert`. 

44- Mark every test. This is the recommended way to use snektest. 

45- Use `mark="fast"` for in-memory tests with no IO, threads, or subprocesses. 

46- Use `mark="medium"` for tests that use local IO or threads. 

47- Use `mark="slow"` for tests that use network IO, subprocesses, or expensive external resources. 

48- Async tests are regular `async def` functions decorated with `@test(mark=...)`. 

49- Use `Param(value=..., name=...)` inside `@test([...], mark=...)` for parameterization. 

50- Use generator fixtures with `load_fixture(fixture())`. 

51- Put all `load_fixture(...)` calls at the beginning of the test, before actions or assertions. 

52- Avoid conditional or mid-test fixture loading unless delayed loading is the behavior under test. 

53- Annotate shared fixtures as `SessionFixture[T]` or `AsyncSessionFixture[T]` for setup shared by the whole test run. 

54- Filter runs with paths such as `snektest tests/test_math.py::test_addition` or markers such as `snektest --mark fast`. 

55 

56## Copyable examples 

57 

58List bundled examples: 

59 

60```bash 

61snektest --examples 

62``` 

63 

64Print one example: 

65 

66```bash 

67snektest --example basic 

68snektest --example fixtures 

69snektest --example async 

70snektest --example parametrize 

71``` 

72""" 

73 

74EXAMPLE_FILES: dict[str, str] = { 

75 "async": "async_tests.py", 

76 "basic": "basic_test.py", 

77 "fixtures": "fixtures.py", 

78 "parametrize": "parametrize.py", 

79} 

80 

81 

82def get_agent_docs() -> str: 

83 """Return the embedded guide for AI agents and humans.""" 

84 return AGENT_DOCS 

85 

86 

87def get_examples_listing() -> str: 

88 """Return a human-readable list of bundled examples.""" 

89 lines = [ 

90 "Bundled snektest examples:", 

91 *[f" {name:<12} snektest --example {name}" for name in sorted(EXAMPLE_FILES)], 

92 ] 

93 return "\n".join(lines) + "\n" 

94 

95 

96def get_example_source(example_name: str) -> str: 

97 """Return the source code for a bundled example.""" 

98 normalized_name = example_name.removesuffix(".py") 

99 file_name = EXAMPLE_FILES.get(normalized_name) 

100 if file_name is None: 

101 file_name = next( 

102 ( 

103 candidate 

104 for candidate in EXAMPLE_FILES.values() 

105 if candidate.removesuffix(".py") == normalized_name 

106 ), 

107 None, 

108 ) 

109 if file_name is None: 

110 available = ", ".join(sorted(EXAMPLE_FILES)) 

111 msg = f"Unknown example `{example_name}`. Use one of: {available}" 

112 raise BadRequestError(msg) 

113 

114 resource = files("snektest.examples").joinpath(file_name) 

115 return resource.read_text(encoding="utf-8")