Skip to content

Commit 501f5b8

Browse files
kshyjuCopilot
andauthored
Handles loading extensions.json with empty extensions (#11174)
* Handle empty extensions array usecase. * release notes. * Liniting - remove empty line. * Fixed typo in test comment. * Update test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs Co-authored-by: Copilot <[email protected]> --------- Co-authored-by: Copilot <[email protected]>
1 parent a391304 commit 501f5b8

File tree

3 files changed

+59
-1
lines changed

3 files changed

+59
-1
lines changed

release_notes.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,3 +7,4 @@
77
- Add JitTrace Files for v4.1041
88
- Fix startup deadlock on transient exceptions (#11142)
99
- Add warning log for end of support bundle version, any bundle version < 4 (#11075), (#11160)
10+
- Handles loading extensions.json with empty extensions(#11174)

src/WebJobs.Script/DependencyInjection/ScriptStartupTypeLocator.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,7 @@ private async Task<ExtensionReference[]> ParseExtensionsAsync(string metadataFil
235235
await using var stream = File.OpenRead(metadataFilePath);
236236
var extensionReferences = await JsonSerializer.DeserializeAsync(stream, ExtensionReferencesJsonContext.Default.ExtensionReferences);
237237

238-
if (extensionReferences?.Extensions == null || extensionReferences.Extensions.Length == 0)
238+
if (extensionReferences?.Extensions == null)
239239
{
240240
_logger.ScriptStartUpUnableParseMetadataMissingProperty(metadataFilePath);
241241
return [];

test/WebJobs.Script.Tests/ScriptStartupTypeDiscovererTests.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredBundleVersions(string
182182
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading extension bundle")));
183183
}
184184
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
185+
AssertNoErrors(traces);
185186
}
186187
}
187188

@@ -310,6 +311,7 @@ public async Task GetExtensionsStartupTypes_AcceptsRequiredExtensionVersions(str
310311
{
311312
Assert.True(traces.Any(m => m.FormattedMessage.Contains($"Loading startup extension 'Storage")));
312313
}
314+
AssertNoErrors(traces);
313315
}
314316
}
315317

@@ -423,6 +425,7 @@ void CopyToBin(string path)
423425
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
424426
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
425427
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
428+
AssertNoErrors(traces);
426429
}
427430
}
428431

@@ -516,6 +519,7 @@ void CopyToBin(string path)
516519
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
517520
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[0].TypeName}' belongs to a builtin extension")));
518521
Assert.True(traces.Any(m => string.Equals(m.FormattedMessage, $"The extension startup type '{references[1].TypeName}' belongs to a builtin extension")));
522+
AssertNoErrors(traces);
519523
}
520524
}
521525

@@ -592,11 +596,13 @@ void CopyToBin(string path)
592596

593597
// Act
594598
var types = await discoverer.GetExtensionsStartupTypesAsync();
599+
var traces = testLoggerProvider.GetAllLogMessages();
595600

596601
// Assert
597602
AreExpectedMetricsGenerated(testMetricsLogger);
598603
Assert.Equal(types.Count(), 2);
599604
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.FirstOrDefault().FullName);
605+
AssertNoErrors(traces);
600606
}
601607
}
602608

@@ -644,11 +650,13 @@ void CopyToBin(string path)
644650

645651
// Act
646652
var types = await discoverer.GetExtensionsStartupTypesAsync();
653+
var traces = testLoggerProvider.GetAllLogMessages();
647654

648655
// Assert
649656
AreExpectedMetricsGenerated(testMetricsLogger);
650657
Assert.Single(types);
651658
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
659+
AssertNoErrors(traces);
652660
}
653661
}
654662

@@ -698,11 +706,13 @@ void CopyToBin(string path)
698706

699707
// Act
700708
var types = await discoverer.GetExtensionsStartupTypesAsync();
709+
var traces = testLoggerProvider.GetAllLogMessages();
701710

702711
//Assert
703712
AreExpectedMetricsGenerated(testMetricsLogger);
704713
Assert.Single(types);
705714
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.Single().FullName);
715+
AssertNoErrors(traces);
706716
}
707717
}
708718

@@ -1144,13 +1154,55 @@ void CopyToBin(string path)
11441154

