@@ -846,6 +846,148 @@ public async Task UniverseDomain_DifferentCustomInRequestAndCredential()
846846 Assert . Null ( request . Headers . Authorization ? . Parameter ) ;
847847 }
848848
849+ [ Theory ]
850+ [ MemberData ( nameof ( CredentialFactory_Success_Data ) ) ]
851+ public void FromFile_WithType ( string json , string credentialType , Type expectedType )
852+ {
853+ var tempFile = Path . GetTempFileName ( ) ;
854+ try
855+ {
856+ File . WriteAllText ( tempFile , json ) ;
857+ var credential = CredentialFactory . FromFile ( tempFile , credentialType ) ;
858+ Assert . IsType ( expectedType , credential . UnderlyingCredential ) ;
859+ }
860+ finally
861+ {
862+ File . Delete ( tempFile ) ;
863+ }
864+ }
865+
866+ [ Theory ]
867+ [ MemberData ( nameof ( CredentialFactory_Failure_Data ) ) ]
868+ public void FromFile_WithType_Failure ( string json , string credentialType , string expectedMessage )
869+ {
870+ var tempFile = Path . GetTempFileName ( ) ;
871+ try
872+ {
873+ File . WriteAllText ( tempFile , json ) ;
874+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => CredentialFactory . FromFile ( tempFile , credentialType ) ) ;
875+ Assert . Equal ( expectedMessage , ex . Message ) ;
876+ }
877+ finally
878+ {
879+ File . Delete ( tempFile ) ;
880+ }
881+ }
882+
883+ [ Theory ]
884+ [ MemberData ( nameof ( CredentialFactory_Success_Data ) ) ]
885+ public void FromJson_WithType ( string json , string credentialType , Type expectedType )
886+ {
887+ var credential = CredentialFactory . FromJson ( json , credentialType ) ;
888+ Assert . IsType ( expectedType , credential . UnderlyingCredential ) ;
889+ }
890+
891+ [ Theory ]
892+ [ MemberData ( nameof ( CredentialFactory_Failure_Data ) ) ]
893+ public void FromJson_WithType_Failure ( string json , string credentialType , string expectedMessage )
894+ {
895+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => CredentialFactory . FromJson ( json , credentialType ) ) ;
896+ Assert . Equal ( expectedMessage , ex . Message ) ;
897+ }
898+
899+ [ Theory ]
900+ [ MemberData ( nameof ( CredentialFactory_Success_Data ) ) ]
901+ public async Task FromFileAsync_WithType ( string json , string credentialType , Type expectedType )
902+ {
903+ var tempFile = Path . GetTempFileName ( ) ;
904+ try
905+ {
906+ File . WriteAllText ( tempFile , json ) ;
907+ var credential = await CredentialFactory . FromFileAsync ( tempFile , credentialType , CancellationToken . None ) ;
908+ Assert . IsType ( expectedType , credential . UnderlyingCredential ) ;
909+ }
910+ finally
911+ {
912+ File . Delete ( tempFile ) ;
913+ }
914+ }
915+
916+ [ Theory ]
917+ [ MemberData ( nameof ( CredentialFactory_Failure_Data ) ) ]
918+ public async Task FromFileAsync_WithType_Failure ( string json , string credentialType , string expectedMessage )
919+ {
920+ var tempFile = Path . GetTempFileName ( ) ;
921+ try
922+ {
923+ File . WriteAllText ( tempFile , json ) ;
924+ var ex = await Assert . ThrowsAsync < InvalidOperationException > ( ( ) => CredentialFactory . FromFileAsync ( tempFile , credentialType , CancellationToken . None ) ) ;
925+ Assert . Equal ( expectedMessage , ex . Message ) ;
926+ }
927+ finally
928+ {
929+ File . Delete ( tempFile ) ;
930+ }
931+ }
932+
933+ public static TheoryData < string , string , Type > CredentialFactory_Success_Data ( ) =>
934+ new TheoryData < string , string , Type >
935+ {
936+ { FakeUserCredentialFileContents , JsonCredentialParameters . AuthorizedUserCredentialType , typeof ( UserCredential ) } ,
937+ { FakeServiceAccountCredentialFileContents , JsonCredentialParameters . ServiceAccountCredentialType , typeof ( ServiceAccountCredential ) }
938+ } ;
939+
940+ public static TheoryData < string , string , string > CredentialFactory_Failure_Data ( ) =>
941+ new TheoryData < string , string , string >
942+ {
943+ { FakeUserCredentialFileContents , JsonCredentialParameters . ServiceAccountCredentialType , "The credential type authorized_user is not compatible with the requested type service_account" } ,
944+ { FakeUserCredentialFileContents , "invalid_type" , "The credential type authorized_user is not compatible with the requested type invalid_type" } ,
945+ { "invalid_json" , "any_type" , "Error deserializing JSON credential data." }
946+ } ;
947+
948+ [ Fact ]
949+ public void FromStream_WrapsDeserializationException_WithBadJson ( )
950+ {
951+ string malformedUserCredentialFileContents = FakeUserCredentialFileContents . Replace ( "}" , "" ) ;
952+ using Stream stream = new MemoryStream ( Encoding . UTF8 . GetBytes ( malformedUserCredentialFileContents ) ) ;
953+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => CredentialFactory . FromStream < UserCredential > ( stream ) ) ;
954+ Assert . Equal ( "Error deserializing JSON credential data." , ex . Message ) ;
955+ Assert . IsType < Newtonsoft . Json . JsonSerializationException > ( ex . InnerException ) ;
956+ }
957+
958+ [ Fact ]
959+ public void FromStream_WrapsDeserializationException ( )
960+ {
961+ var stream = new MockStream ( ( ) => throw new Exception ( "Underlying exception" ) ) ;
962+ var ex = Assert . Throws < InvalidOperationException > ( ( ) => CredentialFactory . FromStream < UserCredential > ( stream ) ) ;
963+ Assert . Equal ( "Error deserializing JSON credential data." , ex . Message ) ;
964+ Assert . IsType < Exception > ( ex . InnerException ) ;
965+ Assert . Equal ( "Underlying exception" , ex . InnerException . Message ) ;
966+ }
967+
968+ private class MockStream : Stream
969+ {
970+ private readonly Action _onRead ;
971+
972+ public MockStream ( Action onRead ) => _onRead = onRead ;
973+
974+ public override bool CanRead => true ;
975+ public override bool CanSeek => false ;
976+ public override bool CanWrite => false ;
977+ public override long Length => throw new NotSupportedException ( ) ;
978+ public override long Position { get => throw new NotSupportedException ( ) ; set => throw new NotSupportedException ( ) ; }
979+
980+ public override void Flush ( ) => throw new NotSupportedException ( ) ;
981+ public override int Read ( byte [ ] buffer , int offset , int count )
982+ {
983+ _onRead ( ) ;
984+ return 0 ;
985+ }
986+ public override long Seek ( long offset , SeekOrigin origin ) => throw new NotSupportedException ( ) ;
987+ public override void SetLength ( long value ) => throw new NotSupportedException ( ) ;
988+ public override void Write ( byte [ ] buffer , int offset , int count ) => throw new NotSupportedException ( ) ;
989+ }
990+
849991 /// <summary>
850992 /// Fake implementation of <see cref="IAuthorizationCodeFlow"/> which only supports fetching the
851993 /// clock and the access method.
0 commit comments