Skip to content

fix(scaffold): make generated projects pass tests out of the box#93

Merged
qjrm1430 merged 2 commits into
mainfrom
fix/scaffold-tests-and-pytest-config
May 27, 2026
Merged

fix(scaffold): make generated projects pass tests out of the box#93
qjrm1430 merged 2 commits into
mainfrom
fix/scaffold-tests-and-pytest-config

Conversation

@qjrm1430
Copy link
Copy Markdown
Member

🚀 PR Type

  • 🎯 Ready for Review — verified by regenerating fa and running uv run pytest + uv run ruff check .

📝 Summary

act new + act cast로 생성된 새 프로젝트가 즉시 uv run pytest를 통과하지 못하는 문제 6건 수정. 이 PR 적용 후 갓 스캐폴딩된 프로젝트는 추가 수정 없이 4/4 테스트 통과.

📄 Description

act_operator/fa(개발 환경에 생성된 실제 스캐폴드)로 검증 중 다음 결함들을 발견:

1. tests/node_tests/__init.py 파일명 오타

  • 실제: __init.py (밑줄 1개)
  • 정상: __init__.py
  • 영향: 엄격한 모듈 탐색 시 Python이 패키지로 인식 못 함

2. pyproject.toml[tool.pytest.ini_options] 부재

  • pytest가 부모 디렉토리의 pyproject.toml로 올라가 fallback → casts 모듈 import 실패
  • 검증 시 3개 test file 모두 ModuleNotFoundError: No module named 'casts' collection error
  • 추가: pythonpath = ["."], testpaths = ["tests"], asyncio_mode = "auto"

3. SampleNodeOutputState 의미 불일치 → graph.invoke() 항상 None

  • SampleNode{"messages": [AIMessage(...)]}만 반환
  • OutputStateresult: str만 선언 → output filter가 result만 노출
  • 결과: graph.invoke({"query": "test"})None
  • 수정: SampleNoderesultmessages 둘 다 작성 (다중 키 state update 패턴도 시연)

4. 테스트 어설션이 노드 출력과 완전 불일치

  • test_node.py: {"message": "..."} (단수 + str) vs 실제 {"messages": [AIMessage]} (복수 + 리스트)
  • {cast_snake}_test.py: result["messages"] == "..."chore: Design Claude Skills architecture for Act template #3 때문에 None["messages"]TypeError
  • 양쪽 모두 신규 노드 출력 계약에 맞게 재작성

5. pytest-asyncio 미설치 + asyncio_mode 미설정

  • test_async_base_node_calls_executeasync def인데 plugin 없어 collection 실패
  • pytest-asyncio 추가 + asyncio_mode = "auto"로 decorator 없이 자동 collection

6. "Sam"/"Sample Cast"/"sam graphs" 하드코딩

  • nodes.py(처음 docstring은 ok), state.py, middlewares.py, agents.py, tests/node_tests/test_node.py
  • 모두 {{ cookiecutter.cast_name }}로 템플릿화

✅ Quality Checks

  • uv run ruff check . passed (All checks passed!)
  • uv run pytest -q passed (4 passed in 0.66s)

검증 절차:

rm -rf fa
uv run python -m act_operator new -p . -a "Fa" -c "dmk" -l en
uv run python -m act_operator cast -p ./fa -c "nvsd"
cd fa
uv sync --group test
uv run pytest -v
# tests/cast_tests/dmk_test.py::test_graph_produces_message PASSED  [ 25%]
# tests/cast_tests/nvsd_test.py::test_graph_produces_message PASSED [ 50%]
# tests/node_tests/test_node.py::test_base_node_calls_execute PASSED [ 75%]
# tests/node_tests/test_node.py::test_async_base_node_calls_execute PASSED [100%]
# 4 passed in 0.66s

💪 Ownership

  • Hand-raise: 새 프로젝트의 first-run UX에 큰 영향이라 follow-up 관찰

