99using System . Linq ;
1010using System . Net . Http ;
1111using System . Security . Cryptography ;
12+ using System . Threading ;
1213using System . Threading . Tasks ;
1314using Xunit ;
1415using Xunit . Abstractions ;
@@ -23,7 +24,7 @@ public class IdentityUIScriptsTest : IDisposable
2324 public IdentityUIScriptsTest ( ITestOutputHelper output )
2425 {
2526 _output = output ;
26- _httpClient = new HttpClient ( ) ;
27+ _httpClient = new HttpClient ( new RetryHandler ( new HttpClientHandler ( ) { } ) ) ;
2728 }
2829
2930 public static IEnumerable < object [ ] > ScriptWithIntegrityData
@@ -40,15 +41,18 @@ public static IEnumerable<object[]> ScriptWithIntegrityData
4041 [ MemberData ( nameof ( ScriptWithIntegrityData ) ) ]
4142 public async Task IdentityUI_ScriptTags_SubresourceIntegrityCheck ( ScriptTag scriptTag )
4243 {
43- string expectedIntegrity ;
44+ var sha256Integrity = await GetShaIntegrity ( scriptTag , SHA256 . Create ( ) , "sha256" ) ;
45+ Assert . Equal ( scriptTag . Integrity , sha256Integrity ) ;
46+ }
47+
48+ private async Task < string > GetShaIntegrity ( ScriptTag scriptTag , HashAlgorithm algorithm , string prefix )
49+ {
4450 using ( var respStream = await _httpClient . GetStreamAsync ( scriptTag . Src ) )
4551 using ( var alg = SHA256 . Create ( ) )
4652 {
4753 var hash = alg . ComputeHash ( respStream ) ;
48- expectedIntegrity = "sha256 -" + Convert . ToBase64String ( hash ) ;
54+ return $ " { prefix } -" + Convert . ToBase64String ( hash ) ;
4955 }
50-
51- Assert . Equal ( expectedIntegrity , scriptTag . Integrity ) ;
5256 }
5357
5458 public static IEnumerable < object [ ] > ScriptWithFallbackSrcData
@@ -66,7 +70,7 @@ public static IEnumerable<object[]> ScriptWithFallbackSrcData
6670 public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent ( ScriptTag scriptTag )
6771 {
6872 var slnDir = GetSolutionDir ( ) ;
69- var wwwrootDir = Path . Combine ( slnDir , "src" , "UI" , "wwwroot" , "V4" ) ;
73+ var wwwrootDir = Path . Combine ( slnDir , "src" , "UI" , "wwwroot" , scriptTag . Version ) ;
7074
7175 var cdnContent = await _httpClient . GetStringAsync ( scriptTag . Src ) ;
7276 var fallbackSrcContent = File . ReadAllText (
@@ -77,6 +81,7 @@ public async Task IdentityUI_ScriptTags_FallbackSourceContent_Matches_CDNContent
7781
7882 public struct ScriptTag
7983 {
84+ public string Version ;
8085 public string Src ;
8186 public string Integrity ;
8287 public string FallbackSrc ;
@@ -91,8 +96,9 @@ public override string ToString()
9196 private static List < ScriptTag > GetScriptTags ( )
9297 {
9398 var slnDir = GetSolutionDir ( ) ;
94- var uiDir = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V4" ) ;
95- var cshtmlFiles = Directory . GetFiles ( uiDir , "*.cshtml" , SearchOption . AllDirectories ) ;
99+ var uiDirV3 = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V3" ) ;
100+ var uiDirV4 = Path . Combine ( slnDir , "src" , "UI" , "Areas" , "Identity" , "Pages" , "V4" ) ;
101+ var cshtmlFiles = GetRazorFiles ( uiDirV3 ) . Concat ( GetRazorFiles ( uiDirV4 ) ) ;
96102
97103 var scriptTags = new List < ScriptTag > ( ) ;
98104 foreach ( var cshtmlFile in cshtmlFiles )
@@ -104,6 +110,8 @@ private static List<ScriptTag> GetScriptTags()
104110 Assert . NotEmpty ( scriptTags ) ;
105111
106112 return scriptTags ;
113+
114+ IEnumerable < string > GetRazorFiles ( string dir ) => Directory . GetFiles ( dir , "*.cshtml" , SearchOption . AllDirectories ) ;
107115 }
108116
109117 private static List < ScriptTag > GetScriptTags ( string cshtmlFile )
@@ -123,6 +131,7 @@ private static List<ScriptTag> GetScriptTags(string cshtmlFile)
123131
124132 scriptTags . Add ( new ScriptTag
125133 {
134+ Version = cshtmlFile . Contains ( "V3" ) ? "V3" : "V4" ,
126135 Src = scriptElement . Source ,
127136 Integrity = scriptElement . Integrity ,
128137 FallbackSrc = fallbackSrcAttribute ? . Value ,
@@ -155,5 +164,25 @@ public void Dispose()
155164 {
156165 _httpClient . Dispose ( ) ;
157166 }
167+
168+ class RetryHandler : DelegatingHandler
169+ {
170+ public RetryHandler ( HttpMessageHandler innerHandler ) : base ( innerHandler ) { }
171+
172+ protected override async Task < HttpResponseMessage > SendAsync ( HttpRequestMessage request , CancellationToken cancellationToken )
173+ {
174+ HttpResponseMessage result = null ;
175+ for ( var i = 0 ; i < 10 ; i ++ )
176+ {
177+ result = await base . SendAsync ( request , cancellationToken ) ;
178+ if ( result . IsSuccessStatusCode )
179+ {
180+ return result ;
181+ }
182+ await Task . Delay ( 1000 ) ;
183+ }
184+ return result ;
185+ }
186+ }
158187 }
159188}
0 commit comments