Merge ~r00ta/maas:refactoring-baserepository-interface into maas:master
- Git
- lp:~r00ta/maas
- refactoring-baserepository-interface
- Merge into master
Status: | Needs review |
---|---|
Proposed branch: | ~r00ta/maas:refactoring-baserepository-interface |
Merge into: | maas:master |
Diff against target: |
546 lines (+154/-72) 14 files modified
src/maasapiserver/common/db/sequences.py (+4/-0) src/maasapiserver/v3/db/base.py (+10/-4) src/maasapiserver/v3/db/bmc.py (+5/-3) src/maasapiserver/v3/db/machines.py (+5/-3) src/maasapiserver/v3/db/nodes.py (+5/-3) src/maasapiserver/v3/db/resource_pools.py (+14/-15) src/maasapiserver/v3/db/users.py (+5/-3) src/maasapiserver/v3/db/vmcluster.py (+5/-3) src/maasapiserver/v3/db/zones.py (+16/-13) src/maasapiserver/v3/services/resource_pools.py (+10/-2) src/maasapiserver/v3/services/zones.py (+11/-1) src/tests/maasapiserver/v3/db/test_resource_pools.py (+20/-13) src/tests/maasapiserver/v3/db/test_zones.py (+17/-8) src/tests/maasapiserver/v3/services/test_resource_pools.py (+27/-1) |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
MAAS Lander | Needs Fixing | ||
MAAS Maintainers | Pending | ||
Review via email: mp+464818@code.launchpad.net |
Commit message
refactoring: improve BaseRepository interface removing dependencies from the request models
Description of the change
This MP aims to remove the dependency from the request models from the db layer.
Since the ID of the resources comes from the undelying database sequence, we can retrieve the value from the service in order to properly build the model that the repository has to store.
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b refactoring-
STATUS: FAILED
LOG: http://
COMMIT: 1de447f43e4367f
MAAS Lander (maas-lander) wrote : | # |
UNIT TESTS
-b refactoring-
STATUS: FAILED
LOG: http://
COMMIT: 1de447f43e4367f
Unmerged commits
- 1de447f... by Jacopo Rota
-
refactoring: improve BaseRepository interface removing dependencies from the request models
Preview Diff
1 | diff --git a/src/maasapiserver/common/db/sequences.py b/src/maasapiserver/common/db/sequences.py |
2 | new file mode 100644 |
3 | index 0000000..963a7c6 |
4 | --- /dev/null |
5 | +++ b/src/maasapiserver/common/db/sequences.py |
6 | @@ -0,0 +1,4 @@ |
7 | +from sqlalchemy import Sequence |
8 | + |
9 | +ZoneIdSequence = Sequence("maasserver_zone_id_seq") |
10 | +ResourcePoolIdSequence = Sequence("maasserver_resourcepool_id_seq") |
11 | diff --git a/src/maasapiserver/v3/db/base.py b/src/maasapiserver/v3/db/base.py |
12 | index 21ff3cf..26cea78 100644 |
13 | --- a/src/maasapiserver/v3/db/base.py |
14 | +++ b/src/maasapiserver/v3/db/base.py |
15 | @@ -8,15 +8,21 @@ from maasapiserver.v3.models.base import ListResult |
16 | |
17 | T = TypeVar("T") |
18 | |
19 | -K = TypeVar("K") |
20 | |
21 | - |
22 | -class BaseRepository(ABC, Generic[T, K]): |
23 | +class BaseRepository(ABC, Generic[T]): |
24 | def __init__(self, connection: AsyncConnection): |
25 | self.connection = connection |
26 | |
27 | @abstractmethod |
28 | - async def create(self, request: K) -> T: |
29 | + async def get_next_id(self) -> int: |
30 | + """ |
31 | + Get the next ID for a new resource. Usually, this is useful because the ID comes from a sequence defined in the |
32 | + database. |
33 | + """ |
34 | + pass |
35 | + |
36 | + @abstractmethod |
37 | + async def create(self, request: T) -> T: |
38 | pass |
39 | |
40 | @abstractmethod |
41 | diff --git a/src/maasapiserver/v3/db/bmc.py b/src/maasapiserver/v3/db/bmc.py |
42 | index 7dee695..df07a0a 100644 |
43 | --- a/src/maasapiserver/v3/db/bmc.py |
44 | +++ b/src/maasapiserver/v3/db/bmc.py |
45 | @@ -2,15 +2,17 @@ from sqlalchemy import update |
46 | from sqlalchemy.sql.operators import eq |
47 | |
48 | from maasapiserver.common.db.tables import BMCTable |
49 | -from maasapiserver.v3.api.models.requests.bmc import BmcRequest |
50 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
51 | from maasapiserver.v3.db.base import BaseRepository |
52 | from maasapiserver.v3.models.base import ListResult |
53 | from maasapiserver.v3.models.bmc import Bmc |
54 | |
55 | |
56 | -class BmcRepository(BaseRepository[Bmc, BmcRequest]): |
57 | - async def create(self, request: BmcRequest) -> Bmc: |
58 | +class BmcRepository(BaseRepository[Bmc]): |
59 | + async def get_next_id(self) -> int: |
60 | + raise Exception("Not implemented yet.") |
61 | + |
62 | + async def create(self, resource: Bmc) -> Bmc: |
63 | raise Exception("Not implemented yet.") |
64 | |
65 | async def find_by_id(self, id: int) -> Bmc | None: |
66 | diff --git a/src/maasapiserver/v3/db/machines.py b/src/maasapiserver/v3/db/machines.py |
67 | index 3002fb5..85205aa 100644 |
68 | --- a/src/maasapiserver/v3/db/machines.py |
69 | +++ b/src/maasapiserver/v3/db/machines.py |
70 | @@ -11,7 +11,6 @@ from maasapiserver.common.db.tables import ( |
71 | NodeTable, |
72 | UserTable, |
73 | ) |
74 | -from maasapiserver.v3.api.models.requests.machines import MachineRequest |
75 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
76 | from maasapiserver.v3.db.base import BaseRepository |
77 | from maasapiserver.v3.models.base import ListResult |
78 | @@ -19,8 +18,11 @@ from maasapiserver.v3.models.machines import Machine |
79 | from maasserver.enum import NODE_TYPE |
80 | |
81 | |
82 | -class MachinesRepository(BaseRepository[Machine, MachineRequest]): |
83 | - async def create(self, request: MachineRequest) -> Machine: |
84 | +class MachinesRepository(BaseRepository[Machine]): |
85 | + async def get_next_id(self) -> int: |
86 | + raise Exception("Not implemented yet.") |
87 | + |
88 | + async def create(self, machine: Machine) -> Machine: |
89 | raise Exception("Not implemented yet.") |
90 | |
91 | async def find_by_id(self, id: int) -> Machine | None: |
92 | diff --git a/src/maasapiserver/v3/db/nodes.py b/src/maasapiserver/v3/db/nodes.py |
93 | index 2686a7a..1bf1b71 100644 |
94 | --- a/src/maasapiserver/v3/db/nodes.py |
95 | +++ b/src/maasapiserver/v3/db/nodes.py |
96 | @@ -2,15 +2,17 @@ from sqlalchemy import update |
97 | from sqlalchemy.sql.operators import eq |
98 | |
99 | from maasapiserver.common.db.tables import NodeTable |
100 | -from maasapiserver.v3.api.models.requests.nodes import NodeRequest |
101 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
102 | from maasapiserver.v3.db.base import BaseRepository |
103 | from maasapiserver.v3.models.base import ListResult |
104 | from maasapiserver.v3.models.nodes import Node |
105 | |
106 | |
107 | -class NodesRepository(BaseRepository[Node, NodeRequest]): |
108 | - async def create(self, request: NodeRequest) -> Node: |
109 | +class NodesRepository(BaseRepository[Node]): |
110 | + async def get_next_id(self) -> int: |
111 | + raise Exception("Not implemented yet.") |
112 | + |
113 | + async def create(self, resource: Node) -> Node: |
114 | raise Exception("Not implemented yet.") |
115 | |
116 | async def find_by_id(self, id: int) -> Node | None: |
117 | diff --git a/src/maasapiserver/v3/db/resource_pools.py b/src/maasapiserver/v3/db/resource_pools.py |
118 | index 33640ae..525847b 100644 |
119 | --- a/src/maasapiserver/v3/db/resource_pools.py |
120 | +++ b/src/maasapiserver/v3/db/resource_pools.py |
121 | @@ -6,6 +6,7 @@ from sqlalchemy.exc import IntegrityError |
122 | from sqlalchemy.sql.functions import count |
123 | from sqlalchemy.sql.operators import eq |
124 | |
125 | +from maasapiserver.common.db.sequences import ResourcePoolIdSequence |
126 | from maasapiserver.common.db.tables import ResourcePoolTable |
127 | from maasapiserver.common.models.constants import ( |
128 | UNIQUE_CONSTRAINT_VIOLATION_TYPE, |
129 | @@ -15,9 +16,6 @@ from maasapiserver.common.models.exceptions import ( |
130 | BaseExceptionDetail, |
131 | ) |
132 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
133 | -from maasapiserver.v3.api.models.requests.resource_pools import ( |
134 | - ResourcePoolRequest, |
135 | -) |
136 | from maasapiserver.v3.db.base import BaseRepository |
137 | from maasapiserver.v3.models.base import ListResult |
138 | from maasapiserver.v3.models.resource_pools import ResourcePool |
139 | @@ -31,9 +29,11 @@ RESOURCE_POOLS_FIELDS = ( |
140 | ) |
141 | |
142 | |
143 | -class ResourcePoolRepository( |
144 | - BaseRepository[ResourcePool, ResourcePoolRequest] |
145 | -): |
146 | +class ResourcePoolRepository(BaseRepository[ResourcePool]): |
147 | + async def get_next_id(self) -> int: |
148 | + stmt = select(ResourcePoolIdSequence.next_value()) |
149 | + return (await self.connection.execute(stmt)).scalar() |
150 | + |
151 | async def find_by_id(self, id: int) -> Optional[ResourcePool]: |
152 | stmt = self._select_all_statement().where( |
153 | eq(ResourcePoolTable.c.id, id) |
154 | @@ -43,24 +43,23 @@ class ResourcePoolRepository( |
155 | return ResourcePool(**resource_pools._asdict()) |
156 | return None |
157 | |
158 | - async def create(self, request: ResourcePoolRequest) -> ResourcePool: |
159 | - now = datetime.utcnow() |
160 | + async def create(self, resource_pool: ResourcePool) -> ResourcePool: |
161 | stmt = ( |
162 | insert(ResourcePoolTable) |
163 | .returning(*RESOURCE_POOLS_FIELDS) |
164 | .values( |
165 | - name=request.name, |
166 | - description=request.description, |
167 | - updated=now, |
168 | - created=now, |
169 | + name=resource_pool.name, |
170 | + description=resource_pool.description, |
171 | + updated=resource_pool.updated, |
172 | + created=resource_pool.created, |
173 | ) |
174 | ) |
175 | try: |
176 | result = await self.connection.execute(stmt) |
177 | except IntegrityError: |
178 | - self._raise_constraint_violation(request.name) |
179 | - resource_pools = result.one() |
180 | - return ResourcePool(**resource_pools._asdict()) |
181 | + self._raise_constraint_violation(resource_pool.name) |
182 | + created_resource_pools = result.one() |
183 | + return ResourcePool(**created_resource_pools._asdict()) |
184 | |
185 | async def list( |
186 | self, pagination_params: PaginationParams |
187 | diff --git a/src/maasapiserver/v3/db/users.py b/src/maasapiserver/v3/db/users.py |
188 | index 573b790..097c475 100644 |
189 | --- a/src/maasapiserver/v3/db/users.py |
190 | +++ b/src/maasapiserver/v3/db/users.py |
191 | @@ -3,14 +3,16 @@ from sqlalchemy.sql.operators import eq |
192 | |
193 | from maasapiserver.common.db.tables import UserTable |
194 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
195 | -from maasapiserver.v3.api.models.requests.users import UserRequest |
196 | from maasapiserver.v3.db.base import BaseRepository |
197 | from maasapiserver.v3.models.base import ListResult |
198 | from maasapiserver.v3.models.users import User |
199 | |
200 | |
201 | -class UsersRepository(BaseRepository[User, UserRequest]): |
202 | - async def create(self, request: UserRequest) -> User: |
203 | +class UsersRepository(BaseRepository[User]): |
204 | + async def get_next_id(self) -> int: |
205 | + raise Exception("Not implemented yet.") |
206 | + |
207 | + async def create(self, resource: User) -> User: |
208 | raise Exception("Not implemented yet.") |
209 | |
210 | async def find_by_id(self, id: int) -> User | None: |
211 | diff --git a/src/maasapiserver/v3/db/vmcluster.py b/src/maasapiserver/v3/db/vmcluster.py |
212 | index b0a2bbf..9153d95 100644 |
213 | --- a/src/maasapiserver/v3/db/vmcluster.py |
214 | +++ b/src/maasapiserver/v3/db/vmcluster.py |
215 | @@ -3,14 +3,16 @@ from sqlalchemy.sql.operators import eq |
216 | |
217 | from maasapiserver.common.db.tables import VmClusterTable |
218 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
219 | -from maasapiserver.v3.api.models.requests.vmcluster import VmClusterRequest |
220 | from maasapiserver.v3.db.base import BaseRepository |
221 | from maasapiserver.v3.models.base import ListResult |
222 | from maasapiserver.v3.models.vmcluster import VmCluster |
223 | |
224 | |
225 | -class VmClustersRepository(BaseRepository[VmCluster, VmClusterRequest]): |
226 | - async def create(self, request: VmClusterRequest) -> VmCluster: |
227 | +class VmClustersRepository(BaseRepository[VmCluster]): |
228 | + async def get_next_id(self) -> int: |
229 | + raise Exception("Not implemented yet.") |
230 | + |
231 | + async def create(self, resource: VmCluster) -> VmCluster: |
232 | raise Exception("Not implemented yet.") |
233 | |
234 | async def find_by_id(self, id: int) -> VmCluster | None: |
235 | diff --git a/src/maasapiserver/v3/db/zones.py b/src/maasapiserver/v3/db/zones.py |
236 | index 5325896..417e553 100644 |
237 | --- a/src/maasapiserver/v3/db/zones.py |
238 | +++ b/src/maasapiserver/v3/db/zones.py |
239 | @@ -1,10 +1,10 @@ |
240 | -from datetime import datetime |
241 | from typing import Any |
242 | |
243 | from sqlalchemy import delete, desc, insert, select, Select |
244 | from sqlalchemy.sql.functions import count |
245 | from sqlalchemy.sql.operators import eq |
246 | |
247 | +from maasapiserver.common.db.sequences import ZoneIdSequence |
248 | from maasapiserver.common.db.tables import DefaultResourceTable, ZoneTable |
249 | from maasapiserver.common.models.constants import ( |
250 | UNIQUE_CONSTRAINT_VIOLATION_TYPE, |
251 | @@ -14,18 +14,21 @@ from maasapiserver.common.models.exceptions import ( |
252 | BaseExceptionDetail, |
253 | ) |
254 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
255 | -from maasapiserver.v3.api.models.requests.zones import ZoneRequest |
256 | from maasapiserver.v3.db.base import BaseRepository |
257 | from maasapiserver.v3.models.base import ListResult |
258 | from maasapiserver.v3.models.zones import Zone |
259 | |
260 | |
261 | -class ZonesRepository(BaseRepository[Zone, ZoneRequest]): |
262 | - async def create(self, request: ZoneRequest) -> Zone: |
263 | +class ZonesRepository(BaseRepository[Zone]): |
264 | + async def get_next_id(self) -> int: |
265 | + stmt = select(ZoneIdSequence.next_value()) |
266 | + return (await self.connection.execute(stmt)).scalar() |
267 | + |
268 | + async def create(self, zone: Zone) -> Zone: |
269 | check_integrity_stmt = ( |
270 | select(ZoneTable.c.id) |
271 | .select_from(ZoneTable) |
272 | - .where(eq(ZoneTable.c.name, request.name)) |
273 | + .where(eq(ZoneTable.c.name, zone.name)) |
274 | .limit(1) |
275 | ) |
276 | existing_entity = ( |
277 | @@ -36,12 +39,11 @@ class ZonesRepository(BaseRepository[Zone, ZoneRequest]): |
278 | details=[ |
279 | BaseExceptionDetail( |
280 | type=UNIQUE_CONSTRAINT_VIOLATION_TYPE, |
281 | - message=f"An entity with name '{request.name}' already exists. Its id is '{existing_entity.id}'.", |
282 | + message=f"An entity with name '{zone.name}' already exists. Its id is '{existing_entity.id}'.", |
283 | ) |
284 | ] |
285 | ) |
286 | |
287 | - now = datetime.utcnow() |
288 | stmt = ( |
289 | insert(ZoneTable) |
290 | .returning( |
291 | @@ -52,15 +54,16 @@ class ZonesRepository(BaseRepository[Zone, ZoneRequest]): |
292 | ZoneTable.c.updated, |
293 | ) |
294 | .values( |
295 | - name=request.name, |
296 | - description=request.description, |
297 | - updated=now, |
298 | - created=now, |
299 | + id=zone.id, |
300 | + name=zone.name, |
301 | + description=zone.description, |
302 | + updated=zone.updated, |
303 | + created=zone.created, |
304 | ) |
305 | ) |
306 | result = await self.connection.execute(stmt) |
307 | - zone = result.one() |
308 | - return Zone(**zone._asdict()) |
309 | + created_zone = result.one() |
310 | + return Zone(**created_zone._asdict()) |
311 | |
312 | async def find_by_id(self, id: int) -> Zone | None: |
313 | stmt = self._select_all_statement().filter(eq(ZoneTable.c.id, id)) |
314 | diff --git a/src/maasapiserver/v3/services/resource_pools.py b/src/maasapiserver/v3/services/resource_pools.py |
315 | index 6862bdc..4388b31 100644 |
316 | --- a/src/maasapiserver/v3/services/resource_pools.py |
317 | +++ b/src/maasapiserver/v3/services/resource_pools.py |
318 | @@ -1,3 +1,4 @@ |
319 | +from datetime import datetime |
320 | from typing import Optional |
321 | |
322 | from sqlalchemy.ext.asyncio import AsyncConnection |
323 | @@ -34,9 +35,16 @@ class ResourcePoolsService(Service): |
324 | async def create( |
325 | self, resource_pool_request: ResourcePoolRequest |
326 | ) -> ResourcePool: |
327 | - return await self.resource_pools_repository.create( |
328 | - resource_pool_request |
329 | + now = datetime.utcnow() |
330 | + resource_pool_id = await self.resource_pools_repository.get_next_id() |
331 | + resource_pool = ResourcePool( |
332 | + id=resource_pool_id, |
333 | + name=resource_pool_request.name, |
334 | + description=resource_pool_request.description, |
335 | + updated=now, |
336 | + created=now, |
337 | ) |
338 | + return await self.resource_pools_repository.create(resource_pool) |
339 | |
340 | async def get_by_id(self, id: int) -> Optional[ResourcePool]: |
341 | return await self.resource_pools_repository.find_by_id(id) |
342 | diff --git a/src/maasapiserver/v3/services/zones.py b/src/maasapiserver/v3/services/zones.py |
343 | index 8b6e5f3..f40f419 100644 |
344 | --- a/src/maasapiserver/v3/services/zones.py |
345 | +++ b/src/maasapiserver/v3/services/zones.py |
346 | @@ -1,3 +1,4 @@ |
347 | +from datetime import datetime |
348 | from typing import Optional |
349 | |
350 | from sqlalchemy.ext.asyncio import AsyncConnection |
351 | @@ -50,7 +51,16 @@ class ZonesService(Service): |
352 | ) |
353 | |
354 | async def create(self, zone_request: ZoneRequest) -> Zone: |
355 | - return await self.zones_repository.create(zone_request) |
356 | + zone_id = await self.zones_repository.get_next_id() |
357 | + now = datetime.utcnow() |
358 | + zone = Zone( |
359 | + id=zone_id, |
360 | + name=zone_request.name, |
361 | + description=zone_request.description, |
362 | + updated=now, |
363 | + created=now, |
364 | + ) |
365 | + return await self.zones_repository.create(zone) |
366 | |
367 | async def get_by_id(self, id: int) -> Optional[Zone]: |
368 | return await self.zones_repository.find_by_id(id) |
369 | diff --git a/src/tests/maasapiserver/v3/db/test_resource_pools.py b/src/tests/maasapiserver/v3/db/test_resource_pools.py |
370 | index 6ad5feb..da0e994 100644 |
371 | --- a/src/tests/maasapiserver/v3/db/test_resource_pools.py |
372 | +++ b/src/tests/maasapiserver/v3/db/test_resource_pools.py |
373 | @@ -7,9 +7,6 @@ from sqlalchemy.orm.exc import NoResultFound |
374 | |
375 | from maasapiserver.common.models.exceptions import AlreadyExistsException |
376 | from maasapiserver.v3.api.models.requests.query import PaginationParams |
377 | -from maasapiserver.v3.api.models.requests.resource_pools import ( |
378 | - ResourcePoolRequest, |
379 | -) |
380 | from maasapiserver.v3.db.resource_pools import ResourcePoolRepository |
381 | from maasapiserver.v3.models.resource_pools import ResourcePool |
382 | from tests.fixtures.factories.resource_pools import ( |
383 | @@ -22,35 +19,45 @@ from tests.maasapiserver.fixtures.db import Fixture |
384 | @pytest.mark.usefixtures("ensuremaasdb") |
385 | @pytest.mark.asyncio |
386 | class TestResourcePoolRepository: |
387 | + async def test_get_next_id(self, db_connection: AsyncConnection) -> None: |
388 | + resource_pools_repository = ResourcePoolRepository(db_connection) |
389 | + first = await resource_pools_repository.get_next_id() |
390 | + second = await resource_pools_repository.get_next_id() |
391 | + assert first < second |
392 | + |
393 | async def test_create(self, db_connection: AsyncConnection) -> None: |
394 | - now = datetime.utcnow() |
395 | + date = datetime.fromisocalendar(2024, 1, 1).astimezone(timezone.utc) |
396 | resource_pools_repository = ResourcePoolRepository(db_connection) |
397 | created_resource_pools = await resource_pools_repository.create( |
398 | - ResourcePoolRequest( |
399 | - name="my_resource_pool", description="my description" |
400 | + ResourcePool( |
401 | + id=2, |
402 | + name="my_resource_pool", |
403 | + description="my description", |
404 | + created=date, |
405 | + updated=date, |
406 | ) |
407 | ) |
408 | assert created_resource_pools.id |
409 | assert created_resource_pools.name == "my_resource_pool" |
410 | assert created_resource_pools.description == "my description" |
411 | - assert created_resource_pools.created.astimezone( |
412 | - timezone.utc |
413 | - ) >= now.astimezone(timezone.utc) |
414 | - assert created_resource_pools.updated.astimezone( |
415 | - timezone.utc |
416 | - ) >= now.astimezone(timezone.utc) |
417 | + assert created_resource_pools.created.astimezone(timezone.utc) == date |
418 | + assert created_resource_pools.updated.astimezone(timezone.utc) == date |
419 | |
420 | async def test_create_duplicated( |
421 | self, db_connection: AsyncConnection, fixture: Fixture |
422 | ) -> None: |
423 | + date = datetime.fromisocalendar(2024, 1, 1).astimezone(timezone.utc) |
424 | resource_pools_repository = ResourcePoolRepository(db_connection) |
425 | created_resource_pools = await create_test_resource_pool(fixture) |
426 | |
427 | with pytest.raises(AlreadyExistsException): |
428 | await resource_pools_repository.create( |
429 | - ResourcePoolRequest( |
430 | + ResourcePool( |
431 | + id=3, |
432 | name=created_resource_pools.name, |
433 | description=created_resource_pools.description, |
434 | + created=date, |
435 | + updated=date, |
436 | ) |
437 | ) |
438 | |
439 | diff --git a/src/tests/maasapiserver/v3/db/test_zones.py b/src/tests/maasapiserver/v3/db/test_zones.py |
440 | index 1104182..55367c9 100644 |
441 | --- a/src/tests/maasapiserver/v3/db/test_zones.py |
442 | +++ b/src/tests/maasapiserver/v3/db/test_zones.py |
443 | @@ -11,6 +11,7 @@ from maasapiserver.v3.api.models.requests.query import PaginationParams |
444 | from maasapiserver.v3.api.models.requests.zones import ZoneRequest |
445 | from maasapiserver.v3.constants import DEFAULT_ZONE_NAME |
446 | from maasapiserver.v3.db.zones import ZonesRepository |
447 | +from maasapiserver.v3.models.zones import Zone |
448 | from tests.fixtures.factories.zone import create_test_zone |
449 | from tests.maasapiserver.fixtures.db import Fixture |
450 | |
451 | @@ -18,21 +19,29 @@ from tests.maasapiserver.fixtures.db import Fixture |
452 | @pytest.mark.usefixtures("ensuremaasdb") |
453 | @pytest.mark.asyncio |
454 | class TestZonesRepository: |
455 | + async def test_get_next_id(self, db_connection: AsyncConnection) -> None: |
456 | + zones_repository = ZonesRepository(db_connection) |
457 | + first = await zones_repository.get_next_id() |
458 | + second = await zones_repository.get_next_id() |
459 | + assert first < second |
460 | + |
461 | async def test_create(self, db_connection: AsyncConnection) -> None: |
462 | - now = datetime.utcnow() |
463 | + date = datetime.fromisocalendar(2024, 1, 1).astimezone(timezone.utc) |
464 | zones_repository = ZonesRepository(db_connection) |
465 | created_zone = await zones_repository.create( |
466 | - ZoneRequest(name="my_zone", description="my description") |
467 | + Zone( |
468 | + id=2, |
469 | + name="my_zone", |
470 | + description="my description", |
471 | + created=date, |
472 | + updated=date, |
473 | + ) |
474 | ) |
475 | assert created_zone.id > 1 |
476 | assert created_zone.name == "my_zone" |
477 | assert created_zone.description == "my description" |
478 | - assert created_zone.created.astimezone(timezone.utc) >= now.astimezone( |
479 | - timezone.utc |
480 | - ) |
481 | - assert created_zone.updated.astimezone(timezone.utc) >= now.astimezone( |
482 | - timezone.utc |
483 | - ) |
484 | + assert created_zone.created.astimezone(timezone.utc) == date |
485 | + assert created_zone.updated.astimezone(timezone.utc) == date |
486 | |
487 | async def test_create_duplicated( |
488 | self, db_connection: AsyncConnection, fixture: Fixture |
489 | diff --git a/src/tests/maasapiserver/v3/services/test_resource_pools.py b/src/tests/maasapiserver/v3/services/test_resource_pools.py |
490 | index b3012a3..d3c5cbb 100644 |
491 | --- a/src/tests/maasapiserver/v3/services/test_resource_pools.py |
492 | +++ b/src/tests/maasapiserver/v3/services/test_resource_pools.py |
493 | @@ -1,4 +1,5 @@ |
494 | from datetime import datetime |
495 | +from typing import Any |
496 | from unittest.mock import AsyncMock, Mock |
497 | |
498 | import pytest |
499 | @@ -23,6 +24,24 @@ class TestResourcePoolsService: |
500 | async def test_create( |
501 | self, db_connection: AsyncConnection, fixture: Fixture |
502 | ) -> None: |
503 | + class ResourcePoolMatcher(ResourcePool): |
504 | + def __init__(self, **kwargs: Any) -> None: |
505 | + kwargs["created"] = datetime.now() |
506 | + kwargs["updated"] = datetime.now() |
507 | + super().__init__(**kwargs) |
508 | + |
509 | + def __eq__(self, other: Any) -> bool: |
510 | + if not isinstance(other, ResourcePool): |
511 | + return False |
512 | + return ( |
513 | + self.id == other.id |
514 | + and self.name == other.name |
515 | + and self.description == other.description |
516 | + and self.created >= other.created |
517 | + and self.updated # The matcher is created after the actual resource |
518 | + >= other.updated |
519 | + ) |
520 | + |
521 | now = datetime.utcnow() |
522 | resource_pool = ResourcePool( |
523 | id=1, |
524 | @@ -35,6 +54,7 @@ class TestResourcePoolsService: |
525 | resource_pool_repository_mock.create = AsyncMock( |
526 | return_value=resource_pool |
527 | ) |
528 | + resource_pool_repository_mock.get_next_id = AsyncMock(return_value=1) |
529 | resource_pools_service = ResourcePoolsService( |
530 | connection=db_connection, |
531 | resource_pools_repository=resource_pool_repository_mock, |
532 | @@ -43,7 +63,13 @@ class TestResourcePoolsService: |
533 | name=resource_pool.name, description=resource_pool.description |
534 | ) |
535 | created_resource_pool = await resource_pools_service.create(request) |
536 | - resource_pool_repository_mock.create.assert_called_once_with(request) |
537 | + resource_pool_repository_mock.create.assert_called_once_with( |
538 | + ResourcePoolMatcher( |
539 | + id=1, |
540 | + name=resource_pool.name, |
541 | + description=resource_pool.description, |
542 | + ) |
543 | + ) |
544 | assert created_resource_pool is not None |
545 | |
546 | async def test_list( |
UNIT TESTS baserepository- interface lp:~r00ta/maas/+git/maas into -b master lp:~maas-committers/maas
-b refactoring-
STATUS: SUCCESS 1eb9e0c0cef2c57 e70136f670
COMMIT: 1de447f43e4367f