@@ -282,6 +282,155 @@ public async Task AddOrUpdateFunctionSecrets_WithFunctionNameAndNoSecret_Generat
282
282
}
283
283
}
284
284
285
+ [ Fact ]
286
+ public async Task AddOrUpdateFunctionSecret_ClearsCache_WhenFunctionSecretAdded ( )
287
+ {
288
+ using ( var directory = new TempDirectory ( ) )
289
+ {
290
+ CreateTestSecrets ( directory . Path ) ;
291
+
292
+ Mock < IKeyValueConverterFactory > mockValueConverterFactory = GetConverterFactoryMock ( false ) ;
293
+ KeyOperationResult result ;
294
+ var traceWriter = new TestTraceWriter ( TraceLevel . Verbose ) ;
295
+ ISecretsRepository repository = new FileSystemSecretsRepository ( directory . Path ) ;
296
+ using ( var secretManager = new SecretManager ( repository , mockValueConverterFactory . Object , traceWriter ) )
297
+ {
298
+ var keys = await secretManager . GetFunctionSecretsAsync ( "testfunction" ) ;
299
+ Assert . Equal ( 2 , keys . Count ) ;
300
+
301
+ // add a new key
302
+ result = await secretManager . AddOrUpdateFunctionSecretAsync ( "function-key-3" , "9876" , "TestFunction" , ScriptSecretsType . Function ) ;
303
+ }
304
+
305
+ string secretsJson = File . ReadAllText ( Path . Combine ( directory . Path , "testfunction.json" ) ) ;
306
+ var persistedSecrets = ScriptSecretSerializer . DeserializeSecrets < FunctionSecrets > ( secretsJson ) ;
307
+
308
+ Assert . Equal ( OperationResult . Created , result . Result ) ;
309
+ Assert . Equal ( result . Secret , "9876" ) ;
310
+
311
+ var logs = traceWriter . GetTraces ( ) ;
312
+ Assert . Equal ( 1 , logs . Count ( p => string . Equals ( p . Message , "Function keys change detected. Clearing cache for function 'TestFunction'." , StringComparison . OrdinalIgnoreCase ) ) ) ;
313
+ Assert . Equal ( 1 , logs . Count ( p => p . Message == "Function secret 'function-key-3' for 'TestFunction' Created." ) ) ;
314
+ }
315
+ }
316
+
317
+ [ Fact ]
318
+ public async Task AddOrUpdateFunctionSecret_ClearsCache_WhenHostLevelFunctionSecretAdded ( )
319
+ {
320
+ using ( var directory = new TempDirectory ( ) )
321
+ {
322
+ CreateTestSecrets ( directory . Path ) ;
323
+
324
+ Mock < IKeyValueConverterFactory > mockValueConverterFactory = GetConverterFactoryMock ( false ) ;
325
+ KeyOperationResult result ;
326
+ var traceWriter = new TestTraceWriter ( TraceLevel . Verbose ) ;
327
+ ISecretsRepository repository = new FileSystemSecretsRepository ( directory . Path ) ;
328
+ using ( var secretManager = new SecretManager ( repository , mockValueConverterFactory . Object , traceWriter ) )
329
+ {
330
+ var hostKeys = await secretManager . GetHostSecretsAsync ( ) ;
331
+ Assert . Equal ( 2 , hostKeys . FunctionKeys . Count ) ;
332
+
333
+ // add a new key
334
+ result = await secretManager . AddOrUpdateFunctionSecretAsync ( "function-host-3" , "9876" , HostKeyScopes . FunctionKeys , ScriptSecretsType . Host ) ;
335
+ }
336
+
337
+ string secretsJson = File . ReadAllText ( Path . Combine ( directory . Path , "host.json" ) ) ;
338
+ var persistedSecrets = ScriptSecretSerializer . DeserializeSecrets < HostSecrets > ( secretsJson ) ;
339
+
340
+ Assert . Equal ( OperationResult . Created , result . Result ) ;
341
+ Assert . Equal ( result . Secret , "9876" ) ;
342
+
343
+ var logs = traceWriter . GetTraces ( ) ;
344
+ Assert . Equal ( 1 , logs . Count ( p => p . Message == "Host keys change detected. Clearing cache." ) ) ;
345
+ Assert . Equal ( 1 , logs . Count ( p => p . Message == "Host secret 'function-host-3' for 'functionkeys' Created." ) ) ;
346
+ }
347
+ }
348
+
349
+ [ Fact ]
350
+ public async Task AddOrUpdateFunctionSecret_ClearsCache_WhenHostSystemSecretAdded ( )
351
+ {
352
+ using ( var directory = new TempDirectory ( ) )
353
+ {
354
+ CreateTestSecrets ( directory . Path ) ;
355
+
356
+ Mock < IKeyValueConverterFactory > mockValueConverterFactory = GetConverterFactoryMock ( false ) ;
357
+ KeyOperationResult result ;
358
+ var traceWriter = new TestTraceWriter ( TraceLevel . Verbose ) ;
359
+ ISecretsRepository repository = new FileSystemSecretsRepository ( directory . Path ) ;
360
+ using ( var secretManager = new SecretManager ( repository , mockValueConverterFactory . Object , traceWriter ) )
361
+ {
362
+ var hostKeys = await secretManager . GetHostSecretsAsync ( ) ;
363
+ Assert . Equal ( 2 , hostKeys . SystemKeys . Count ) ;
364
+
365
+ // add a new key
366
+ result = await secretManager . AddOrUpdateFunctionSecretAsync ( "host-system-3" , "123" , HostKeyScopes . SystemKeys , ScriptSecretsType . Host ) ;
367
+ }
368
+
369
+ string secretsJson = File . ReadAllText ( Path . Combine ( directory . Path , "host.json" ) ) ;
370
+ var persistedSecrets = ScriptSecretSerializer . DeserializeSecrets < HostSecrets > ( secretsJson ) ;
371
+
372
+ Assert . Equal ( OperationResult . Created , result . Result ) ;
373
+ Assert . Equal ( result . Secret , "123" ) ;
374
+
375
+ var logs = traceWriter . GetTraces ( ) ;
376
+ Assert . Equal ( 1 , logs . Count ( p => p . Message == "Host keys change detected. Clearing cache." ) ) ;
377
+ Assert . Equal ( 1 , logs . Count ( p => p . Message == "Host secret 'host-system-3' for 'systemkeys' Created." ) ) ;
378
+ }
379
+ }
380
+
381
+ private void CreateTestSecrets ( string path )
382
+ {
383
+ string hostSecrets =
384
+ @"{
385
+ 'masterKey': {
386
+ 'name': 'master',
387
+ 'value': '1234',
388
+ 'encrypted': false
389
+ },
390
+ 'functionKeys': [
391
+ {
392
+ 'name': 'function-host-1',
393
+ 'value': '456',
394
+ 'encrypted': false
395
+ },
396
+ {
397
+ 'name': 'function-host-2',
398
+ 'value': '789',
399
+ 'encrypted': false
400
+ }
401
+ ],
402
+ 'systemKeys': [
403
+ {
404
+ 'name': 'host-system-1',
405
+ 'value': '654',
406
+ 'encrypted': false
407
+ },
408
+ {
409
+ 'name': 'host-system-2',
410
+ 'value': '321',
411
+ 'encrypted': false
412
+ }
413
+ ]
414
+ }" ;
415
+ string functionSecrets =
416
+ @"{
417
+ 'keys': [
418
+ {
419
+ 'name': 'function-key-1',
420
+ 'value': '1234',
421
+ 'encrypted': false
422
+ },
423
+ {
424
+ 'name': 'function-key-2',
425
+ 'value': '5678',
426
+ 'encrypted': false
427
+ }
428
+ ]
429
+ }" ;
430
+ File . WriteAllText ( Path . Combine ( path , ScriptConstants . HostMetadataFileName ) , hostSecrets ) ;
431
+ File . WriteAllText ( Path . Combine ( path , "testfunction.json" ) , functionSecrets ) ;
432
+ }
433
+
285
434
[ Fact ]
286
435
public async Task AddOrUpdateFunctionSecrets_WithFunctionNameAndNoSecret_EncryptsSecretAndPersistsFile ( )
287
436
{
0 commit comments