Skip to content

Commit 09eedff

Browse files
committed
Allow deletion of a single impostor from a running server. Also improve query_all_impostors() so that the impostor instances are attached to the server they are from.
Signed-off-by: Simon Brunning <simon@brunningonline.net>
1 parent 416bf96 commit 09eedff

File tree

3 files changed

+93
-25
lines changed

3 files changed

+93
-25
lines changed

src/mbtest/matchers.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def describe_mismatch(
112112
description.append_text(". All requests: ").append_description_of(self.all_requests)
113113

114114
def _matches(self, actual: Union[Imposter, MountebankServer]) -> bool:
115-
self.all_requests = cast(Sequence[HttpRequest], list(actual.get_actual_requests()))
115+
self.all_requests = cast(Sequence[HttpRequest], actual.get_actual_requests())
116116
self.matching_requests = [
117117
request
118118
for request in self.all_requests

src/mbtest/server.py

Lines changed: 36 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,10 @@
33
import subprocess # nosec
44
import time
55
from collections import abc
6+
from operator import attrgetter
67
from pathlib import Path
78
from threading import Lock
8-
from typing import Iterable, Iterator, List, MutableSequence, Optional, Sequence, Set, Union
9+
from typing import Iterable, List, MutableSequence, Optional, Sequence, Set, Union
910

1011
import requests
1112
from _pytest.fixtures import FixtureRequest # type: ignore
@@ -138,39 +139,59 @@ def add_imposters(self, definition: Union[Imposter, Iterable[Imposter]]) -> None
138139
"""Add imposters to Mountebank server.
139140
140141
:param definition: One or more Imposters.
141-
:type definition: Imposter or list(Imposter)
142142
"""
143143
if isinstance(definition, abc.Iterable):
144144
for imposter in definition:
145145
self.add_imposters(imposter)
146146
else:
147-
json = definition.as_structure()
148-
post = requests.post(self.server_url, json=json, timeout=10)
149-
post.raise_for_status()
150-
definition.attach(self.host, post.json()["port"], self.server_url)
151-
self._running_imposters.append(definition)
147+
self.add_impostor(definition)
148+
149+
def add_impostor(self, definition):
150+
"""Add single imposter to Mountebank server.
151+
152+
:param definition: One or more Imposters."""
153+
json = definition.as_structure()
154+
post = requests.post(self.server_url, json=json, timeout=10)
155+
post.raise_for_status()
156+
definition.attach(self.host, post.json()["port"], self.server_url)
157+
self._running_imposters.append(definition)
152158

153159
def delete_imposters(self) -> None:
160+
"""Delete all impostors from server."""
154161
while self._running_imposters:
155-
imposter = self._running_imposters.pop()
156-
requests.delete(imposter.configuration_url).raise_for_status()
162+
self.delete_impostor(self._running_imposters[0])
163+
164+
def delete_impostor(self, imposter):
165+
"""Delete impostor from server."""
166+
requests.delete(imposter.configuration_url).raise_for_status()
167+
self._running_imposters = [
168+
i for i in self._running_imposters if i.configuration_url != imposter.configuration_url
169+
]
157170

158-
def get_actual_requests(self) -> Iterable[Request]:
171+
def get_actual_requests(self) -> Sequence[Request]:
172+
actual_requests: MutableSequence[Request] = []
159173
for imposter in self._running_imposters:
160-
yield from imposter.get_actual_requests()
174+
actual_requests += imposter.get_actual_requests()
175+
return actual_requests
161176

162177
@property
163178
def server_url(self) -> furl:
164179
return furl().set(
165180
scheme=self.scheme, host=self.host, port=self.server_port, path=self.imposters_path
166181
)
167182

168-
def query_all_imposters(self) -> Iterator[Imposter]:
183+
def query_all_imposters(self) -> Sequence[Imposter]:
169184
"""Yield all imposters running on the server, including those defined elsewhere."""
170185
server_info = requests.get(self.server_url)
171-
imposters = server_info.json()["imposters"]
172-
for imposter in imposters:
173-
yield Imposter.from_structure(requests.get(imposter["_links"]["self"]["href"]).json())
186+
imposters_structure = server_info.json()["imposters"]
187+
all_imposters: MutableSequence[Imposter] = []
188+
for imposter_structure in imposters_structure:
189+
impostor_url = imposter_structure["_links"]["self"]["href"]
190+
imposter = Imposter.from_structure(requests.get(impostor_url).json())
191+
imposter.host = self.host
192+
imposter.server_url = self.server_url
193+
all_imposters.append(imposter)
194+
return sorted(all_imposters, key=attrgetter("port"))
174195

175196

176197
class ExecutingMountebankServer(MountebankServer):

tests/integration/test_server.py

Lines changed: 56 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -98,17 +98,64 @@ def test_query_all_imposters(mock_server):
9898
imposter2 = Imposter(Stub(Predicate(path="/test2"), Response(body="egg")))
9999

100100
with mock_server([imposter1, imposter2]) as server:
101-
actual = list(server.query_all_imposters())
101+
actual = server.query_all_imposters()
102102
assert_that(
103103
actual,
104104
contains_inanyorder(
105-
has_identical_properties_to(
106-
imposter1,
107-
ignoring={"host", "url", "server_url", "configuration_url", "attached"},
108-
),
109-
has_identical_properties_to(
110-
imposter2,
111-
ignoring={"host", "url", "server_url", "configuration_url", "attached"},
112-
),
105+
has_identical_properties_to(imposter1), has_identical_properties_to(imposter2)
106+
),
107+
)
108+
109+
110+
def test_removing_impostor_from_running_server(mock_server):
111+
# Set up server
112+
with mock_server(
113+
[
114+
Imposter(Stub(Predicate(path="/test"), Response(body="sausage")), name="sausage"),
115+
Imposter(Stub(Predicate(path="/test"), Response(body="egg")), name="egg"),
116+
Imposter(Stub(Predicate(path="/test"), Response(body="chips")), name="chips"),
117+
]
118+
) as server:
119+
120+
# Retrieve impostor details from running server, and check they work
121+
initial = server.query_all_imposters()
122+
123+
responses = [requests.get(f"{initial[i].url}/test") for i in range(3)]
124+
assert_that(
125+
responses,
126+
contains_inanyorder(
127+
is_response().with_body("sausage"),
128+
is_response().with_body("egg"),
129+
is_response().with_body("chips"),
130+
),
131+
)
132+
133+
# Delete one impostor, make sure it's gone, and that the rest still work
134+
egg_impostor = [i for i in initial if i.name == "egg"][0]
135+
other_impostors = [i for i in initial if i.name != "egg"]
136+
server.delete_impostor(egg_impostor)
137+
138+
with pytest.raises(requests.exceptions.ConnectionError):
139+
requests.get(f"{egg_impostor.url}/test")
140+
responses = [requests.get(f"{i.url}/test") for i in other_impostors]
141+
assert_that(
142+
responses,
143+
contains_inanyorder(
144+
is_response().with_body("sausage"),
145+
is_response().with_body("chips"),
146+
),
147+
)
148+
149+
# Reset the server from the initial impostors, and check it's back to normal
150+
server.delete_imposters()
151+
server.add_imposters(initial)
152+
153+
responses = [requests.get(f"{initial[i].url}/test") for i in range(3)]
154+
assert_that(
155+
responses,
156+
contains_inanyorder(
157+
is_response().with_body("sausage"),
158+
is_response().with_body("egg"),
159+
is_response().with_body("chips"),
113160
),
114161
)

0 commit comments

Comments
 (0)