@@ -760,4 +760,166 @@ public async Task WhenDeleteClientPermissionWithPermissionNotAssigned_ShouldRetu
760760 Assert . Equal ( HttpStatusCode . Conflict , response . StatusCode ) ;
761761 Assert . Equal ( ClientErrors . PermissionNotAssigned , error ) ;
762762 }
763+
764+ [ Fact ( DisplayName = "[e2e] - when POST /clients/{id}/audiences with valid audience should assign audience successfully" ) ]
765+ public async Task WhenPostClientAudiencesWithValidAudience_ShouldAssignAudienceSuccessfully ( )
766+ {
767+ /* arrange: resolve required dependencies */
768+ var clientCollection = factory . Services . GetRequiredService < IClientCollection > ( ) ;
769+
770+ /* arrange: authenticate user and get access token */
771+ var httpClient = factory . HttpClient . WithRealmHeader ( "master" ) ;
772+ var credentials = new AuthenticationCredentials
773+ {
774+ Username = "federation.testing.user" ,
775+ Password = "federation.testing.password"
776+ } ;
777+
778+ var authenticationResponse = await httpClient . PostAsJsonAsync ( "api/v1/identity/authenticate" , credentials ) ;
779+ var authenticationResult = await authenticationResponse . Content . ReadFromJsonAsync < AuthenticationResult > ( ) ;
780+
781+ Assert . NotNull ( authenticationResult ) ;
782+ Assert . NotEmpty ( authenticationResult . AccessToken ) ;
783+
784+ httpClient . WithAuthorization ( authenticationResult . AccessToken ) ;
785+
786+ /* arrange: create a new client */
787+ var clientPayload = _fixture . Build < ClientCreationScheme > ( )
788+ . With ( client => client . Name , $ "test-client-{ Guid . NewGuid ( ) } ")
789+ . With ( client => client . Flows , [ Grant . ClientCredentials ] )
790+ . With ( client => client . RedirectUris , [ ] )
791+ . Create ( ) ;
792+
793+ var clientResponse = await httpClient . PostAsJsonAsync ( "api/v1/clients" , clientPayload ) ;
794+
795+ Assert . Equal ( HttpStatusCode . Created , clientResponse . StatusCode ) ;
796+
797+ var clientFilters = ClientFilters . WithSpecifications ( )
798+ . WithName ( clientPayload . Name )
799+ . Build ( ) ;
800+
801+ var clients = await clientCollection . GetClientsAsync ( clientFilters , CancellationToken . None ) ;
802+ var client = clients . FirstOrDefault ( ) ;
803+
804+ Assert . NotEmpty ( clients ) ;
805+ Assert . NotNull ( client ) ;
806+
807+ /* arrange: prepare request to assign audience to client */
808+ var payload = new AssignClientAudienceScheme
809+ {
810+ Value = "https://api.example.com"
811+ } ;
812+
813+ /* act: send POST request to assign audience to client */
814+ var response = await httpClient . PostAsJsonAsync ( $ "api/v1/clients/{ client . Id } /audiences", payload ) ;
815+ var audiences = await response . Content . ReadFromJsonAsync < IReadOnlyCollection < string > > ( ) ;
816+
817+ /* assert: response should be 200 OK and audiences list should be returned */
818+ Assert . Equal ( HttpStatusCode . OK , response . StatusCode ) ;
819+ Assert . NotNull ( audiences ) ;
820+
821+ /* assert: the assigned audience should be in the returned list */
822+ Assert . Contains ( audiences , current => current == payload . Value ) ;
823+ }
824+
825+ [ Fact ( DisplayName = "[e2e] - when POST /clients/{id}/audiences with duplicate audience should return 409 #ERROR-F4E2A" ) ]
826+ public async Task WhenPostClientAudiencesWithDuplicateAudience_ShouldReturnConflict ( )
827+ {
828+ /* arrange: resolve required dependencies */
829+ var clientCollection = factory . Services . GetRequiredService < IClientCollection > ( ) ;
830+
831+ /* arrange: authenticate user and get access token */
832+ var httpClient = factory . HttpClient . WithRealmHeader ( "master" ) ;
833+ var credentials = new AuthenticationCredentials
834+ {
835+ Username = "federation.testing.user" ,
836+ Password = "federation.testing.password"
837+ } ;
838+
839+ var authenticationResponse = await httpClient . PostAsJsonAsync ( "api/v1/identity/authenticate" , credentials ) ;
840+ var authenticationResult = await authenticationResponse . Content . ReadFromJsonAsync < AuthenticationResult > ( ) ;
841+
842+ Assert . NotNull ( authenticationResult ) ;
843+ Assert . NotEmpty ( authenticationResult . AccessToken ) ;
844+
845+ httpClient . WithAuthorization ( authenticationResult . AccessToken ) ;
846+
847+ /* arrange: create a new client */
848+ var payload = _fixture . Build < ClientCreationScheme > ( )
849+ . With ( client => client . Name , $ "test-client-{ Guid . NewGuid ( ) } ")
850+ . With ( client => client . Flows , [ Grant . ClientCredentials ] )
851+ . With ( client => client . RedirectUris , [ ] )
852+ . Create ( ) ;
853+
854+ var clientResponse = await httpClient . PostAsJsonAsync ( "api/v1/clients" , payload ) ;
855+
856+ Assert . Equal ( HttpStatusCode . Created , clientResponse . StatusCode ) ;
857+
858+ var clientFilters = ClientFilters . WithSpecifications ( )
859+ . WithName ( payload . Name )
860+ . Build ( ) ;
861+
862+ var clients = await clientCollection . GetClientsAsync ( clientFilters , CancellationToken . None ) ;
863+ var client = clients . FirstOrDefault ( ) ;
864+
865+ Assert . NotEmpty ( clients ) ;
866+ Assert . NotNull ( client ) ;
867+
868+ /* arrange: assign audience to client first time */
869+ var content = new AssignClientAudienceScheme
870+ {
871+ Value = "https://api.example.com"
872+ } ;
873+
874+ var firstResponse = await httpClient . PostAsJsonAsync ( $ "api/v1/clients/{ client . Id } /audiences", content ) ;
875+
876+ Assert . Equal ( HttpStatusCode . OK , firstResponse . StatusCode ) ;
877+
878+ /* act: attempt to assign the same audience again */
879+ var secondResponse = await httpClient . PostAsJsonAsync ( $ "api/v1/clients/{ client . Id } /audiences", content ) ;
880+ var error = await secondResponse . Content . ReadFromJsonAsync < Error > ( ) ;
881+
882+ /* assert: response should be 409 Conflict */
883+ Assert . NotNull ( error ) ;
884+
885+ Assert . Equal ( HttpStatusCode . Conflict , secondResponse . StatusCode ) ;
886+ Assert . Equal ( ClientErrors . ClientAlreadyHasAudience , error ) ;
887+ }
888+
889+ [ Fact ( DisplayName = "[e2e] - when POST /clients/{id}/audiences with non-existent client should return 404 #ERROR-2D943" ) ]
890+ public async Task WhenPostClientAudiencesWithNonExistentClient_ShouldReturnNotFound ( )
891+ {
892+ /* arrange: authenticate user and get access token */
893+ var httpClient = factory . HttpClient . WithRealmHeader ( "master" ) ;
894+ var credentials = new AuthenticationCredentials
895+ {
896+ Username = "federation.testing.user" ,
897+ Password = "federation.testing.password"
898+ } ;
899+
900+ var authenticationResponse = await httpClient . PostAsJsonAsync ( "api/v1/identity/authenticate" , credentials ) ;
901+ var authenticationResult = await authenticationResponse . Content . ReadFromJsonAsync < AuthenticationResult > ( ) ;
902+
903+ Assert . NotNull ( authenticationResult ) ;
904+ Assert . NotEmpty ( authenticationResult . AccessToken ) ;
905+
906+ httpClient . WithAuthorization ( authenticationResult . AccessToken ) ;
907+
908+ /* arrange: prepare request with a non-existent client ID */
909+ var nonExistentClientId = Guid . NewGuid ( ) . ToString ( ) ;
910+ var payload = new AssignClientAudienceScheme
911+ {
912+ Value = "https://api.example.com"
913+ } ;
914+
915+ /* act: send POST request for non-existent client */
916+ var response = await httpClient . PostAsJsonAsync ( $ "api/v1/clients/{ nonExistentClientId } /audiences", payload ) ;
917+ var error = await response . Content . ReadFromJsonAsync < Error > ( ) ;
918+
919+ /* assert: response should be 404 Not Found */
920+ Assert . NotNull ( error ) ;
921+
922+ Assert . Equal ( HttpStatusCode . NotFound , response . StatusCode ) ;
923+ Assert . Equal ( ClientErrors . ClientDoesNotExist , error ) ;
924+ }
763925}
0 commit comments