1
+ // Copyright (c) .NET Foundation. All rights reserved.
2
+ // Licensed under the MIT License. See License.txt in the project root for license information.
3
+
4
+ using System ;
5
+ using System . Collections . Generic ;
6
+ using System . IO ;
7
+ using System . IO . Abstractions ;
8
+ using System . Linq ;
9
+ using System . Text ;
10
+ using System . Threading . Tasks ;
11
+ using Microsoft . Azure . WebJobs . Script . WebHost ;
12
+ using Microsoft . Extensions . Logging . Abstractions ;
13
+ using Moq ;
14
+ using Xunit ;
15
+
16
+ namespace Microsoft . Azure . WebJobs . Script . Tests ;
17
+
18
+ public class ContainerAppsSecretsRepositoryTests : IDisposable
19
+ {
20
+ private Dictionary < string , Func < MemoryStream > > _fileContentMap ;
21
+ private ContainerAppsSecretsRepository _repo ;
22
+
23
+ public ContainerAppsSecretsRepositoryTests ( )
24
+ {
25
+ // Mock the file system to return predefined secrets
26
+ var mockFileSystem = new Mock < IFileSystem > ( MockBehavior . Strict ) ;
27
+ var mockFile = new Mock < FileBase > ( MockBehavior . Strict ) ;
28
+ var mockDirectory = new Mock < DirectoryBase > ( MockBehavior . Strict ) ;
29
+
30
+ // Setup directory and file existence
31
+ mockDirectory . Setup ( d => d . Exists ( It . IsAny < string > ( ) ) ) . Returns ( true ) ;
32
+ mockFileSystem . SetupGet ( fs => fs . Directory ) . Returns ( mockDirectory . Object ) ;
33
+ mockFileSystem . SetupGet ( fs => fs . File ) . Returns ( mockFile . Object ) ;
34
+
35
+ // Return all files when asked
36
+ mockDirectory
37
+ . Setup ( d => d . GetFiles ( ContainerAppsSecretsRepository . ContainerAppsSecretsDir , "*" ) )
38
+ . Returns ( ( ) => _fileContentMap . Keys . ToArray ( ) ) ;
39
+
40
+ // Setup file existence checks
41
+ mockFile
42
+ . Setup ( f => f . Exists ( It . IsAny < string > ( ) ) )
43
+ . Returns ( ( string f ) => _fileContentMap . ContainsKey ( f ) ) ;
44
+
45
+ // Return content when asked
46
+ mockFile
47
+ . Setup ( f => f . Open ( It . IsAny < string > ( ) , FileMode . Open , FileAccess . Read , FileShare . ReadWrite | FileShare . Delete ) )
48
+ . Returns ( ( string f , FileMode _ , FileAccess _ , FileShare _ ) => _fileContentMap [ f ] ( ) ) ;
49
+
50
+ FileUtility . Instance = mockFileSystem . Object ;
51
+
52
+ _repo = new ContainerAppsSecretsRepository ( NullLogger < ContainerAppsSecretsRepository > . Instance ) ;
53
+ }
54
+
55
+ [ Fact ]
56
+ public async Task Read_Host_Secrets ( )
57
+ {
58
+ _fileContentMap = new ( )
59
+ {
60
+ { "/run/secrets/functions-keys/host.master" , ( ) => GetStream ( "mk" ) } ,
61
+ { "/run/secrets/functions-keys/host.function.default" , ( ) => GetStream ( "hfd" ) } ,
62
+ { "/run/secrets/functions-keys/host.function.key1" , ( ) => GetStream ( "hfk1" ) } ,
63
+ { "/run/secrets/functions-keys/host.systemKey.key1" , ( ) => GetStream ( "hsk1" ) } ,
64
+ } ;
65
+
66
+ var result = await _repo . ReadAsync ( ScriptSecretsType . Host , null ) ;
67
+
68
+ var hostSecrets = result as HostSecrets ;
69
+ Assert . NotNull ( hostSecrets ) ;
70
+ Assert . Equal ( "mk" , hostSecrets . MasterKey . Value ) ;
71
+ Assert . Equal ( "hfd" , hostSecrets . GetFunctionKey ( "default" , HostKeyScopes . FunctionKeys ) . Value ) ;
72
+ Assert . Equal ( "hfk1" , hostSecrets . GetFunctionKey ( "Key1" , HostKeyScopes . FunctionKeys ) . Value ) ;
73
+ Assert . Equal ( "hsk1" , hostSecrets . GetFunctionKey ( "Key1" , HostKeyScopes . SystemKeys ) . Value ) ;
74
+ }
75
+
76
+ [ Fact ]
77
+ public async Task Read_Function_Secrets ( )
78
+ {
79
+ _fileContentMap = new ( )
80
+ {
81
+ { "/run/secrets/functions-keys/functions.function1.key1" , ( ) => GetStream ( "f1k1" ) } ,
82
+ { "/run/secrets/functions-keys/functions.function1.key2" , ( ) => GetStream ( "f1k2" ) } ,
83
+ { "/run/secrets/functions-keys/functions.function2.key1" , ( ) => GetStream ( "f2k1" ) } ,
84
+ { "/run/secrets/functions-keys/functions.function2.key2" , ( ) => GetStream ( "f2k2" ) }
85
+ } ;
86
+
87
+ var result = await _repo . ReadAsync ( ScriptSecretsType . Function , "function1" ) ;
88
+
89
+ var functionSecrets = result as FunctionSecrets ;
90
+ Assert . NotNull ( functionSecrets ) ;
91
+ Assert . Equal ( "f1k1" , functionSecrets . GetFunctionKey ( "Key1" , "function1" ) . Value ) ;
92
+ Assert . Equal ( "f1k2" , functionSecrets . GetFunctionKey ( "key2" , "Function1" ) . Value ) ;
93
+
94
+ result = await _repo . ReadAsync ( ScriptSecretsType . Function , "function2" ) ;
95
+ functionSecrets = result as FunctionSecrets ;
96
+ Assert . NotNull ( functionSecrets ) ;
97
+ Assert . Equal ( "f2k1" , functionSecrets . GetFunctionKey ( "Key1" , "funcTion2" ) . Value ) ;
98
+ Assert . Equal ( "f2k2" , functionSecrets . GetFunctionKey ( "key2" , "function2" ) . Value ) ;
99
+ }
100
+
101
+ [ Fact ]
102
+ public async Task No_HostKeys_Returns_Empty_HostSecrets ( )
103
+ {
104
+ _fileContentMap = [ ] ;
105
+
106
+ var result = await _repo . ReadAsync ( ScriptSecretsType . Host , null ) ;
107
+
108
+ var hostSecrets = result as HostSecrets ;
109
+ Assert . NotNull ( hostSecrets ) ;
110
+ Assert . Null ( hostSecrets . MasterKey ) ;
111
+ Assert . Empty ( hostSecrets . FunctionKeys ) ;
112
+ Assert . Empty ( hostSecrets . SystemKeys ) ;
113
+ }
114
+
115
+ [ Fact ]
116
+ public async Task No_FunctionKeys_Returns_Empty_FunctionSecrets ( )
117
+ {
118
+ _fileContentMap = [ ] ;
119
+
120
+ var result = await _repo . ReadAsync ( ScriptSecretsType . Function , "function1" ) ;
121
+
122
+ var hostSecrets = result as FunctionSecrets ;
123
+ Assert . NotNull ( hostSecrets ) ;
124
+ Assert . Empty ( hostSecrets . Keys ) ;
125
+ }
126
+
127
+ [ Fact ]
128
+ public async Task SecretManager_DoesNotCreate_HostSecrets ( )
129
+ {
130
+ // no keys; we don't want the SecretManager to try to create new ones
131
+ _fileContentMap = [ ] ;
132
+
133
+ var testEnvironment = new TestEnvironment ( ) ;
134
+ var mockHostNameProvider = new Mock < HostNameProvider > ( MockBehavior . Strict , testEnvironment ) ;
135
+ var startupContextProvider = new StartupContextProvider ( testEnvironment , NullLogger < StartupContextProvider > . Instance ) ;
136
+
137
+ var secretManager = new SecretManager ( _repo , NullLogger . Instance , new TestMetricsLogger ( ) , mockHostNameProvider . Object , startupContextProvider ) ;
138
+
139
+ var hostSecrets = await secretManager . GetHostSecretsAsync ( ) ;
140
+
141
+ Assert . NotNull ( hostSecrets ) ;
142
+ Assert . Null ( hostSecrets . MasterKey ) ;
143
+ Assert . Empty ( hostSecrets . FunctionKeys ) ;
144
+ Assert . Empty ( hostSecrets . SystemKeys ) ;
145
+ }
146
+
147
+ [ Fact ]
148
+ public async Task SecretManager_DoesNotCreate_FunctionSecrets ( )
149
+ {
150
+ // no keys; we don't want the SecretManager to try to create new ones
151
+ _fileContentMap = [ ] ;
152
+
153
+ var testEnvironment = new TestEnvironment ( ) ;
154
+ var mockHostNameProvider = new Mock < HostNameProvider > ( MockBehavior . Strict , testEnvironment ) ;
155
+ var startupContextProvider = new StartupContextProvider ( testEnvironment , NullLogger < StartupContextProvider > . Instance ) ;
156
+
157
+ var secretManager = new SecretManager ( _repo , NullLogger . Instance , new TestMetricsLogger ( ) , mockHostNameProvider . Object , startupContextProvider ) ;
158
+
159
+ var functionSecrets = await secretManager . GetFunctionSecretsAsync ( "function1" ) ;
160
+
161
+ Assert . NotNull ( functionSecrets ) ;
162
+ Assert . Empty ( functionSecrets ) ;
163
+ }
164
+
165
+ private static MemoryStream GetStream ( string content )
166
+ {
167
+ return new MemoryStream ( Encoding . UTF8 . GetBytes ( content ) ) ;
168
+ }
169
+
170
+ public void Dispose ( )
171
+ {
172
+ FileUtility . Instance = null ;
173
+ }
174
+ }
0 commit comments