Skip to content

Commit 4c9105b

Browse files
committed
fix: address follow-up copilot feedback
1 parent 5b8203a commit 4c9105b

4 files changed

Lines changed: 83 additions & 12 deletions

File tree

src/specify_cli/integration_state.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,21 @@
1111
INTEGRATION_STATE_SCHEMA = 1
1212

1313

14+
def clean_integration_key(key: Any) -> str | None:
15+
"""Return a stripped integration key, or None for empty/non-string values."""
16+
if not isinstance(key, str) or not key.strip():
17+
return None
18+
return key.strip()
19+
20+
1421
def dedupe_integration_keys(keys: list[Any]) -> list[str]:
1522
"""Return a de-duplicated list of non-empty integration keys."""
1623
seen: set[str] = set()
1724
deduped: list[str] = []
1825
for key in keys:
19-
if not isinstance(key, str) or not key.strip():
26+
clean = clean_integration_key(key)
27+
if clean is None:
2028
continue
21-
clean = key.strip()
2229
if clean in seen:
2330
continue
2431
seen.add(clean)
@@ -61,10 +68,8 @@ def normalize_integration_settings(settings: Any) -> dict[str, dict[str, Any]]:
6168

6269
def normalize_integration_state(data: dict[str, Any]) -> dict[str, Any]:
6370
"""Normalize legacy and multi-install integration metadata."""
64-
legacy_key = data.get("integration")
65-
default_key = data.get("default_integration")
66-
if not isinstance(default_key, str) or not default_key.strip():
67-
default_key = legacy_key if isinstance(legacy_key, str) else None
71+
legacy_key = clean_integration_key(data.get("integration"))
72+
default_key = clean_integration_key(data.get("default_integration")) or legacy_key
6873

6974
installed = data.get("installed_integrations")
7075
installed_keys = dedupe_integration_keys(installed if isinstance(installed, list) else [])
@@ -93,7 +98,7 @@ def normalize_integration_state(data: dict[str, Any]) -> dict[str, Any]:
9398
def default_integration_key(state: dict[str, Any]) -> str | None:
9499
"""Return the default integration key from normalized state."""
95100
key = state.get("default_integration") or state.get("integration")
96-
return key if isinstance(key, str) and key else None
101+
return clean_integration_key(key)
97102

98103

99104
def installed_integration_keys(state: dict[str, Any]) -> list[str]:
@@ -123,6 +128,7 @@ def write_integration_json(
123128
dest = project_root / INTEGRATION_JSON
124129
dest.parent.mkdir(parents=True, exist_ok=True)
125130

131+
integration_key = clean_integration_key(integration_key)
126132
installed = dedupe_integration_keys(installed_integrations or [])
127133
if integration_key and integration_key not in installed:
128134
installed.insert(0, integration_key)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
"""Tests for integration state normalization helpers."""
2+
3+
import json
4+
5+
from specify_cli.integration_state import INTEGRATION_JSON
6+
from specify_cli.integration_state import (
7+
default_integration_key,
8+
normalize_integration_state,
9+
write_integration_json,
10+
)
11+
12+
13+
def test_normalize_integration_state_strips_default_key_without_duplicates():
14+
state = normalize_integration_state(
15+
{
16+
"default_integration": " claude ",
17+
"integration": " claude ",
18+
"installed_integrations": ["claude"],
19+
}
20+
)
21+
22+
assert state["integration"] == "claude"
23+
assert state["default_integration"] == "claude"
24+
assert state["installed_integrations"] == ["claude"]
25+
26+
27+
def test_normalize_integration_state_strips_legacy_key_fallback():
28+
state = normalize_integration_state(
29+
{
30+
"integration": " codex ",
31+
"installed_integrations": [],
32+
}
33+
)
34+
35+
assert state["integration"] == "codex"
36+
assert state["default_integration"] == "codex"
37+
assert state["installed_integrations"] == ["codex"]
38+
39+
40+
def test_default_integration_key_strips_raw_state_values():
41+
assert default_integration_key({"default_integration": " claude "}) == "claude"
42+
assert default_integration_key({"integration": " codex "}) == "codex"
43+
44+
45+
def test_write_integration_json_strips_integration_key(tmp_path):
46+
write_integration_json(
47+
tmp_path,
48+
version="1.2.3",
49+
integration_key=" claude ",
50+
installed_integrations=["claude"],
51+
)
52+
53+
state = json.loads((tmp_path / INTEGRATION_JSON).read_text(encoding="utf-8"))
54+
assert state["integration"] == "claude"
55+
assert state["default_integration"] == "claude"
56+
assert state["installed_integrations"] == ["claude"]

tests/integrations/test_integration_subcommand.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,15 @@ def test_list_shows_multi_install_safe_status(self, tmp_path):
8181
assert result.exit_code == 0
8282
assert "Multi-install" in result.output
8383
assert "Safe" in result.output
84-
assert "yes" in result.output
85-
assert "no" in result.output
84+
rows = {
85+
cells[0]: cells
86+
for line in result.output.splitlines()
87+
if line.startswith("│")
88+
for cells in ([cell.strip() for cell in line.split("│")[1:-1]],)
89+
if cells and cells[0]
90+
}
91+
assert rows["claude"][4] == "yes"
92+
assert rows["copilot"][4] == "no"
8693

8794

8895
# ── install ──────────────────────────────────────────────────────────

tests/integrations/test_registry.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -66,15 +66,17 @@ def _posix_path(value: str | None) -> str | None:
6666

6767
def _integration_root_dir(key: str) -> str | None:
6868
integration = INTEGRATION_REGISTRY[key]
69-
return _posix_path(integration.config.get("folder"))
69+
cfg = integration.config if isinstance(integration.config, dict) else {}
70+
return _posix_path(cfg.get("folder"))
7071

7172

7273
def _integration_commands_dir(key: str) -> str | None:
7374
integration = INTEGRATION_REGISTRY[key]
74-
folder = integration.config.get("folder")
75+
cfg = integration.config if isinstance(integration.config, dict) else {}
76+
folder = cfg.get("folder")
7577
if not folder:
7678
return None
77-
subdir = integration.config.get("commands_subdir", "commands")
79+
subdir = cfg.get("commands_subdir", "commands")
7880
return (PurePosixPath(folder) / subdir).as_posix()
7981

8082

0 commit comments

Comments
 (0)