1
1
// Copyright (c) Microsoft Corporation. All rights reserved.
2
2
// Licensed under the MIT License.
3
3
4
- using System ;
5
4
using System . Collections . Generic ;
6
5
using System . Linq ;
7
- using System . Reflection ;
8
- using System . Threading ;
9
- using System . Threading . Tasks ;
10
- using Azure ;
11
- using Azure . Storage . Blobs ;
12
- using Azure . Storage . Blobs . Models ;
13
- using Microsoft . Extensions . Logging ;
14
- using Microsoft . Extensions . Logging . Abstractions ;
15
- using Moq ;
16
6
using NUnit . Framework ;
17
7
18
8
namespace Microsoft . Azure . WebJobs . Extensions . Storage . Blobs . Listeners
19
9
{
20
10
public class BlobLogListenerTests
21
11
{
22
- [ TestCase ( "write" , 1 , null , 0 , true , TestName = "HasBlobWritesAsync_WriteLogPresent_ReturnsTrue" ) ]
23
- [ TestCase ( "read" , 1 , null , 0 , false , TestName = "HasBlobWritesAsync_NonWriteLogPresent_ReturnsFalse" ) ]
24
- [ TestCase ( null , 1 , null , 0 , false , TestName = "HasBlobWritesAsync_NoBlobs_ReturnsFalse" ) ]
25
- [ TestCase ( "read" , 100 , "write" , 1 , true , TestName = "HasBlobWritesAsync_WriteLogPresentMultipleLogBlobs_ReturnsTrue" ) ]
26
- [ TestCase ( "delete" , 100 , "read" , 100 , false , TestName = "HasBlobWritesAsync_NonWriteLogPresentMultipleLogBlobs_ReturnsFalse" ) ]
27
- public async Task HasBlobWritesAsync_VariousCases ( string logType1 , int logType1Count , string logType2 , int logType2Count , bool expected )
28
- {
29
- // Arrange
30
- var blobServiceClientMock = new Mock < BlobServiceClient > ( MockBehavior . Strict ) ;
31
- var containerClientMock = new Mock < BlobContainerClient > ( MockBehavior . Strict ) ;
32
-
33
- TestAsyncPageable < BlobItem > pageable ;
34
- var blobItems = new List < BlobItem > ( ) ;
35
- if ( logType1 != null )
36
- {
37
- for ( int i = 0 ; i < logType1Count ; i ++ )
38
- {
39
- var blobItem = BlobItemFactory . Create (
40
- name : Guid . NewGuid ( ) . ToString ( ) ,
41
- metadata : new Dictionary < string , string > { { "LogType" , logType1 } } ) ;
42
- blobItems . Add ( blobItem ) ;
43
- }
44
- }
45
- if ( logType2 != null )
46
- {
47
- for ( int i = 0 ; i < logType2Count ; i ++ )
48
- {
49
- var blobItem = BlobItemFactory . Create (
50
- name : Guid . NewGuid ( ) . ToString ( ) ,
51
- metadata : new Dictionary < string , string > { { "LogType" , logType2 } } ) ;
52
- blobItems . Add ( blobItem ) ;
53
- }
54
- }
55
-
56
- if ( blobItems . Count == 0 )
57
- {
58
- pageable = new TestAsyncPageable < BlobItem > ( Enumerable . Empty < BlobItem > ( ) ) ;
59
- }
60
- else
61
- {
62
- pageable = new TestAsyncPageable < BlobItem > ( blobItems ) ;
63
- }
64
-
65
- containerClientMock
66
- . Setup ( c => c . GetBlobsAsync (
67
- It . IsAny < BlobTraits > ( ) ,
68
- It . IsAny < BlobStates > ( ) ,
69
- It . IsAny < string > ( ) ,
70
- It . IsAny < CancellationToken > ( ) ) )
71
- . Returns ( pageable ) ;
72
-
73
- blobServiceClientMock
74
- . Setup ( c => c . GetBlobContainerClient ( "$logs" ) )
75
- . Returns ( containerClientMock . Object ) ;
76
-
77
- var listener = new BlobLogListener ( blobServiceClientMock . Object , NullLogger < BlobListener > . Instance ) ;
78
-
79
- // Act
80
- bool result = await listener . HasBlobWritesAsync ( CancellationToken . None , hoursWindow : 1 ) ;
81
-
82
- // Assert
83
- Assert . AreEqual ( expected , result ) ;
84
- }
85
-
86
12
[ Test ]
87
13
public void GetPathsForValidBlobWrites_Returns_ValidBlobWritesOnly ( )
88
14
{
89
15
StorageAnalyticsLogEntry [ ] entries = new [ ]
90
16
{
17
+ // This is a valid write entry with a valid path
91
18
new StorageAnalyticsLogEntry
92
19
{
93
20
ServiceType = StorageServiceType . Blob ,
94
21
OperationType = StorageServiceOperationType . PutBlob ,
95
22
RequestedObjectKey = @"/storagesample/sample-container/""0x8D199A96CB71468""/sample-blob.txt"
96
23
} ,
24
+
25
+ // This is an invalid path and will be filtered out
97
26
new StorageAnalyticsLogEntry
98
27
{
99
28
ServiceType = StorageServiceType . Blob ,
100
29
OperationType = StorageServiceOperationType . PutBlob ,
101
30
RequestedObjectKey = "/"
102
31
} ,
32
+
33
+ // This does not constitute a write and will be filtered out
103
34
new StorageAnalyticsLogEntry
104
35
{
105
36
ServiceType = StorageServiceType . Blob ,
@@ -114,52 +45,5 @@ public void GetPathsForValidBlobWrites_Returns_ValidBlobWritesOnly()
114
45
Assert . AreEqual ( "sample-container" , singlePath . ContainerName ) ;
115
46
Assert . AreEqual ( @"""0x8D199A96CB71468""/sample-blob.txt" , singlePath . BlobName ) ;
116
47
}
117
-
118
- private static class BlobItemFactory
119
- {
120
- private static readonly Type BlobItemType = typeof ( BlobItem ) ;
121
- private static readonly System . Reflection . PropertyInfo NameProp = BlobItemType . GetProperty ( nameof ( BlobItem . Name ) ) ! ;
122
- private static readonly System . Reflection . PropertyInfo MetadataProp = BlobItemType . GetProperty ( nameof ( BlobItem . Metadata ) ) ! ;
123
-
124
- public static BlobItem Create ( string name , IDictionary < string , string > metadata )
125
- {
126
- var ctor = BlobItemType . GetConstructor ( BindingFlags . Instance | BindingFlags . NonPublic , binder : null , types : Type . EmptyTypes , modifiers : null )
127
- ?? throw new InvalidOperationException ( "BlobItem internal constructor not found." ) ;
128
- object raw = ctor . Invoke ( null ) ;
129
-
130
- NameProp . SetValue ( raw , name ) ;
131
- var dict = new Dictionary < string , string > ( metadata , StringComparer . OrdinalIgnoreCase ) ;
132
- MetadataProp . SetValue ( raw , dict ) ;
133
-
134
- return ( BlobItem ) raw ;
135
- }
136
- }
137
-
138
- private sealed class TestAsyncPageable < T > : AsyncPageable < T >
139
- {
140
- private readonly IReadOnlyList < Page < T > > _pages ;
141
-
142
- public TestAsyncPageable ( IEnumerable < T > items )
143
- {
144
- var list = items . ToList ( ) ;
145
- var responseMock = new Mock < Response > ( ) ;
146
- responseMock . Setup ( r => r . Status ) . Returns ( 200 ) ;
147
- responseMock . Setup ( r => r . ClientRequestId ) . Returns ( Guid . NewGuid ( ) . ToString ( ) ) ;
148
-
149
- _pages = new [ ]
150
- {
151
- Page < T > . FromValues ( list , continuationToken : null , response : responseMock . Object )
152
- } ;
153
- }
154
-
155
- public override async IAsyncEnumerable < Page < T > > AsPages ( string continuationToken = null , int ? pageSizeHint = null )
156
- {
157
- foreach ( var p in _pages )
158
- {
159
- yield return p ;
160
- await Task . Yield ( ) ;
161
- }
162
- }
163
- }
164
48
}
165
- }
49
+ }
0 commit comments