-
Notifications
You must be signed in to change notification settings - Fork 123
Expand file tree
/
Copy pathtest_completion.py
More file actions
139 lines (104 loc) · 5.25 KB
/
Copy pathtest_completion.py
File metadata and controls
139 lines (104 loc) · 5.25 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
"""Tests for shell tab-completion support (issue #247)."""
from __future__ import annotations
import json
import os
import subprocess
import sys
from pathlib import Path
import pytest
from sqlit.domains.connections.cli import completion
def _write_connections(config_dir: Path, payload: object) -> None:
config_dir.mkdir(parents=True, exist_ok=True)
(config_dir / "connections.json").write_text(json.dumps(payload), encoding="utf-8")
# --------------------------------------------------------------------------
# Unit tests for the connection-name completer
# --------------------------------------------------------------------------
def test_complete_connection_names_v2_format(tmp_path: Path, monkeypatch):
_write_connections(
tmp_path,
{"version": 2, "connections": [{"name": "prod-pg"}, {"name": "prod-mysql"}, {"name": "staging"}]},
)
# The completer reads CONFIG_DIR lazily from the store module.
monkeypatch.setattr("sqlit.shared.core.store.CONFIG_DIR", tmp_path)
assert completion.complete_connection_names("prod") == ["prod-pg", "prod-mysql"]
assert set(completion.complete_connection_names("")) == {"prod-pg", "prod-mysql", "staging"}
assert completion.complete_connection_names("zzz") == []
def test_complete_connection_names_legacy_list_format(tmp_path: Path, monkeypatch):
_write_connections(tmp_path, [{"name": "legacy-one"}, {"name": "legacy-two"}])
monkeypatch.setattr("sqlit.shared.core.store.CONFIG_DIR", tmp_path)
assert set(completion.complete_connection_names("")) == {"legacy-one", "legacy-two"}
def test_complete_connection_names_missing_file_is_safe(tmp_path: Path, monkeypatch):
monkeypatch.setattr("sqlit.shared.core.store.CONFIG_DIR", tmp_path / "nope")
assert completion.complete_connection_names("") == []
def test_complete_connection_names_malformed_json_is_safe(tmp_path: Path, monkeypatch):
(tmp_path).mkdir(parents=True, exist_ok=True)
(tmp_path / "connections.json").write_text("{not valid json", encoding="utf-8")
monkeypatch.setattr("sqlit.shared.core.store.CONFIG_DIR", tmp_path)
assert completion.complete_connection_names("") == []
# --------------------------------------------------------------------------
# `sqlit completion <shell>` subcommand
# --------------------------------------------------------------------------
@pytest.mark.parametrize("shell", ["bash", "zsh", "fish"])
def test_completion_subcommand_prints_script(shell: str):
pytest.importorskip("argcomplete")
result = subprocess.run(
[sys.executable, "-m", "sqlit.cli", "completion", shell],
capture_output=True,
text=True,
)
assert result.returncode == 0, result.stderr
assert result.stdout.strip(), "expected a non-empty completion script"
assert "sqlit" in result.stdout
def test_completion_subcommand_rejects_unknown_shell():
result = subprocess.run(
[sys.executable, "-m", "sqlit.cli", "completion", "powershell"],
capture_output=True,
text=True,
)
assert result.returncode == 2
assert "invalid choice" in result.stderr
# --------------------------------------------------------------------------
# End-to-end argcomplete protocol
# --------------------------------------------------------------------------
def _run_completion(comp_line: str, config_dir: Path) -> list[str]:
"""Drive the argcomplete protocol and return the emitted candidates.
argcomplete writes the newline/IFS-separated candidates to fd 8, so we use
a bash redirection (`8>file`) to capture them.
"""
out_file = config_dir / "_comp_out"
inner = f"exec {sys.executable!s} -m sqlit.cli 8>{out_file!s} 9>/dev/null 2>/dev/null"
env = {
**os.environ,
"SQLIT_CONFIG_DIR": str(config_dir),
"_ARGCOMPLETE": "1",
"_ARGCOMPLETE_SHELL": "bash",
"_ARGCOMPLETE_COMP_WORDBREAKS": " \t\n\"'><=;|&(:",
"COMP_LINE": comp_line,
"COMP_POINT": str(len(comp_line)),
"COMP_TYPE": "9",
}
subprocess.run(["bash", "-c", inner], env=env)
if not out_file.exists():
return []
raw = out_file.read_text(encoding="utf-8", errors="replace")
# Candidates are separated by the IFS argcomplete uses (\013) or whitespace.
parts = raw.replace("\013", "\n").split("\n")
return [p.strip() for p in parts if p.strip()]
def test_argcomplete_completes_subcommands(tmp_path: Path):
pytest.importorskip("argcomplete")
candidates = _run_completion("sqlit ", tmp_path)
for expected in ("connections", "connect", "query", "alerts", "completion"):
assert expected in candidates, f"{expected!r} missing from {candidates}"
def test_argcomplete_completes_providers_for_connect(tmp_path: Path):
pytest.importorskip("argcomplete")
candidates = _run_completion("sqlit connect ", tmp_path)
for expected in ("postgresql", "mysql", "sqlite"):
assert expected in candidates
def test_argcomplete_completes_saved_connection_names(tmp_path: Path):
pytest.importorskip("argcomplete")
_write_connections(
tmp_path,
{"version": 2, "connections": [{"name": "prod-pg"}, {"name": "prod-mysql"}, {"name": "staging"}]},
)
candidates = _run_completion("sqlit query --connection prod", tmp_path)
assert set(candidates) == {"prod-pg", "prod-mysql"}