@@ -490,7 +490,7 @@ func TestMarkAsDeletingThenUpdate(t *testing.T) {
490490 ID : resource .ID ,
491491 },
492492 Version : resource .Version ,
493- Status : createStatusWithSequenceID (t , h , resource .ID , fmt .Sprintf ("%d" , 1 )),
493+ Status : createStatusWithSequenceID (t , resource .ID , fmt .Sprintf ("%d" , 1 )),
494494 }
495495 _ , updated , svcErr := resourceService .UpdateStatus (ctx , statusRes )
496496 Expect (svcErr ).NotTo (HaveOccurred ())
@@ -529,8 +529,118 @@ func updateWorkStatus(ctx context.Context, workClient workv1client.ManifestWorkI
529529 return nil
530530}
531531
532+ // TestUpdateAndUpdateStatusIsolation ensures that Update and UpdateStatus operations
533+ // work independently without affecting each other. Specifically:
534+ // 1. Create a resource
535+ // 2. Update the status
536+ // 3. Update the resource payload
537+ // 4. Verify the status from step 2 is preserved and not affected by the payload update
538+ func TestUpdateAndUpdateStatusIsolation (t * testing.T ) {
539+ h , _ := test .RegisterIntegration (t )
540+
541+ account := h .NewRandAccount ()
542+ ctx := h .NewAuthenticatedContext (account )
543+
544+ // Step 1: Create a resource
545+ consumer , err := h .CreateConsumer ("cluster-" + rand .String (5 ))
546+ Expect (err ).NotTo (HaveOccurred ())
547+ deployName := fmt .Sprintf ("nginx-%s" , rand .String (5 ))
548+ resource , err := h .CreateResource (uuid .NewString (), consumer .Name , deployName , "default" , 1 )
549+ Expect (err ).NotTo (HaveOccurred ())
550+ Expect (resource .Version ).To (Equal (int32 (1 )))
551+ Expect (len (resource .Status )).To (Equal (0 ), "Initial resource should have no status" )
552+
553+ resourceService := h .Env ().Services .Resources ()
554+
555+ // Step 2: Update the status with sequence ID "1"
556+ statusRes1 := & api.Resource {
557+ Meta : api.Meta {
558+ ID : resource .ID ,
559+ },
560+ Version : resource .Version ,
561+ Status : createStatusWithSequenceID (t , resource .ID , "1" ),
562+ }
563+ _ , updated , svcErr := resourceService .UpdateStatus (ctx , statusRes1 )
564+ Expect (svcErr ).NotTo (HaveOccurred ())
565+ Expect (updated ).Should (BeTrue (), "Status should be updated" )
566+
567+ // Verify status was set correctly
568+ updatedRes , svcErr := resourceService .Get (ctx , resource .ID )
569+ Expect (svcErr ).NotTo (HaveOccurred ())
570+ Expect (len (updatedRes .Status )).ShouldNot (Equal (0 ), "Status should not be empty after update" )
571+ statusEvt , err := api .JSONMAPToCloudEvent (updatedRes .Status )
572+ Expect (err ).NotTo (HaveOccurred ())
573+ statusPayload := & workpayload.ManifestBundleStatus {}
574+ Expect (statusEvt .DataAs (statusPayload )).NotTo (HaveOccurred ())
575+ Expect (statusPayload .Conditions ).To (HaveLen (1 ))
576+ Expect (statusPayload .Conditions [0 ].Type ).To (Equal ("Applied" ))
577+ Expect (statusPayload .Conditions [0 ].Status ).To (Equal (metav1 .ConditionStatus ("True" )))
578+ initialStatusMessage := statusPayload .Conditions [0 ].Message
579+
580+ // Version should still be 1 (UpdateStatus doesn't increment version)
581+ Expect (updatedRes .Version ).To (Equal (int32 (1 )))
582+
583+ // Step 3: Update the resource payload (change replicas from 1 to 3)
584+ newResource , err := h .NewResource (resource .ID , consumer .Name , deployName , "default" , 3 , updatedRes .Version )
585+ Expect (err ).NotTo (HaveOccurred ())
586+ newResource .ID = updatedRes .ID
587+ newResource .Status = createStatusWithSequenceID (t , resource .ID , "2" )
588+
589+ updatedPayloadRes , svcErr := resourceService .Update (ctx , newResource )
590+ Expect (svcErr ).NotTo (HaveOccurred ())
591+ Expect (updatedPayloadRes .Version ).To (Equal (int32 (2 )), "Version should be incremented after Update" )
592+
593+ // Step 4: Verify that the status is preserved and unchanged
594+ finalRes , svcErr := resourceService .Get (ctx , resource .ID )
595+ Expect (svcErr ).NotTo (HaveOccurred ())
596+
597+ // Status should still be present and unchanged
598+ Expect (len (finalRes .Status )).ShouldNot (Equal (0 ), "Status should be preserved after Update" )
599+
600+ finalStatusEvt , err := api .JSONMAPToCloudEvent (finalRes .Status )
601+ Expect (err ).NotTo (HaveOccurred ())
602+ finalStatusPayload := & workpayload.ManifestBundleStatus {}
603+ Expect (finalStatusEvt .DataAs (finalStatusPayload )).NotTo (HaveOccurred ())
604+ Expect (finalStatusPayload .Conditions ).To (HaveLen (1 ))
605+ Expect (finalStatusPayload .Conditions [0 ].Type ).To (Equal ("Applied" ))
606+ Expect (finalStatusPayload .Conditions [0 ].Status ).To (Equal (metav1 .ConditionStatus ("True" )))
607+ Expect (finalStatusPayload .Conditions [0 ].Message ).To (Equal (initialStatusMessage ), "Status message should be unchanged" )
608+
609+ // Verify the payload was actually updated (replicas changed from 1 to 3)
610+ payloadEvt , err := api .JSONMAPToCloudEvent (finalRes .Payload )
611+ Expect (err ).NotTo (HaveOccurred ())
612+ payloadData := & workpayload.ManifestBundle {}
613+ Expect (payloadEvt .DataAs (payloadData )).NotTo (HaveOccurred ())
614+ Expect (payloadData .Manifests ).To (HaveLen (1 ))
615+
616+ var manifest map [string ]interface {}
617+ Expect (json .Unmarshal (payloadData .Manifests [0 ].Raw , & manifest )).NotTo (HaveOccurred ())
618+ spec := manifest ["spec" ].(map [string ]interface {})
619+ Expect (spec ["replicas" ]).To (Equal (float64 (3 )), "Replicas should be updated to 3" )
620+
621+ // Additional test: Update status again to verify it still works after a payload update
622+ statusRes2 := & api.Resource {
623+ Meta : api.Meta {
624+ ID : resource .ID ,
625+ },
626+ Version : finalRes .Version , // Now version 2
627+ Status : createStatusWithSequenceID (t , resource .ID , "2" ),
628+ }
629+ updatedRes2 , updated2 , svcErr := resourceService .UpdateStatus (ctx , statusRes2 )
630+ Expect (svcErr ).NotTo (HaveOccurred ())
631+ Expect (updated2 ).Should (BeTrue (), "Status should be updated again" )
632+ Expect (updatedRes2 .Version ).To (Equal (int32 (2 )), "Version should remain 2 after UpdateStatus" )
633+
634+ // Verify the new status
635+ newStatusEvt , err := api .JSONMAPToCloudEvent (updatedRes2 .Status )
636+ Expect (err ).NotTo (HaveOccurred ())
637+ newStatusPayload := & workpayload.ManifestBundleStatus {}
638+ Expect (newStatusEvt .DataAs (newStatusPayload )).NotTo (HaveOccurred ())
639+ Expect (newStatusPayload .Conditions [0 ].Message ).To (ContainSubstring ("sequence 2" ), "Status should reflect new update" )
640+ }
641+
532642// createStatusWithSequenceID creates a resource status CloudEvent with the given sequence ID
533- func createStatusWithSequenceID (t * testing.T , h * test. Helper , resourceID , sequenceID string ) map [string ]interface {} {
643+ func createStatusWithSequenceID (t * testing.T , resourceID , sequenceID string ) map [string ]interface {} {
534644 source := "test-agent"
535645 eventType := cetypes.CloudEventsType {
536646 CloudEventsDataType : workpayload .ManifestBundleEventDataType ,
0 commit comments