11451155
// Act
11461156
var types = await discoverer.GetExtensionsStartupTypesAsync();
1157+
var traces = testLoggerProvider.GetAllLogMessages();
11471158
Environment.SetEnvironmentVariable(EnvironmentSettingNames.AzureWebJobsFeatureFlags, null);
11481159
Environment.SetEnvironmentVariable(EnvironmentSettingNames.FunctionWorkerRuntime, null);
11491160

11501161
//Assert that filtering did not take place because of worker indexing
11511162
Assert.True(types.Count() == 1);
11521163
Assert.Equal(typeof(AzureStorageWebJobsStartup).FullName, types.ElementAt(0).FullName);
1164+
AssertNoErrors(traces);
1165+
}
1166+
}
1167+
1168+
[Fact]
1169+
public async Task GetExtensionsStartupTypes_EmptyExtensionsArray()
1170+
{
1171+
TestMetricsLogger testMetricsLogger = new TestMetricsLogger();
1172+
1173+
using var directory = new TempDirectory();
1174+
var binPath = Path.Combine(directory.Path, "bin");
1175+
Directory.CreateDirectory(binPath);
1176+
1177+
// extensions.json file with an empty extensions array (simulating extensions.json produced by in-proc app)
1178+
string extensionJson = """
1179+
{
1180+
"extensions": []
11531181
}
1182+
""";
1183+
File.WriteAllText(Path.Combine(binPath, "extensions.json"), extensionJson);
1184+
1185+
TestLoggerProvider testLoggerProvider = new TestLoggerProvider();
1186+
LoggerFactory factory = new LoggerFactory();
1187+
factory.AddProvider(testLoggerProvider);
1188+
var testLogger = factory.CreateLogger<ScriptStartupTypeLocator>();
1189+
1190+
var mockExtensionBundleManager = new Mock<IExtensionBundleManager>();
1191+
mockExtensionBundleManager.Setup(e => e.IsExtensionBundleConfigured()).Returns(true);
1192+
mockExtensionBundleManager.Setup(e => e.GetExtensionBundleDetails()).Returns(Task.FromResult(new ExtensionBundleDetails() { Id = "bundleID", Version = "1.0.0" }));
1193+
mockExtensionBundleManager.Setup(e => e.GetExtensionBundleBinPathAsync()).Returns(Task.FromResult(binPath));
1194+
1195+
var languageWorkerOptions = new TestOptionsMonitor<LanguageWorkerOptions>(new LanguageWorkerOptions());
1196+
var mockFunctionMetadataManager = GetTestFunctionMetadataManager(languageWorkerOptions);
1197+
OptionsWrapper<ExtensionRequirementOptions> optionsWrapper = new(new ExtensionRequirementOptions());
1198+
var discoverer = new ScriptStartupTypeLocator(directory.Path, testLogger, mockExtensionBundleManager.Object, mockFunctionMetadataManager, testMetricsLogger, optionsWrapper);
1199+
1200+
var types = await discoverer.GetExtensionsStartupTypesAsync();
1201+
var traces = testLoggerProvider.GetAllLogMessages();
1202+
1203+
AreExpectedMetricsGenerated(testMetricsLogger);
1204+
Assert.Empty(types); // Ensure no types are loaded because the extensions array is empty
1205+
AssertNoErrors(traces);
11541206
}
11551207

11561208
private IFunctionMetadataManager GetTestFunctionMetadataManager(IOptionsMonitor<LanguageWorkerOptions> options, ICollection<FunctionMetadata> metadataCollection = null, bool hasPrecompiledFunction = false, bool hasNodeFunctions = false, bool hasDotnetIsolatedFunctions = false)
@@ -1234,5 +1286,10 @@ private ILogger<ScriptStartupTypeLocator> GetTestLogger()
12341286
var testLogger = factory.CreateLogger<ScriptStartupTypeLocator>();
12351287
return testLogger;
12361288
}
1289+
1290+
private static void AssertNoErrors(IList<LogMessage> traces)
1291+
{
1292+
Assert.False(traces.Any(m => m.Level == LogLevel.Error || m.Level == LogLevel.Critical));
1293+
}
12371294
}
12381295
}

0 commit comments

Comments
 (0)