1+ // ---------------------------------------------------------------------------------- 
2+ // 
3+ // Copyright Microsoft Corporation 
4+ // Licensed under the Apache License, Version 2.0 (the "License"); 
5+ // you may not use this file except in compliance with the License. 
6+ // You may obtain a copy of the License at 
7+ // http://www.apache.org/licenses/LICENSE-2.0 
8+ // Unless required by applicable law or agreed to in writing, software 
9+ // distributed under the License is distributed on an "AS IS" BASIS, 
10+ // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
11+ // See the License for the specific language governing permissions and 
12+ // limitations under the License. 
13+ // ---------------------------------------------------------------------------------- 
14+ using  Microsoft . Azure . Commands . Common . Authentication . Abstractions ; 
15+ using  Microsoft . Azure . Commands . Common . Authentication . Abstractions . Interfaces ; 
16+ 
17+ using  Moq ; 
18+ 
19+ using  System ; 
20+ using  System . Collections . Generic ; 
21+ using  System . Linq ; 
22+ using  System . Threading . Tasks ; 
23+ 
24+ using  Xunit ; 
25+ 
26+ namespace  Authentication . Abstractions . Test 
27+ { 
28+     public  class  AuthenticationTelemetryTest 
29+     { 
30+         // Theory for PushTelemetryRecord tests 
31+         [ Theory ] 
32+         [ InlineData ( true ,  true ,  true ,  1 ,  1 ,  0 ) ]      // Valid context, valid record -> success 
33+         [ InlineData ( false ,  true ,  false ,  0 ,  0 ,  1 ) ]    // Invalid context, valid record -> failure 
34+         [ InlineData ( true ,  false ,  false ,  0 ,  0 ,  1 ) ]    // Valid context, null record -> failure 
35+         [ InlineData ( null ,  true ,  false ,  0 ,  0 ,  1 ) ]     // Null context, valid record -> failure 
36+         public  void  PushTelemetryRecord_Tests ( bool ?  isContextValid ,  bool  hasRecord ,  bool  expectedResult , 
37+                                              int  expectedKeysCurrent ,  int  expectedKeysAll ,  int  expectedEmptyCount ) 
38+         { 
39+             // Arrange 
40+             var  telemetry  =  new  AuthenticationTelemetry ( ) ; 
41+             ICmdletContext  context  =  null ; 
42+             AuthTelemetryRecord  record  =  hasRecord  ?  new  AuthTelemetryRecord ( )  :  null ; 
43+ 
44+             if  ( isContextValid . HasValue ) 
45+             { 
46+                 var  mockContext  =  new  Mock < ICmdletContext > ( ) ; 
47+                 mockContext . Setup ( c =>  c . IsValid ) . Returns ( isContextValid . Value ) ; 
48+                 if  ( isContextValid . Value ) 
49+                 { 
50+                     mockContext . Setup ( c =>  c . CmdletId ) . Returns ( "TestCmdlet" ) ; 
51+                 } 
52+                 context  =  mockContext . Object ; 
53+             } 
54+ 
55+             // Act 
56+             var  result  =  telemetry . PushDataRecord ( context ,  record ) ; 
57+ 
58+             // Assert 
59+             Assert . Equal ( expectedResult ,  result ) ; 
60+         } 
61+ 
62+         // Test data for PopTelemetryRecord tests 
63+         public  static IEnumerable < object [ ] >  PopTelemetryRecordTestData  => 
64+             new  List < object [ ] > 
65+             { 
66+                 // Parameters: isContextValid, cmdletId, pushBeforePop, expectedNotNull, expectedKeysCount, expectedAllCount, expectedEmptyCount, expectedKeyNotFoundCount 
67+                 new  object [ ]  {  true ,  "TestCmdlet" ,  true ,  true ,  0 ,  1 ,  0 ,  0  } ,     // Valid context with existing record 
68+                 new  object [ ]  {  null ,  null ,  false ,  false ,  0 ,  0 ,  1 ,  0  } ,            // Null context 
69+                 new  object [ ]  {  false ,  null ,  false ,  false ,  0 ,  0 ,  1 ,  0  } ,           // Invalid context 
70+                 new  object [ ]  {  true ,  "TestCmdlet" ,  false ,  false ,  0 ,  0 ,  0 ,  1  }     // Valid context with non-existent key 
71+             } ; 
72+ 
73+         [ Theory ] 
74+         [ MemberData ( nameof ( PopTelemetryRecordTestData ) ) ] 
75+         public  void  PopTelemetryRecord_Tests ( bool ?  isContextValid ,  string  cmdletId ,  bool  pushBeforePop , 
76+                                            bool  expectedNotNull ,  int  expectedKeysCount ,  int  expectedAllCount , 
77+                                            int  expectedEmptyCount ,  int  expectedKeyNotFoundCount ) 
78+         { 
79+             // Arrange 
80+             var  telemetry  =  new  AuthenticationTelemetry ( ) ; 
81+             ICmdletContext  context  =  null ; 
82+ 
83+             if  ( isContextValid . HasValue ) 
84+             { 
85+                 var  mockContext  =  new  Mock < ICmdletContext > ( ) ; 
86+                 mockContext . Setup ( c =>  c . IsValid ) . Returns ( isContextValid . Value ) ; 
87+                 if  ( ! string . IsNullOrEmpty ( cmdletId ) ) 
88+                 { 
89+                     mockContext . Setup ( c =>  c . CmdletId ) . Returns ( cmdletId ) ; 
90+                 } 
91+                 context  =  mockContext . Object ; 
92+             } 
93+ 
94+             // Push a record first if needed 
95+             if  ( pushBeforePop  &&  context  !=  null ) 
96+             { 
97+                 var  record  =  new  AuthTelemetryRecord  {  TokenCredentialName  =  "TestCredential"  } ; 
98+                 Assert . True ( telemetry . PushDataRecord ( context ,  record ) ) ; 
99+             } 
100+ 
101+             // Act 
102+             var  result  =  telemetry . PopDataRecords ( context ) ; 
103+ 
104+             // Assert 
105+             Assert . Equal ( expectedNotNull ,  result  !=  null ) ; 
106+             if  ( expectedNotNull ) 
107+             { 
108+                 Assert . Single ( result ) ; 
109+                 Assert . Equal ( "TestCredential" ,  result . FirstOrDefault ( ) ? . TokenCredentialName ) ; 
110+             } 
111+         } 
112+ 
113+         // Test data for GetTelemetryRecord tests 
114+         public  static IEnumerable < object [ ] >  GetTelemetryRecordTestData  => 
115+             new  List < object [ ] > 
116+             { 
117+         // Parameters: isContextValid, cmdletId, recordCount, expectedNotNull 
118+         new  object [ ]  {  true ,  "TestCmdlet" ,  1 ,  true  } ,      // Valid context with single record 
119+         new  object [ ]  {  true ,  "TestCmdlet" ,  3 ,  true  } ,      // Valid context with multiple records 
120+         new  object [ ]  {  null ,  null ,  0 ,  false  } ,             // Null context 
121+         new  object [ ]  {  false ,  null ,  0 ,  false  } ,            // Invalid context 
122+         new  object [ ]  {  true ,  "TestCmdlet" ,  0 ,  false  }      // Valid context with no records 
123+             } ; 
124+ 
125+         [ Theory ] 
126+         [ MemberData ( nameof ( GetTelemetryRecordTestData ) ) ] 
127+         public  void  GetTelemetryRecord_Tests ( bool ?  isContextValid ,  string  cmdletId ,  int  recordCount , 
128+                                            bool  expectedNotNull ) 
129+         { 
130+             // Arrange 
131+             var  telemetry  =  new  AuthenticationTelemetry ( ) ; 
132+             ICmdletContext  context  =  null ; 
133+ 
134+             if  ( isContextValid . HasValue ) 
135+             { 
136+                 var  mockContext  =  new  Mock < ICmdletContext > ( ) ; 
137+                 mockContext . Setup ( c =>  c . IsValid ) . Returns ( isContextValid . Value ) ; 
138+                 if  ( ! string . IsNullOrEmpty ( cmdletId ) ) 
139+                 { 
140+                     mockContext . Setup ( c =>  c . CmdletId ) . Returns ( cmdletId ) ; 
141+                 } 
142+                 context  =  mockContext . Object ; 
143+             } 
144+ 
145+             // Push records if needed 
146+             for  ( int  i  =  0 ;  i  <  recordCount ;  i ++ ) 
147+             { 
148+                 var  record  =  new  AuthTelemetryRecord  {  TokenCredentialName  =  $ "TestCredential{ i } "} ; 
149+                 Assert . True ( telemetry . PushDataRecord ( context ,  record ) ) ; 
150+             } 
151+ 
152+             // Act 
153+             var  result  =  telemetry . GetTelemetryRecord ( context ) ; 
154+ 
155+             // Assert 
156+             Assert . Equal ( expectedNotNull ,  result  !=  null ) ; 
157+ 
158+             if  ( expectedNotNull ) 
159+             { 
160+                 // Verify the AuthenticationTelemetryData contains our records 
161+                 Assert . NotNull ( result . Primary ) ; 
162+                 Assert . Equal ( "TestCredential0" ,  result . Primary . TokenCredentialName ) ; 
163+ 
164+                 if  ( recordCount  >  1 ) 
165+                 { 
166+                     Assert . NotEmpty ( result . Secondary ) ; 
167+                     Assert . Equal ( recordCount  -  1 ,  result . Secondary . Count ) ; 
168+ 
169+                     // Verify each record in the tail 
170+                     for  ( int  i  =  1 ;  i  <  recordCount ;  i ++ ) 
171+                     { 
172+                         Assert . Equal ( $ "TestCredential{ i } ",  result . Secondary [ i  -  1 ] . TokenCredentialName ) ; 
173+                     } 
174+                 } 
175+                 else 
176+                 { 
177+                     Assert . Empty ( result . Secondary ) ; 
178+                 } 
179+             } 
180+         } 
181+         [ Fact ] 
182+         public  void  TelemetryRecord_ConcurrentTests ( ) 
183+         { 
184+             // Arrange 
185+             var  telemetry  =  new  AuthenticationTelemetry ( ) ; 
186+             const  int  pusherThreadCount  =  10 ; 
187+ 
188+             // Create delegate to push records and get telemetry data 
189+             Func < ICmdletContext ,  AuthenticationTelemetryData >  PushAndGetFromMultipleThreads  =  ( context )  => 
190+             { 
191+                 // Create tasks for pushing records (10 threads) 
192+                 var  pushTasks  =  new  Task [ pusherThreadCount ] ; 
193+                 for  ( int  t  =  0 ;  t  <  pusherThreadCount ;  t ++ ) 
194+                 { 
195+                     var  threadId  =  t ; 
196+                     pushTasks [ t ]  =  Task . Run ( ( )  => 
197+                     { 
198+                         // Each thread pushes one unique record 
199+                         var  record  =  new  AuthTelemetryRecord  {  TokenCredentialName  =  $ "TestCredential-{ context . CmdletId } -{ threadId } "} ; 
200+                         Assert . True ( telemetry . PushDataRecord ( context ,  record ) ) ; 
201+                     } ) ; 
202+                 } 
203+                 Task . WaitAll ( pushTasks ) ;  // Wait for all push tasks to complete 
204+                 return  telemetry . GetTelemetryRecord ( context ) ; 
205+             } ; 
206+ 
207+             // Create two contexts 
208+             var  mockContext1  =  new  Mock < ICmdletContext > ( ) ; 
209+             mockContext1 . Setup ( c =>  c . IsValid ) . Returns ( true ) ; 
210+             mockContext1 . Setup ( c =>  c . CmdletId ) . Returns ( "TestCmdlet1" ) ; 
211+             var  context1  =  mockContext1 . Object ; 
212+ 
213+             var  mockContext2  =  new  Mock < ICmdletContext > ( ) ; 
214+             mockContext2 . Setup ( c =>  c . IsValid ) . Returns ( true ) ; 
215+             mockContext2 . Setup ( c =>  c . CmdletId ) . Returns ( "TestCmdlet2" ) ; 
216+             var  context2  =  mockContext2 . Object ; 
217+ 
218+             // Act 
219+             // Run tasks in parallel 
220+             var  task1  =  Task < AuthenticationTelemetryData > . Run ( ( )  =>  PushAndGetFromMultipleThreads ( context1 ) ) ; 
221+             var  task2  =  Task < AuthenticationTelemetryData > . Run ( ( )  =>  PushAndGetFromMultipleThreads ( context2 ) ) ; 
222+ 
223+             // Wait for both tasks to complete 
224+             Task . WaitAll ( task1 ,  task2 ) ; 
225+ 
226+             // Get results 
227+             var  results1  =  task1 . Result ; 
228+             var  results2  =  task2 . Result ; 
229+ 
230+             // Assert 
231+             // Check that we have results from both contexts 
232+             Assert . NotNull ( results1 ) ; 
233+             Assert . True ( results1 . Primary ? . TokenCredentialName . StartsWith ( "TestCredential-TestCmdlet1" ) ) ; 
234+             Assert . Equal ( 9 ,  results1 . Secondary ? . Count ) ; 
235+             Assert . NotNull ( results2 ) ; 
236+             Assert . True ( results2 . Primary ? . TokenCredentialName . StartsWith ( "TestCredential-TestCmdlet2" ) ) ; 
237+             Assert . Equal ( 9 ,  results2 . Secondary ? . Count ) ; 
238+ 
239+             // Verify all records were retrieved (nothing left) 
240+             Assert . Null ( telemetry . GetTelemetryRecord ( context1 ) ) ; 
241+             Assert . Null ( telemetry . GetTelemetryRecord ( context2 ) ) ; 
242+         } 
243+     } 
244+ } 
0 commit comments