@@ -111,50 +111,38 @@ def test_init_derives_act_name_from_path(tmp_path: Path) -> None:
111111 assert "custom-act" in _read (project_pyproject )
112112
113113
114- def _create_sample_project (tmp_path : Path , lang : str = "en" ) -> Path :
115- """Helper to scaffold a sample Act project for testing."""
116- target_dir = tmp_path / "sample-act"
117- result = runner .invoke (
118- app ,
119- [
120- "--path" ,
121- str (target_dir ),
122- "--act-name" ,
123- "Sample Act" ,
124- "--cast-name" ,
125- "Primary Cast" ,
126- "--lang" ,
127- lang ,
128- ],
129- )
130- assert result .exit_code == 0 , result .stdout
131- return target_dir
132-
133-
134114def test_upgrade_replaces_skills (tmp_path : Path ) -> None :
135- target_dir = _create_sample_project (tmp_path )
115+ """Test that `act upgrade` restores scaffolded skills after local corruption."""
116+ # Given a valid Act project with a corrupted skill file
117+ target_dir = _create_project (tmp_path )
136118 skill_file = target_dir / ".claude" / "skills" / "testing-cast" / "SKILL.md"
137119 assert skill_file .exists ()
138120
139- # Corrupt a skill file
140121 original = _read (skill_file )
141122 skill_file .write_text ("corrupted content" , encoding = "utf-8" )
142123 assert _read (skill_file ) == "corrupted content"
143124
144- # Upgrade should restore it
125+ # When the upgrade command is executed
145126 result = runner .invoke (app , ["upgrade" , "--path" , str (target_dir )])
146127 combined = (result .stdout or "" ) + (result .stderr or "" )
128+
129+ # Then the original scaffold content should be restored
147130 assert result .exit_code == 0 , combined
148131 assert "upgraded successfully" in combined
149132 assert _read (skill_file ) == original
150133
151134
152135def test_upgrade_creates_backup (tmp_path : Path ) -> None :
153- target_dir = _create_sample_project (tmp_path )
136+ """Test that `act upgrade` creates a backup of the existing skills directory."""
137+ # Given a valid Act project with scaffolded skills
138+ target_dir = _create_project (tmp_path )
154139 skills_dir = target_dir / ".claude" / "skills"
155140 assert skills_dir .exists ()
156141
142+ # When the upgrade command is executed
157143 result = runner .invoke (app , ["upgrade" , "--path" , str (target_dir )])
144+
145+ # Then the previous skills should exist in .claude/skills.bak
158146 assert result .exit_code == 0 , result .stdout
159147
160148 backup_dir = target_dir / ".claude" / "skills.bak"
@@ -163,13 +151,68 @@ def test_upgrade_creates_backup(tmp_path: Path) -> None:
163151
164152
165153def test_upgrade_fails_on_invalid_project (tmp_path : Path ) -> None :
154+ """Test that `act upgrade` fails for paths that are not valid Act projects."""
155+ # Given a directory that is not an Act project
166156 invalid_dir = tmp_path / "not-a-project"
167157 invalid_dir .mkdir ()
168158
159+ # When the upgrade command is executed against that path
169160 result = runner .invoke (app , ["upgrade" , "--path" , str (invalid_dir )])
161+
162+ # Then the command should fail
170163 assert result .exit_code != 0
171164
172165
166+ def test_upgrade_detects_korean_language_from_readme (tmp_path : Path ) -> None :
167+ """Test that `act upgrade` reports Korean when the project README is Korean."""
168+ # Given a Korean-language Act project
169+ target_dir = _create_project (tmp_path , lang = "kr" )
170+
171+ # When the upgrade command is executed
172+ result = runner .invoke (app , ["upgrade" , "--path" , str (target_dir )])
173+ combined = (result .stdout or "" ) + (result .stderr or "" )
174+
175+ # Then the upgrade summary should report the detected Korean language
176+ assert result .exit_code == 0 , combined
177+ assert "한국어" in combined
178+
179+
180+ def test_upgrade_replaces_existing_backup_directory (tmp_path : Path ) -> None :
181+ """Test that `act upgrade` replaces an existing skills backup with a fresh one."""
182+ # Given a valid Act project and an existing stale backup directory
183+ target_dir = _create_project (tmp_path )
184+ backup_dir = target_dir / ".claude" / "skills.bak"
185+ backup_dir .mkdir (parents = True )
186+ stale_file = backup_dir / "stale.txt"
187+ stale_file .write_text ("old backup" , encoding = "utf-8" )
188+
189+ # When the upgrade command is executed
190+ result = runner .invoke (app , ["upgrade" , "--path" , str (target_dir )])
191+
192+ # Then the stale backup should be replaced by the new backup contents
193+ assert result .exit_code == 0 , result .stdout
194+ assert backup_dir .exists ()
195+ assert not stale_file .exists ()
196+ assert (backup_dir / "testing-cast" ).exists ()
197+
198+
199+ def test_upgrade_succeeds_with_malformed_langgraph (tmp_path : Path ) -> None :
200+ """Test that `act upgrade` falls back safely when langgraph.json is malformed."""
201+ # Given a valid Act project with an invalid langgraph.json file
202+ target_dir = _create_project (tmp_path )
203+ langgraph_file = target_dir / "langgraph.json"
204+ langgraph_file .write_text ("{ invalid json" , encoding = "utf-8" )
205+
206+ # When the upgrade command is executed
207+ result = runner .invoke (app , ["upgrade" , "--path" , str (target_dir )])
208+ combined = (result .stdout or "" ) + (result .stderr or "" )
209+
210+ # Then the upgrade should still succeed using the fallback cast context
211+ assert result .exit_code == 0 , combined
212+ assert "upgraded successfully" in combined
213+ assert (target_dir / ".claude" / "skills" ).exists ()
214+
215+
173216def test_init_aborts_on_non_empty_dir (tmp_path : Path ) -> None :
174217 """Test that project creation aborts when the target directory is not empty."""
175218 # Given a target directory that already contains files
0 commit comments