Skip to content

Commit b9696af

Browse files
author
Henry Dai
committed
Merge remote-tracking branch 'other-repo/henrydai/ImplementCRUDforChangeState' into henrydai/implementChangeStateCRUD0901
2 parents 1f12482 + 1636d5d commit b9696af

File tree

2 files changed

+162
-11
lines changed

2 files changed

+162
-11
lines changed

src/change-state/azext_change_state/commands.py

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,14 @@
1212

1313

1414
def load_command_table(self, _): # pylint: disable=unused-argument
15-
"""Apply custom command overrides after the AAZ-generated command table is loaded."""
16-
# Register custom commands for both 'change-state' and 'change-safety change-state'
17-
for prefix in ["change-state", "change-safety change-state"]:
18-
if f"{prefix} create" in self.command_table:
19-
self.command_table[f"{prefix} create"] = custom.ChangeStateCreate(loader=self)
20-
if f"{prefix} update" in self.command_table:
21-
self.command_table[f"{prefix} update"] = custom.ChangeStateUpdate(loader=self)
22-
if f"{prefix} show" in self.command_table:
23-
self.command_table[f"{prefix} show"] = custom.ChangeStateShow(loader=self)
24-
if f"{prefix} delete" in self.command_table:
25-
self.command_table[f"{prefix} delete"] = custom.ChangeStateDelete(loader=self)
15+
from .custom import ChangeStateCreate, ChangeStateUpdate, ChangeStateDelete, ChangeStateShow
16+
17+
create_command = ChangeStateCreate(loader=self)
18+
update_command = ChangeStateUpdate(loader=self)
19+
delete_command = ChangeStateDelete(loader=self)
20+
show_command = ChangeStateShow(loader=self)
21+
22+
self.command_table['change-state create'] = create_command
23+
self.command_table['change-state update'] = update_command
24+
self.command_table['change-state delete'] = delete_command
25+
self.command_table['change-state show'] = show_command

src/change-state/azext_change_state/tests/latest/test_change_state.py

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,160 @@
55
# Code generated by aaz-dev-tools
66
# --------------------------------------------------------------------------------------------
77

8+
from types import SimpleNamespace
9+
10+
import pytest
811
from azure.cli.testsdk import *
912

13+
from azext_change_state.custom import (
14+
ChangeStateCreate,
15+
ChangeStateUpdate,
16+
_inject_change_definition_into_content,
17+
_inject_targets_into_result,
18+
)
19+
from azure.cli.core.azclierror import InvalidArgumentValueError
20+
1021

