@@ -10,7 +10,8 @@ defmodule EphemeralEnvironments.Grpc.EphemeralEnvironmentsServerTest do
1010 EphemeralEnvironmentType ,
1111 EphemeralEnvironments ,
1212 ListRequest ,
13- DescribeRequest
13+ DescribeRequest ,
14+ UpdateRequest
1415 }
1516
1617 @ org_id Ecto.UUID . generate ( )
@@ -199,6 +200,206 @@ defmodule EphemeralEnvironments.Grpc.EphemeralEnvironmentsServerTest do
199200 end
200201
201202 describe "update/2" do
203+ test "updates environment type successfully" , % { channel: channel } do
204+ { :ok , env_type } =
205+ Factories.EphemeralEnvironmentsType . insert (
206+ org_id: @ org_id ,
207+ name: "Original Name" ,
208+ description: "Original description" ,
209+ created_by: @ user_id ,
210+ state: :draft ,
211+ max_number_of_instances: 5
212+ )
213+
214+ updater_id = Ecto.UUID . generate ( )
215+
216+ request = % UpdateRequest {
217+ environment_type: % EphemeralEnvironmentType {
218+ id: env_type . id ,
219+ org_id: @ org_id ,
220+ name: "Updated Name" ,
221+ description: "Updated description" ,
222+ last_updated_by: updater_id ,
223+ state: :TYPE_STATE_READY ,
224+ max_number_of_instances: 10
225+ }
226+ }
227+
228+ { :ok , response } = EphemeralEnvironments.Stub . update ( channel , request )
229+
230+ assert response . environment_type . id == env_type . id
231+ assert response . environment_type . org_id == @ org_id
232+ assert response . environment_type . name == "Updated Name"
233+ assert response . environment_type . description == "Updated description"
234+ assert response . environment_type . last_updated_by == updater_id
235+ assert response . environment_type . state == :TYPE_STATE_READY
236+ assert response . environment_type . max_number_of_instances == 10
237+ # created_by should remain unchanged
238+ assert response . environment_type . created_by == @ user_id
239+
240+ # Verify database record was updated
241+ db_record = Repo . get ( Schema , env_type . id )
242+ assert db_record . name == "Updated Name"
243+ assert db_record . description == "Updated description"
244+ assert db_record . last_updated_by == updater_id
245+ assert db_record . state == :ready
246+ end
247+
248+ test "updates only provided fields" , % { channel: channel } do
249+ { :ok , env_type } =
250+ Factories.EphemeralEnvironmentsType . insert (
251+ org_id: @ org_id ,
252+ name: "Original Name" ,
253+ description: "Original description" ,
254+ created_by: @ user_id ,
255+ state: :draft ,
256+ max_number_of_instances: 5
257+ )
258+
259+ updater_id = Ecto.UUID . generate ( )
260+
261+ # Only update name and last_updated_by
262+ request = % UpdateRequest {
263+ environment_type: % EphemeralEnvironmentType {
264+ id: env_type . id ,
265+ org_id: @ org_id ,
266+ name: "New Name" ,
267+ last_updated_by: updater_id
268+ }
269+ }
270+
271+ { :ok , response } = EphemeralEnvironments.Stub . update ( channel , request )
272+
273+ assert response . environment_type . name == "New Name"
274+ assert response . environment_type . last_updated_by == updater_id
275+ # Other fields should remain unchanged
276+ assert response . environment_type . description == "Original description"
277+ assert response . environment_type . state == :TYPE_STATE_DRAFT
278+ assert response . environment_type . max_number_of_instances == 5
279+ end
280+
281+ test "returns not_found when environment type doesn't exist" , % { channel: channel } do
282+ non_existent_id = Ecto.UUID . generate ( )
283+
284+ request = % UpdateRequest {
285+ environment_type: % EphemeralEnvironmentType {
286+ id: non_existent_id ,
287+ org_id: @ org_id ,
288+ name: "Updated Name" ,
289+ last_updated_by: @ user_id
290+ }
291+ }
292+
293+ assert { :error , % GRPC.RPCError { status: 5 , message: "Environment type not found" } } =
294+ EphemeralEnvironments.Stub . update ( channel , request )
295+ end
296+
297+ test "returns not_found when updating with wrong org_id" , % { channel: channel } do
298+ { :ok , env_type } =
299+ Factories.EphemeralEnvironmentsType . insert (
300+ org_id: @ org_id ,
301+ name: "Test Environment"
302+ )
303+
304+ different_org_id = Ecto.UUID . generate ( )
305+
306+ request = % UpdateRequest {
307+ environment_type: % EphemeralEnvironmentType {
308+ id: env_type . id ,
309+ org_id: different_org_id ,
310+ name: "Updated Name" ,
311+ last_updated_by: @ user_id
312+ }
313+ }
314+
315+ assert { :error , % GRPC.RPCError { status: 5 , message: "Environment type not found" } } =
316+ EphemeralEnvironments.Stub . update ( channel , request )
317+ end
318+
319+ test "fails validation when updating with duplicate name in same org" , % { channel: channel } do
320+ { :ok , env1 } =
321+ Factories.EphemeralEnvironmentsType . insert (
322+ org_id: @ org_id ,
323+ name: "Environment 1"
324+ )
325+
326+ { :ok , env2 } =
327+ Factories.EphemeralEnvironmentsType . insert (
328+ org_id: @ org_id ,
329+ name: "Environment 2"
330+ )
331+
332+ # Try to rename env2 to env1's name
333+ request = % UpdateRequest {
334+ environment_type: % EphemeralEnvironmentType {
335+ id: env2 . id ,
336+ org_id: @ org_id ,
337+ name: "Environment 1" ,
338+ last_updated_by: @ user_id
339+ }
340+ }
341+
342+ { :error , error } = EphemeralEnvironments.Stub . update ( channel , request )
343+ assert % GRPC.RPCError { } = error
344+ # UNKNOWN
345+ assert error . status == 2
346+ assert error . message == "duplicate_name: ephemeral environment name has already been taken"
347+ end
348+
349+ test "allows updating to same name in different org" , % { channel: channel } do
350+ { :ok , env1 } =
351+ Factories.EphemeralEnvironmentsType . insert (
352+ org_id: @ org_id ,
353+ name: "Shared Name"
354+ )
355+
356+ other_org_id = Ecto.UUID . generate ( )
357+
358+ { :ok , env2 } =
359+ Factories.EphemeralEnvironmentsType . insert (
360+ org_id: other_org_id ,
361+ name: "Original Name"
362+ )
363+
364+ # Update env2 to use the same name as env1 (but different org)
365+ request = % UpdateRequest {
366+ environment_type: % EphemeralEnvironmentType {
367+ id: env2 . id ,
368+ org_id: other_org_id ,
369+ name: "Shared Name" ,
370+ last_updated_by: @ user_id
371+ }
372+ }
373+
374+ assert { :ok , response } = EphemeralEnvironments.Stub . update ( channel , request )
375+ assert response . environment_type . name == "Shared Name"
376+ end
377+
378+ test "updates timestamp when updating" , % { channel: channel } do
379+ { :ok , env_type } = Factories.EphemeralEnvironmentsType . insert ( org_id: @ org_id )
380+
381+ # Wait a bit to ensure timestamp changes
382+ :timer . sleep ( 100 )
383+
384+ request = % UpdateRequest {
385+ environment_type: % EphemeralEnvironmentType {
386+ id: env_type . id ,
387+ org_id: @ org_id ,
388+ name: "Updated Name" ,
389+ last_updated_by: @ user_id
390+ }
391+ }
392+
393+ { :ok , response } = EphemeralEnvironments.Stub . update ( channel , request )
394+
395+ # created_at should be the original timestamp
396+ original_created_at = DateTime . from_naive! ( env_type . inserted_at , "Etc/UTC" )
397+ response_created_at = DateTime . from_unix! ( response . environment_type . created_at . seconds )
398+ assert DateTime . diff ( response_created_at , original_created_at , :second ) == 0
399+
400+ # updated_at should be recent
401+ assert_recent_timestamp ( DateTime . from_unix! ( response . environment_type . updated_at . seconds ) )
402+ end
202403 end
203404
204405 describe "delete/2" do
0 commit comments