Skip to content

Commit 5f329f1

Browse files
committed
added unit testcases and updated changelog
1 parent 4546fe3 commit 5f329f1

2 files changed

Lines changed: 156 additions & 0 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
# Unreleased
22

3+
## Enhancements
4+
5+
### New resources
6+
* Added `client.stack_deployments` — list the deployments that belong to a stack. `list(stack_id, options=None)` (`GET /stacks/{stack_id}/stack-deployments`) returns an `Iterator[StackDeployment]`, with optional pagination (`page_size`) and `?include=` (`latest_deployment_run`, `latest_deployment_run.stack_configuration`) via `StackDeploymentListOptions`. The `stack` relationship is hydrated as a typed field; the `latest-deployment-run` relation is reachable via the lossless raw accessors (`deployment.related("latest-deployment-run")`). New models: `StackDeployment`, `StackDeploymentListOptions`, `StackDeploymentIncludeOpt`.
7+
38
# Released
49
# v1.2.0
510

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
# Copyright IBM Corp. 2025, 2026
2+
# SPDX-License-Identifier: MPL-2.0
3+
4+
"""Unit tests for the stack_deployment module."""
5+
6+
from unittest.mock import Mock
7+
8+
import pytest
9+
10+
from pytfe._http import HTTPTransport
11+
from pytfe.errors import InvalidStackIDError
12+
from pytfe.models.stack import Stack
13+
from pytfe.models.stack_deployment import (
14+
StackDeployment,
15+
StackDeploymentIncludeOpt,
16+
StackDeploymentListOptions,
17+
)
18+
from pytfe.resources.stack_deployment import StackDeployments
19+
20+
21+
class TestStackDeployments:
22+
"""Test the StackDeployments service class."""
23+
24+
@pytest.fixture
25+
def mock_transport(self):
26+
"""Create a mock HTTPTransport."""
27+
return Mock(spec=HTTPTransport)
28+
29+
@pytest.fixture
30+
def service(self, mock_transport):
31+
"""Create a StackDeployments service with mocked transport."""
32+
return StackDeployments(mock_transport)
33+
34+
@pytest.fixture
35+
def stack_deployment_api_data(self):
36+
"""Typical API response item for a single stack deployment."""
37+
return {
38+
"id": "st-MWvJsvy1FCg3bnXY-std-simple",
39+
"type": "stack-deployments",
40+
"attributes": {"name": "simple"},
41+
"relationships": {
42+
"stack": {
43+
"data": {"id": "st-MWvJsvy1FCg3bnXY", "type": "stacks"}
44+
},
45+
"latest-deployment-run": {
46+
"data": {
47+
"id": "sdr-vzub48Y4f7sFBk7J",
48+
"type": "stack-deployment-runs",
49+
}
50+
},
51+
},
52+
}
53+
54+
# ── Model tests ──────────────────────────────────────────────────────────
55+
56+
def test_stack_deployment_parse(self, stack_deployment_api_data):
57+
"""StackDeployment parses id, name, and the stack relation."""
58+
attrs = dict(stack_deployment_api_data["attributes"])
59+
attrs["id"] = stack_deployment_api_data["id"]
60+
deployment = StackDeployment.model_validate(attrs)
61+
assert deployment.id == "st-MWvJsvy1FCg3bnXY-std-simple"
62+
assert deployment.name == "simple"
63+
64+
def test_list_options_serialization(self):
65+
"""StackDeploymentListOptions serializes page[size] and include."""
66+
opts = StackDeploymentListOptions(
67+
page_size=50,
68+
include=[StackDeploymentIncludeOpt.LATEST_DEPLOYMENT_RUN],
69+
)
70+
dumped = opts.model_dump(by_alias=True, exclude_none=True)
71+
assert dumped["page[size]"] == 50
72+
73+
# ── list() tests ─────────────────────────────────────────────────────────
74+
75+
def test_list_invalid_stack_id_raises(self, service):
76+
"""list() with an empty stack id raises InvalidStackIDError on iteration."""
77+
with pytest.raises(InvalidStackIDError):
78+
list(service.list(""))
79+
80+
def test_list_success(self, service, stack_deployment_api_data):
81+
"""list() yields StackDeployment objects from paginated results."""
82+
service._list = Mock(return_value=[stack_deployment_api_data])
83+
84+
opts = StackDeploymentListOptions(page_size=20)
85+
results = list(service.list(stack_id="st-MWvJsvy1FCg3bnXY", options=opts))
86+
87+
service._list.assert_called_once_with(
88+
path="/api/v2/stacks/st-MWvJsvy1FCg3bnXY/stack-deployments",
89+
params={"page[size]": 20},
90+
)
91+
assert len(results) == 1
92+
assert isinstance(results[0], StackDeployment)
93+
assert results[0].id == "st-MWvJsvy1FCg3bnXY-std-simple"
94+
assert results[0].name == "simple"
95+
96+
def test_list_hydrates_stack_relation(self, service, stack_deployment_api_data):
97+
"""list() parses the stack relationship into a typed Stack stub."""
98+
service._list = Mock(return_value=[stack_deployment_api_data])
99+
100+
results = list(service.list(stack_id="st-MWvJsvy1FCg3bnXY"))
101+
102+
assert isinstance(results[0].stack, Stack)
103+
assert results[0].stack.id == "st-MWvJsvy1FCg3bnXY"
104+
105+
def test_list_latest_run_reachable_raw(self, service, stack_deployment_api_data):
106+
"""The unmodelled latest-deployment-run relation is reachable losslessly."""
107+
service._list = Mock(return_value=[stack_deployment_api_data])
108+
109+
results = list(service.list(stack_id="st-MWvJsvy1FCg3bnXY"))
110+
111+
refs = results[0].related("latest-deployment-run")
112+
assert refs[0]["id"] == "sdr-vzub48Y4f7sFBk7J"
113+
# raw escape-hatch data never leaks into model_dump()
114+
assert "relationships" not in results[0].model_dump()
115+
116+
def test_list_with_include(self, service, stack_deployment_api_data):
117+
"""list() passes include param as a comma-separated string."""
118+
service._list = Mock(return_value=[stack_deployment_api_data])
119+
120+
opts = StackDeploymentListOptions(
121+
include=[
122+
StackDeploymentIncludeOpt.LATEST_DEPLOYMENT_RUN,
123+
StackDeploymentIncludeOpt.LATEST_DEPLOYMENT_RUN_STACK_CONFIGURATION,
124+
]
125+
)
126+
list(service.list(stack_id="st-MWvJsvy1FCg3bnXY", options=opts))
127+
128+
_, kwargs = service._list.call_args
129+
assert (
130+
kwargs["params"]["include"]
131+
== "latest_deployment_run,latest_deployment_run.stack_configuration"
132+
)
133+
134+
def test_list_empty(self, service):
135+
"""list() returns an empty iterator when no items are returned."""
136+
service._list = Mock(return_value=[])
137+
138+
results = list(service.list(stack_id="st-MWvJsvy1FCg3bnXY"))
139+
assert results == []
140+
141+
def test_list_no_options(self, service, stack_deployment_api_data):
142+
"""list() works correctly when no options are given."""
143+
service._list = Mock(return_value=[stack_deployment_api_data])
144+
145+
results = list(service.list(stack_id="st-MWvJsvy1FCg3bnXY"))
146+
147+
service._list.assert_called_once_with(
148+
path="/api/v2/stacks/st-MWvJsvy1FCg3bnXY/stack-deployments",
149+
params={},
150+
)
151+
assert len(results) == 1

0 commit comments

Comments
 (0)