1122
class ChangeStateScenario(ScenarioTest):
1223
# TODO: add tests here
1324
pass
25+
26+
27+
class _SerializableValue:
28+
"""Test helper that mimics serialized AAZ values."""
29+
30+
def __init__(self, data):
31+
self._data = data
32+
33+
def to_serialized_data(self):
34+
return self._data
35+
36+
37+
def test_parse_targets_single_entry():
38+
result = ChangeStateCreate._parse_targets(["env=prod"])
39+
assert result == [{"env": "prod"}]
40+
41+
42+
def test_parse_targets_with_multiple_delimiters():
43+
tokens = ["env=prod,region=us; role=web "]
44+
result = ChangeStateCreate._parse_targets(tokens)
45+
assert result == [{"env": "prod", "region": "us", "role": "web"}]
46+
47+
48+
def test_parse_targets_rejects_invalid_entries():
49+
with pytest.raises(InvalidArgumentValueError):
50+
ChangeStateCreate._parse_targets(["invalid"])
51+
52+
53+
def test_parse_targets_maps_resource_group_alias():
54+
result = ChangeStateCreate._parse_targets(["rg=my-group"])
55+
assert result == [{"resourceGroupName": "my-group"}]
56+
57+
58+
def test_parse_targets_uppercases_http_method_value():
59+
result = ChangeStateCreate._parse_targets(["httpMethod=delete"])
60+
assert result == [{"httpMethod": "DELETE"}]
61+
62+
63+
def test_parse_targets_maps_operation_to_http_method():
64+
result = ChangeStateCreate._parse_targets(["operation=POST,resource=/abc"])
65+
assert result == [{"httpMethod": "POST", "resource": "/abc"}]
66+
67+
68+
def test_build_change_definition_uses_targets_and_name():
69+
cmd = object.__new__(ChangeStateCreate)
70+
cmd._raw_targets = ["env=prod"]
71+
cmd.ctx = SimpleNamespace(args=SimpleNamespace(change_state_name=_SerializableValue("test-change")))
72+
definition = ChangeStateCreate._build_change_definition(cmd)
73+
assert definition == {
74+
"kind": "Targets",
75+
"name": "test-change",
76+
"details": {"targets": [{"env": "prod"}]}
77+
}
78+
79+
80+
def test_build_change_definition_normalizes_operation():
81+
cmd = object.__new__(ChangeStateCreate)
82+
cmd._raw_targets = ["operation=post"]
83+
cmd.ctx = SimpleNamespace(args=SimpleNamespace(change_state_name=_SerializableValue("test-change")))
84+
definition = ChangeStateCreate._build_change_definition(cmd)
85+
assert definition["details"]["targets"] == [{"httpMethod": "POST"}]
86+
87+
88+
def test_build_change_definition_handles_serializable_value():
89+
class DummyName:
90+
def to_serialized_data(self):
91+
return "serialized-name"
92+
93+
cmd = object.__new__(ChangeStateCreate)
94+
cmd._raw_targets = ["env=prod"]
95+
cmd.ctx = SimpleNamespace(args=SimpleNamespace(change_state_name=DummyName()))
96+
definition = ChangeStateCreate._build_change_definition(cmd)
97+
assert definition["name"] == "serialized-name"
98+
99+
100+
def test_command_name_overrides():
101+
assert ChangeStateCreate.AZ_NAME == "change-safety change-state create"
102+
assert ChangeStateUpdate.AZ_NAME == "change-safety change-state update"
103+
104+
105+
def test_inject_change_definition_into_dict_payload():
106+
ctx = SimpleNamespace(vars=SimpleNamespace(change_definition=_SerializableValue(_valid_change_definition())))
107+
original = {"properties": {"existing": "value"}}
108+
updated = _inject_change_definition_into_content(original, ctx)
109+
assert updated["properties"]["changeDefinition"] == _valid_change_definition()
110+
assert updated["properties"]["existing"] == "value"
111+
112+
113+
def test_inject_change_definition_into_content_when_none():
114+
ctx = SimpleNamespace(vars=SimpleNamespace(change_definition=_SerializableValue(_valid_change_definition())))
115+
payload = _inject_change_definition_into_content(None, ctx)
116+
assert payload["properties"]["changeDefinition"] == _valid_change_definition()
117+
118+
119+
def test_inject_change_definition_skips_empty_definition():
120+
ctx = SimpleNamespace(vars=SimpleNamespace(change_definition=_SerializableValue({})))
121+
original = {"properties": {}}
122+
updated = _inject_change_definition_into_content(original, ctx)
123+
assert updated is original
124+
125+
126+
def test_inject_targets_into_result_populates_properties_container():
127+
targets = [{"env": "prod"}]
128+
data = {"properties": {"changeDefinition": {"details": {}}}}
129+
_inject_targets_into_result(data, targets)
130+
assert data["properties"]["changeDefinition"]["details"]["targets"] == targets
131+
132+
133+
def test_inject_targets_into_result_handles_list_payloads():
134+
targets = [{"httpMethod": "POST"}]
135+
data = [{"changeDefinition": {"details": {}}}]
136+
_inject_targets_into_result(data, targets)
137+
assert data[0]["changeDefinition"]["details"]["targets"] == targets
138+
139+
140+
def test_create_operation_content_includes_change_definition():
141+
args_schema = ChangeStateCreate._build_arguments_schema()
142+
args = args_schema()
143+
args.change_state_name = "test-change"
144+
args.change_type = "AppDeployment"
145+
args.rollout_type = "Normal"
146+
args.anticipated_start_time = "2024-11-01T08:00:00Z"
147+
args.anticipated_end_time = "2024-11-01T10:00:00Z"
148+
ctx = SimpleNamespace(
149+
args=args,
150+
vars=SimpleNamespace(change_definition=_SerializableValue(_valid_change_definition()))
151+
)
152+
153+
op = object.__new__(ChangeStateCreate.ChangeStatesCreateOrUpdate)
154+
op.ctx = ctx
155+
payload = op.content
156+
assert payload["properties"]["changeDefinition"] == _valid_change_definition()
157+
158+
159+
def _valid_change_definition():
160+
return {
161+
"kind": "Targets",
162+
"name": "test-change",
163+
"details": {"targets": [{"resourceId": "/foo", "operation": "DELETE"}]},
164+
}

0 commit comments

Comments
 (0)