Skip to content

Commit f3e8e00

Browse files
committed
feat(stacks): Added unit testcases for stack resource
1 parent b0d25d5 commit f3e8e00

1 file changed

Lines changed: 267 additions & 0 deletions

File tree

tests/units/test_stack.py

Lines changed: 267 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,267 @@
1+
# Copyright IBM Corp. 2025, 2026
2+
# SPDX-License-Identifier: MPL-2.0
3+
4+
"""Unit tests for the stack module."""
5+
6+
from unittest.mock import Mock
7+
8+
import pytest
9+
10+
from pytfe._http import HTTPTransport
11+
from pytfe.models.agent import AgentPool
12+
from pytfe.models.project import Project
13+
from pytfe.models.stack import (
14+
Stack,
15+
StackCreateOptions,
16+
StackListOptions,
17+
StackSortColumn,
18+
StackUpdateOptions,
19+
StackVcsRepoOptions,
20+
)
21+
from pytfe.resources.stack import Stacks
22+
23+
24+
class TestStacks:
25+
"""Test the Stacks service class."""
26+
27+
@pytest.fixture
28+
def mock_transport(self):
29+
"""Create a mock HTTPTransport."""
30+
return Mock(spec=HTTPTransport)
31+
32+
@pytest.fixture
33+
def stacks_service(self, mock_transport):
34+
"""Create a Stacks service with mocked transport."""
35+
return Stacks(mock_transport)
36+
37+
@pytest.fixture
38+
def stack_response_data(self):
39+
"""Return sample API response data for a stack."""
40+
return {
41+
"id": "st-123",
42+
"attributes": {
43+
"name": "demo-stack",
44+
"description": "Stack description",
45+
"speculation-enabled": True,
46+
"vcs-repo": {
47+
"identifier": "hashicorp/terraform",
48+
"branch": "main",
49+
"oauth-token-id": "ot-123",
50+
},
51+
},
52+
"relationships": {
53+
"project": {"data": {"id": "prj-123", "type": "projects"}},
54+
"agent-pool": {
55+
"data": {"id": "apool-123", "type": "agent-pools"}
56+
},
57+
},
58+
}
59+
60+
def test_list_stacks_success(self, stacks_service, stack_response_data):
61+
"""Test successful list operation."""
62+
stacks_service._list = Mock(return_value=[stack_response_data])
63+
64+
options = StackListOptions(
65+
page_size=10,
66+
project_id="prj-123",
67+
sort=StackSortColumn.STACK_SORT_BY_NAME,
68+
search_by_name="demo",
69+
)
70+
71+
result_iter = stacks_service.list("org-123", options)
72+
items = list(result_iter)
73+
74+
stacks_service._list.assert_called_once_with(
75+
"/api/v2/organizations/org-123/stacks",
76+
params={
77+
"page[size]": 10,
78+
"filter[project][id]": "prj-123",
79+
"sort": "name",
80+
"search[name]": "demo",
81+
},
82+
)
83+
84+
assert len(items) == 1
85+
assert isinstance(items[0], Stack)
86+
assert items[0].id == "st-123"
87+
assert items[0].name == "demo-stack"
88+
89+
def test_create_stack_success(self, stacks_service, mock_transport, stack_response_data):
90+
"""Test successful create operation."""
91+
mock_response = Mock()
92+
mock_response.json.return_value = {"data": stack_response_data}
93+
mock_transport.request.return_value = mock_response
94+
95+
options = StackCreateOptions(
96+
name="demo-stack",
97+
description="Stack description",
98+
speculation_enabled=True,
99+
vcs_repo=StackVcsRepoOptions(
100+
identifier="hashicorp/terraform",
101+
branch="main",
102+
oauth_token_id="ot-123",
103+
),
104+
project=Project(id="prj-123"),
105+
agent_pool=AgentPool(id="apool-123"),
106+
)
107+
108+
result = stacks_service.create(options)
109+
110+
mock_transport.request.assert_called_once_with(
111+
"POST",
112+
path="/api/v2/stacks",
113+
json_body={
114+
"data": {
115+
"attributes": {
116+
"name": "demo-stack",
117+
"description": "Stack description",
118+
"speculation-enabled": True,
119+
"vcs-repo": {
120+
"identifier": "hashicorp/terraform",
121+
"branch": "main",
122+
"oauth-token-id": "ot-123",
123+
},
124+
},
125+
"type": "stacks",
126+
"relationships": {
127+
"project": {
128+
"data": {"id": "prj-123", "type": "projects"}
129+
},
130+
"agent-pool": {
131+
"data": {"id": "apool-123", "type": "agent-pools"}
132+
},
133+
},
134+
}
135+
},
136+
)
137+
138+
assert isinstance(result, Stack)
139+
assert result.id == "st-123"
140+
assert result.project.id == "prj-123"
141+
assert result.agent_pool.id == "apool-123"
142+
143+
def test_update_stack_success(self, stacks_service, mock_transport, stack_response_data):
144+
"""Test successful update operation."""
145+
mock_response = Mock()
146+
mock_response.json.return_value = {"data": stack_response_data}
147+
mock_transport.request.return_value = mock_response
148+
149+
options = StackUpdateOptions(
150+
description="Updated description",
151+
vcs_repo=StackVcsRepoOptions(
152+
identifier="hashicorp/terraform",
153+
branch="main",
154+
),
155+
project=Project(id="prj-123"),
156+
agent_pool=AgentPool(id="apool-123"),
157+
)
158+
159+
result = stacks_service.update("st-123", options)
160+
161+
mock_transport.request.assert_called_once_with(
162+
"PATCH",
163+
path="/api/v2/stacks/st-123",
164+
json_body={
165+
"data": {
166+
"attributes": {
167+
"description": "Updated description",
168+
"vcs-repo": {
169+
"identifier": "hashicorp/terraform",
170+
"branch": "main",
171+
},
172+
},
173+
"type": "stacks",
174+
"relationships": {
175+
"project": {
176+
"data": {"id": "prj-123", "type": "projects"}
177+
},
178+
"agent-pool": {
179+
"data": {"id": "apool-123", "type": "agent-pools"}
180+
},
181+
},
182+
}
183+
},
184+
)
185+
186+
assert isinstance(result, Stack)
187+
assert result.id == "st-123"
188+
189+
def test_read_stack_success(self, stacks_service, mock_transport, stack_response_data):
190+
"""Test successful read operation."""
191+
mock_response = Mock()
192+
mock_response.json.return_value = {"data": stack_response_data}
193+
mock_transport.request.return_value = mock_response
194+
195+
result = stacks_service.read("st-123")
196+
197+
mock_transport.request.assert_called_once_with(
198+
"GET",
199+
path="/api/v2/stacks/st-123",
200+
)
201+
202+
assert isinstance(result, Stack)
203+
assert result.id == "st-123"
204+
assert result.name == "demo-stack"
205+
206+
def test_delete_stack_success(self, stacks_service, mock_transport):
207+
"""Test successful delete operation."""
208+
result = stacks_service.delete("st-123")
209+
210+
mock_transport.request.assert_called_once_with(
211+
"DELETE",
212+
path="/api/v2/stacks/st-123",
213+
)
214+
assert result is None
215+
216+
def test_force_delete_stack_success(self, stacks_service, mock_transport):
217+
"""Test successful force-delete operation."""
218+
result = stacks_service.force_delete("st-123")
219+
220+
mock_transport.request.assert_called_once_with(
221+
"DELETE",
222+
path="/api/v2/stacks/st-123?force=true",
223+
)
224+
assert result is None
225+
226+
def test_stack_from_handles_null_vcs_repo(self, stacks_service):
227+
"""Test parsing stack data when vcs-repo is null."""
228+
data = {
229+
"id": "st-456",
230+
"attributes": {
231+
"name": "no-vcs-stack",
232+
"vcs-repo": None,
233+
},
234+
"relationships": {
235+
"project": {"data": {"id": "prj-999", "type": "projects"}},
236+
},
237+
}
238+
239+
result = stacks_service._stack_from(data)
240+
241+
assert isinstance(result, Stack)
242+
assert result.id == "st-456"
243+
assert result.vcs_repo is None
244+
assert result.project is not None
245+
assert result.project.id == "prj-999"
246+
assert result.agent_pool is None
247+
248+
def test_stack_from_handles_missing_relationships(self, stacks_service):
249+
"""Test parsing stack data when relationship data is missing."""
250+
data = {
251+
"id": "st-789",
252+
"attributes": {
253+
"name": "minimal-stack",
254+
"vcs-repo": None,
255+
},
256+
"relationships": {
257+
"project": {"data": None},
258+
"agent-pool": {"data": None},
259+
},
260+
}
261+
262+
result = stacks_service._stack_from(data)
263+
264+
assert isinstance(result, Stack)
265+
assert result.id == "st-789"
266+
assert result.project is None
267+
assert result.agent_pool is None

0 commit comments

Comments
 (0)