feat: param_value context — enum/allowed completions for principalType, environmentType etc.
- Add _KNOWN_ENUMS dict (principalType, principalObjectType, environmentType fallbacks) - Add param_value_completion_items() to BicepModuleCatalog - Detect 'param_value' context in _detect_context() (cursor after 'param: ' inside params block) - Wire param_value into _inject_completions() - 9 new unit tests (context detection, catalog allowed, known enum fallback, injection) - Fix modules.py edit regression (param_completion_items was orphaned) - All 35 tests pass
This commit is contained in:
@@ -240,7 +240,7 @@ def test_detect_param_context():
|
||||
"module myMod 'br/modules:roleassignments:1.1.x' = {",
|
||||
" name: 'test'",
|
||||
" params: {",
|
||||
" ", # ← cursor here
|
||||
" ", # ← cursor here (blank line, no param name yet)
|
||||
" }",
|
||||
"}",
|
||||
]
|
||||
@@ -251,6 +251,124 @@ def test_detect_param_context():
|
||||
assert ctx["version"] == "1.1.x"
|
||||
|
||||
|
||||
def test_detect_param_value_context_no_quote():
|
||||
"""Cursor after 'principalType: ' (no opening quote) → param_value context."""
|
||||
lines = [
|
||||
"module myMod 'br/modules:roleassignments:1.1.x' = {",
|
||||
" params: {",
|
||||
" principalType: ", # ← cursor at end
|
||||
" }",
|
||||
"}",
|
||||
]
|
||||
session = _make_session_with_doc(URI, lines)
|
||||
# character = len(" principalType: ") = 19
|
||||
ctx = session._detect_context(URI, {"line": 2, "character": 19})
|
||||
assert ctx["type"] == "param_value"
|
||||
assert ctx["module"] == "roleassignments"
|
||||
assert ctx["param"] == "principalType"
|
||||
assert ctx["has_open_quote"] is False
|
||||
|
||||
|
||||
def test_detect_param_value_context_open_quote():
|
||||
"""Cursor after 'principalType: \\'' → param_value with has_open_quote=True."""
|
||||
lines = [
|
||||
"module myMod 'br/modules:roleassignments:1.1.x' = {",
|
||||
" params: {",
|
||||
" principalType: '", # ← cursor after opening quote
|
||||
" }",
|
||||
"}",
|
||||
]
|
||||
session = _make_session_with_doc(URI, lines)
|
||||
ctx = session._detect_context(URI, {"line": 2, "character": 20})
|
||||
assert ctx["type"] == "param_value"
|
||||
assert ctx["has_open_quote"] is True
|
||||
|
||||
|
||||
def test_param_value_items_from_catalog_allowed():
|
||||
"""environmentType completions come from catalog 'allowed' field."""
|
||||
BicepModuleCatalog._modules = [_make_module(
|
||||
"roleassignments",
|
||||
versions=["1.1.x"],
|
||||
schema={"1.1.x": {"parameters": {
|
||||
"environmentType": {"type": "string", "allowed": ["DEV", "TEST", "PROD"]},
|
||||
}}},
|
||||
)]
|
||||
items = BicepModuleCatalog.param_value_completion_items(
|
||||
"roleassignments", "1.1.x", "environmentType"
|
||||
)
|
||||
labels = [i["label"] for i in items]
|
||||
assert labels == ["DEV", "TEST", "PROD"]
|
||||
# Without open quote, insertText should wrap in quotes
|
||||
assert items[0]["insertText"] == "'DEV'"
|
||||
|
||||
|
||||
def test_param_value_items_open_quote():
|
||||
"""When has_open_quote=True, insertText closes the quote but doesn't open one."""
|
||||
BicepModuleCatalog._modules = [_make_module(
|
||||
"roleassignments",
|
||||
versions=["1.1.x"],
|
||||
schema={"1.1.x": {"parameters": {
|
||||
"environmentType": {"type": "string", "allowed": ["DEV", "TEST", "PROD"]},
|
||||
}}},
|
||||
)]
|
||||
items = BicepModuleCatalog.param_value_completion_items(
|
||||
"roleassignments", "1.1.x", "environmentType", has_open_quote=True
|
||||
)
|
||||
assert items[0]["insertText"] == "DEV'"
|
||||
|
||||
|
||||
def test_param_value_items_known_enum_fallback():
|
||||
"""principalType uses _KNOWN_ENUMS fallback when not in catalog."""
|
||||
BicepModuleCatalog._modules = [_make_module(
|
||||
"roleassignments",
|
||||
versions=["1.1.x"],
|
||||
schema={"1.1.x": {"parameters": {
|
||||
"principalType": {"type": "string"}, # no 'allowed' in catalog
|
||||
}}},
|
||||
)]
|
||||
items = BicepModuleCatalog.param_value_completion_items(
|
||||
"roleassignments", "1.1.x", "principalType"
|
||||
)
|
||||
labels = [i["label"] for i in items]
|
||||
assert "Group" in labels
|
||||
assert "ServicePrincipal" in labels
|
||||
assert "User" in labels
|
||||
|
||||
|
||||
def test_param_value_items_empty_for_free_string():
|
||||
"""A plain string param with no allowed values returns no completions."""
|
||||
BicepModuleCatalog._modules = [_make_module(
|
||||
"roleassignments",
|
||||
versions=["1.1.x"],
|
||||
schema={"1.1.x": {"parameters": {
|
||||
"principalId": {"type": "string"},
|
||||
}}},
|
||||
)]
|
||||
items = BicepModuleCatalog.param_value_completion_items(
|
||||
"roleassignments", "1.1.x", "principalId"
|
||||
)
|
||||
assert items == []
|
||||
|
||||
|
||||
def test_param_value_injected_in_completion_response():
|
||||
"""Full pipeline: param_value context injects enum completions at top."""
|
||||
BicepModuleCatalog._modules = [_make_module(
|
||||
"roleassignments",
|
||||
versions=["1.1.x"],
|
||||
schema={"1.1.x": {"parameters": {
|
||||
"environmentType": {"type": "string", "allowed": ["DEV", "TEST", "PROD"]},
|
||||
}}},
|
||||
)]
|
||||
msg = _completion_response([{"label": "existing", "sortText": "z"}])
|
||||
ctx = {"type": "param_value", "module": "roleassignments", "version": "1.1.x",
|
||||
"param": "environmentType", "has_open_quote": False}
|
||||
out = json.loads(_inject_completions(msg, ctx))
|
||||
labels = [i["label"] for i in out["result"]["items"]]
|
||||
# LRU enum values should be first
|
||||
assert labels[:3] == ["DEV", "TEST", "PROD"]
|
||||
assert "existing" in labels
|
||||
|
||||
|
||||
def test_detect_unknown_context_outside_module():
|
||||
lines = ["var x = 'hello'"]
|
||||
session = _make_session_with_doc(URI, lines)
|
||||
|
||||
Reference in New Issue
Block a user