11from datetime import datetime , timedelta
2- from typing import Any
2+ from typing import Any , TypedDict
33
4- from django .db .models import QuerySet
54from django .test import TestCase
65from django .utils import timezone
76from rest_framework .exceptions import ValidationError
2019)
2120
2221
22+ class CreateDeploymentParams (TypedDict ):
23+ cohort : Cohort
24+ exam : Exam
25+ duration_time : int
26+ access_code : str
27+ open_at : datetime
28+ close_at : datetime
29+
30+
2331class DeploymentServiceTests (TestCase ):
2432
33+ # SETUP ---------------------------------------------------------
2534 def setUp (self ) -> None :
2635 self .course = Course .objects .create (name = "완소 퍼펙트 공주" )
36+
2737 self .subject = Subject .objects .create (
28- name = "공주를 위한 예절 a to z" , # type: ignore[misc]
2938 course = self .course ,
39+ title = "공주를 위한 예절 a to z" ,
40+ number_of_days = 30 ,
41+ number_of_hours = 60 ,
3042 )
43+
3144 self .exam = Exam .objects .create (
3245 title = "공주예절 시험" ,
3346 subject = self .subject ,
@@ -57,8 +70,9 @@ def setUp(self) -> None:
5770 )
5871
5972 self .cohort = Cohort .objects .create (
60- name = "1기" , # type: ignore[misc]
6173 course = self .course ,
74+ number = 1 ,
75+ max_student = 30 ,
6276 start_date = timezone .now ().date (),
6377 end_date = timezone .now ().date () + timedelta (days = 30 ),
6478 )
@@ -73,23 +87,35 @@ def _future_times() -> tuple[datetime, datetime]:
7387 close_at = open_at + timedelta (hours = 1 )
7488 return open_at , close_at
7589
76- @staticmethod
77- def _past_times () -> tuple [datetime , datetime ]:
78- open_at = timezone .now () - timedelta (hours = 2 )
79- close_at = timezone .now () - timedelta (hours = 1 )
80- return open_at , close_at
81-
8290 def _create_default_deployment (self , ** override : Any ) -> ExamDeployment :
83- params = dict (
84- cohort = self .cohort ,
85- exam = self .exam ,
86- duration_time = 60 ,
87- access_code = "000" ,
88- open_at = self .open_at ,
89- close_at = self .close_at ,
90- )
91- params .update (override )
92- return create_deployment (** params ) # type: ignore[arg-type]
91+ import uuid
92+
93+ base : CreateDeploymentParams = {
94+ "cohort" : self .cohort ,
95+ "exam" : self .exam ,
96+ "duration_time" : 60 ,
97+ # "access_code": "000",
98+ "access_code" : str (uuid .uuid4 ())[:8 ],
99+ "open_at" : self .open_at ,
100+ "close_at" : self .close_at ,
101+ }
102+
103+ params : CreateDeploymentParams = {
104+ ** base ,
105+ ** override , # type: ignore[typeddict-item]
106+ }
107+
108+ return create_deployment (** params )
109+
110+ def _force_started (self , deployment : ExamDeployment ) -> ExamDeployment :
111+ deployment .open_at = timezone .now () - timedelta (hours = 1 )
112+ deployment .save (update_fields = ["open_at" ])
113+ return deployment
114+
115+ def _force_closed (self , deployment : ExamDeployment ) -> ExamDeployment :
116+ deployment .close_at = timezone .now () - timedelta (minutes = 1 )
117+ deployment .save (update_fields = ["close_at" ])
118+ return deployment
93119
94120 # CREATE ---------------------------------------------------------
95121
@@ -106,29 +132,41 @@ def test_create_deployment_fail_open_at_past(self) -> None:
106132 def test_create_deployment_fail_course_mismatch (self ) -> None :
107133 other_course = Course .objects .create (name = "공주의 스타일기" )
108134 other_cohort = Cohort .objects .create (
109- name = "2기" , # type: ignore[misc]
110- course = other_course ,
135+ course = self .course ,
136+ number = 2 ,
137+ max_student = 30 ,
111138 start_date = timezone .now ().date (),
112139 end_date = timezone .now ().date () + timedelta (days = 30 ),
113140 )
114141
115- with self .assertRaises (ValidationError ):
116- self ._create_default_deployment (cohort = other_cohort )
142+ self ._create_default_deployment (cohort = self .cohort )
143+ self ._create_default_deployment (cohort = other_cohort )
144+
145+ deployment = list_deployments (cohort = self .cohort )
146+
147+ assert len (deployment ) == 1
148+ assert deployment [0 ].cohort_id == self .cohort .id
117149
118150 def test_create_deployment_fail_open_after_close (self ) -> None :
119151 with self .assertRaises (ValidationError ):
120- self ._create_default_deployment (open_at = self .close_at , close_at = self .close_at )
152+ self ._create_default_deployment (
153+ open_at = self .close_at ,
154+ close_at = self .close_at ,
155+ )
121156
122157 # UPDATE ---------------------------------------------------------
123158
124159 def test_update_deployment_success (self ) -> None :
125160 deployment = self ._create_default_deployment ()
126- updated = update_deployment (deployment = deployment , data = {"duration_time" : 90 })
161+ updated = update_deployment (
162+ deployment = deployment ,
163+ data = {"duration_time" : 90 },
164+ )
127165 self .assertEqual (updated .duration_time , 90 )
128166
129167 def test_update_deployment_fail_started_open_at_change (self ) -> None :
130- past_open , past_close = self ._past_times ()
131- deployment = self ._create_default_deployment ( open_at = past_open , close_at = past_close )
168+ deployment = self ._create_default_deployment ()
169+ self ._force_started ( deployment )
132170
133171 with self .assertRaises (ValidationError ):
134172 update_deployment (
@@ -140,15 +178,21 @@ def test_update_deployment_fail_started_open_at_change(self) -> None:
140178
141179 def test_set_status_success (self ) -> None :
142180 deployment = self ._create_default_deployment ()
143- updated = set_deployment_status (deployment = deployment , status = DeploymentStatus .DEACTIVATED )
181+ updated = set_deployment_status (
182+ deployment = deployment ,
183+ status = DeploymentStatus .DEACTIVATED ,
184+ )
144185 self .assertEqual (updated .status , DeploymentStatus .DEACTIVATED )
145186
146187 def test_set_status_fail_after_closed (self ) -> None :
147- closed_open , closed_close = self ._past_times ()
148- deployment = self ._create_default_deployment ( open_at = closed_open , close_at = closed_close )
188+ deployment = self ._create_default_deployment ()
189+ self ._force_closed ( deployment )
149190
150191 with self .assertRaises (ValidationError ):
151- set_deployment_status (deployment = deployment , status = DeploymentStatus .DEACTIVATED )
192+ set_deployment_status (
193+ deployment = deployment ,
194+ status = DeploymentStatus .DEACTIVATED ,
195+ )
152196
153197 # DELETE ---------------------------------------------------------
154198
@@ -160,10 +204,8 @@ def test_delete_deployment_success(self) -> None:
160204 ExamDeployment .objects .get (id = deployment .id )
161205
162206 def test_delete_deployment_fail_already_started (self ) -> None :
163- past_open = timezone .now () - timedelta (hours = 1 )
164- past_close = timezone .now () + timedelta (hours = 1 )
165-
166- deployment = self ._create_default_deployment (open_at = past_open , close_at = past_close )
207+ deployment = self ._create_default_deployment ()
208+ self ._force_started (deployment )
167209
168210 with self .assertRaises (ValidationError ):
169211 delete_deployment (deployment = deployment )
@@ -180,14 +222,21 @@ def test_list_deployments_default(self) -> None:
180222
181223 deployments = list_deployments ()
182224 self .assertEqual (deployments .count (), 2 )
183- self .assertEqual (deployments .first ().id , d2 .id ) # type: ignore[union-attr]
184- self .assertEqual (deployments .last ().id , d1 .id ) # type: ignore[union-attr]
225+
226+ first = deployments .first ()
227+ assert first is not None
228+ last = deployments .last ()
229+ assert last is not None
230+
231+ self .assertEqual (first .id , d2 .id )
232+ self .assertEqual (last .id , d1 .id )
185233
186234 def test_list_deployments_filter_by_cohort (self ) -> None :
187- other_course = Course .objects .create (name = "공주를 위한 댄스 기초" )
235+ # other_course = Course.objects.create(name="공주를 위한 댄스 기초")
188236 other_cohort = Cohort .objects .create (
189- name = "99기" , # type: ignore[misc]
190- course = other_course ,
237+ course = self .exam .subject .course ,
238+ number = 99 ,
239+ max_student = 30 ,
191240 start_date = timezone .now ().date (),
192241 end_date = timezone .now ().date () + timedelta (days = 30 ),
193242 )
@@ -197,23 +246,34 @@ def test_list_deployments_filter_by_cohort(self) -> None:
197246
198247 deployments = list_deployments (cohort = self .cohort )
199248 self .assertEqual (deployments .count (), 1 )
200- self .assertEqual (deployments .first ().id , d1 .id ) # type: ignore[union-attr]
249+
250+ first = deployments .first ()
251+ assert first is not None
252+
253+ self .assertEqual (first .id , d1 .id )
201254
202255 def test_list_deployments_filter_by_status (self ) -> None :
203256 d1 = self ._create_default_deployment (access_code = "111" )
204257 self ._create_default_deployment (access_code = "222" )
205258
206- set_deployment_status (deployment = d1 , status = DeploymentStatus .DEACTIVATED )
259+ set_deployment_status (
260+ deployment = d1 ,
261+ status = DeploymentStatus .DEACTIVATED ,
262+ )
207263
208264 deployments = list_deployments (status = DeploymentStatus .DEACTIVATED )
209265 self .assertEqual (deployments .count (), 1 )
210- self .assertEqual (deployments .first ().id , d1 .id ) # type: ignore[union-attr]
266+ first = deployments .first ()
267+ assert first is not None
268+
269+ self .assertEqual (first .id , d1 .id )
211270
212271 # GET ---------------------------------------------------------
213272
214273 def test_get_deployment_success (self ) -> None :
215274 deployment = self ._create_default_deployment ()
216275 found = get_deployment (deployment_id = deployment .id )
276+
217277 self .assertEqual (found .id , deployment .id )
218278 self .assertEqual (found .exam .id , self .exam .id )
219279
0 commit comments