|
7 | 7 |
|
8 | 8 | from attune_author.generator import ( |
9 | 9 | GenerationResult, |
| 10 | + _split_signature, |
10 | 11 | generate_feature_templates, |
11 | 12 | ) |
12 | 13 | from attune_author.manifest import Feature |
13 | 14 |
|
14 | 15 |
|
| 16 | +class TestSplitSignature: |
| 17 | + """Tests for ``_split_signature``.""" |
| 18 | + |
| 19 | + def test_typed_params_and_return(self) -> None: |
| 20 | + params, returns = _split_signature( |
| 21 | + "authenticate(username: str, password: str) -> bool", "authenticate" |
| 22 | + ) |
| 23 | + assert params == "username: str, password: str" |
| 24 | + assert returns == "bool" |
| 25 | + |
| 26 | + def test_no_return_annotation(self) -> None: |
| 27 | + params, returns = _split_signature("main()", "main") |
| 28 | + assert params == "" |
| 29 | + assert returns == "" |
| 30 | + |
| 31 | + def test_complex_return_with_pipe(self) -> None: |
| 32 | + params, returns = _split_signature("main(argv: list[str] | None = None) -> int", "main") |
| 33 | + assert params == "argv: list[str] | None = None" |
| 34 | + assert returns == "int" |
| 35 | + |
| 36 | + def test_unexpected_shape_returns_empty(self) -> None: |
| 37 | + # Defensive: anything not matching ``name(...)`` shouldn't crash. |
| 38 | + assert _split_signature("not a signature", "anything") == ("", "") |
| 39 | + |
| 40 | + |
| 41 | +class TestReferenceTemplateColumns: |
| 42 | + """Reference template must surface Parameters/Returns columns |
| 43 | + from ``function_signatures`` without depending on the LLM polish |
| 44 | + pass — otherwise a polish bypass (no API key, lenient mode, |
| 45 | + cached miss) silently drops typed argument and return data that |
| 46 | + the AST already has. |
| 47 | + """ |
| 48 | + |
| 49 | + def test_reference_table_includes_typed_params_and_return( |
| 50 | + self, help_dir: Path, project_root: Path |
| 51 | + ) -> None: |
| 52 | + feature = Feature( |
| 53 | + name="auth", |
| 54 | + description="Authentication and authorization", |
| 55 | + files=["src/auth/**"], |
| 56 | + tags=["security"], |
| 57 | + ) |
| 58 | + |
| 59 | + with patch.dict("os.environ", {}, clear=False): |
| 60 | + result = generate_feature_templates( |
| 61 | + feature=feature, |
| 62 | + help_dir=help_dir, |
| 63 | + project_root=project_root, |
| 64 | + depths=["reference"], |
| 65 | + use_rag=False, |
| 66 | + ) |
| 67 | + |
| 68 | + ref = next(t for t in result.templates if t.depth == "reference") |
| 69 | + content = ref.path.read_text(encoding="utf-8") |
| 70 | + |
| 71 | + # 4-column header is the structural fix — without polish, |
| 72 | + # the old template emitted only ``Function | Description | File``. |
| 73 | + assert "| Function | Parameters | Returns | Description | File |" in content |
| 74 | + # Typed parameters from the AST are surfaced verbatim. |
| 75 | + assert "`username: str, password: str`" in content |
| 76 | + # Return annotation is surfaced verbatim. |
| 77 | + assert "`bool`" in content |
| 78 | + # Feature description from features.yaml lands directly under |
| 79 | + # the title, not only via LLM-polish synthesis. |
| 80 | + assert "Authentication and authorization" in content |
| 81 | + |
| 82 | + |
15 | 83 | class TestGenerateFeatureTemplates: |
16 | 84 | """Tests for generate_feature_templates().""" |
17 | 85 |
|
|
0 commit comments