💡 Notes

  • 사용자 영향: 이번 fix가 적용된 act-operator 릴리즈를 사용하는 신규 프로젝트만 영향. 기존 프로젝트는 자체 pyproject.toml과 테스트가 이미 작성되어 있어 영향 없음
  • act upgrade 고려: 기존 프로젝트가 act upgrade로 동일 fix를 받을 수 있는지 별도 검증 필요
  • 별도 PR (perf: enhance developing-cast and streaming-cast skill documentatio… #92): cast 스킬 5종 마이그레이션(LangChain 1.3 / LangGraph 1.2 / deepagents 0.6) — 이 PR과 독립

🤖 Generated with Claude Code

Six bugs in the cookiecutter scaffold caused `uv run pytest` to fail
immediately after `act new` + `act cast`. After this change, freshly
scaffolded projects produce 4/4 passing tests with no manual edits.

Bug fixes:

1. tests/node_tests/__init.py → __init__.py
   Filename typo prevented Python from treating the directory as a
   package under strict module discovery.

2. pyproject.toml: add [tool.pytest.ini_options]
   Without `pythonpath`/`testpaths`, pytest climbed past fa/ to a
   parent pyproject and failed with `ModuleNotFoundError: No module
   named 'casts'` for all three test files.

3. SampleNode wrote only `messages`, but OutputState declared only
   `result: str` → graph.invoke() returned None. Now writes both
   `result` (exposed) and `messages` (accumulated via add_messages),
   demonstrating multi-key state updates.

4. Test assertions did not match node output:
   - test_node.py asserted {"message": str} (singular) vs actual
     {"messages": [AIMessage]}.
   - {cast}_test.py asserted result["messages"] == str against a
     None result (#3 cascade) → TypeError.
   Both updated to match the new node contract.

5. asyncio test support: add `pytest-asyncio` + asyncio_mode="auto"
   so async def test_* functions are collected without per-test
   decorators.

6. Docstring placeholders hardcoded "Sam"/"Sample Cast"/"sam graphs"
   in nodes.py / state.py / middlewares.py / agents.py /
   tests/node_tests/test_node.py. Replaced with
   `{{ cookiecutter.cast_name }}` so each cast renders correctly.

Verified by regenerating with `python -m act_operator new` +
`act_operator cast` and running `uv run pytest -v`: 4 passed in 0.66s.
`uv run ruff check .` also passes.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request updates the scaffold templates to use dynamic cookiecutter variables for the cast name, updates the sample nodes to return both a result and messages payload, and configures pytest-asyncio in pyproject.toml. The test cases have been updated accordingly. The review feedback suggests adding an assertion to verify that the graph invocation result is not None before checking for keys, which prevents less clear TypeError exceptions during test failures.

Two code/config files had Korean comments without language conditioning,
so projects scaffolded with `-l en` still contained Korean text:

- tests/cast_tests/{cast}_test.py: two inline comments added in the
  previous commit (regression on my part).
- .pre-commit-config.yaml: three hook description comments hardcoded
  in Korean.

In-code/config comments now consistently English per the project
convention (`.env.example`, `README.md`, `TEMPLATE_README.md`, and
`casts/{cast}/README.md` already gate their content on
`cookiecutter.language` and remain language-aware).

Verified by regenerating with `-l en`: `grep -P '[\x{ac00}-\x{d7a3}]'`
returns 0 matches across the generated project, and
`uv run pytest -v` still reports `4 passed in 0.47s`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@qjrm1430 qjrm1430 merged commit 9363553 into main May 27, 2026
3 checks passed
@qjrm1430 qjrm1430 deleted the fix/scaffold-tests-and-pytest-config branch May 27, 2026 02:10
@qjrm1430 qjrm1430 restored the fix/scaffold-tests-and-pytest-config branch May 27, 2026 02:11
qjrm1430 added a commit that referenced this pull request May 27, 2026
Same filename typo as the scaffold one fixed in #93, but in the
act_operator package's own test tree. The single-underscore suffix
prevented Python from recognising the directory as a package under
strict module discovery, which can break tooling that walks the
package tree (mypy/pyright in strict mode, some coverage configs).

Verified with `uv run pytest -q`: 14 passed in 3.47s.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
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.

1 participant