Skip to content

Commit bf56d3d

Browse files
committed
Add test etc
1 parent 65ec29b commit bf56d3d

File tree

5 files changed

+115
-17
lines changed

5 files changed

+115
-17
lines changed

src/Microsoft.ComponentDetection.Detectors/nuget/NuGetComponentDetector.cs

+13-1
Original file line numberDiff line numberDiff line change
@@ -195,10 +195,22 @@ private async Task ParseNugetLockfileAsync(ProcessRequest processRequest)
195195
var singleFileComponentRecorder = processRequest.SingleFileComponentRecorder;
196196
var stream = processRequest.ComponentStream;
197197

198-
var lockfile = await JsonSerializer.DeserializeAsync<NugetLockfileShape>(stream.Stream);
198+
NuGetLockfileShape lockfile;
199+
try
200+
{
201+
lockfile = await JsonSerializer.DeserializeAsync<NuGetLockfileShape>(stream.Stream).ConfigureAwait(false);
202+
}
203+
catch (Exception e)
204+
{
205+
this.Logger.LogError(e, "Error loading NuGet lockfile from {Location}", stream.Location);
206+
singleFileComponentRecorder.RegisterPackageParseFailure(stream.Location);
207+
return;
208+
}
209+
199210
if (lockfile.Version != 1)
200211
{
201212
// only version 1 is supported
213+
this.Logger.LogError("Unsupported NuGet lockfile version {Version}", lockfile.Version);
202214
singleFileComponentRecorder.RegisterPackageParseFailure(stream.Location);
203215
return;
204216
}
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,22 @@
11
namespace Microsoft.ComponentDetection.Detectors.NuGet;
22

33
using System.Collections.Generic;
4+
using System.Text.Json.Serialization;
45

5-
internal class NugetLockfileShape
6+
internal record NuGetLockfileShape
67
{
8+
[JsonPropertyName("version")]
79
public int Version { get; set; }
810

9-
public Dictionary<string, Dictionary<string, PackageShape>> Dependencies { get; set; }
11+
[JsonPropertyName("dependencies")]
12+
public Dictionary<string, Dictionary<string, PackageShape>> Dependencies { get; set; } = new();
1013

11-
public class PackageShape
14+
public record PackageShape
1215
{
16+
[JsonPropertyName("type")]
1317
public string Type { get; set; }
1418

19+
[JsonPropertyName("resolved")]
1520
public string Resolved { get; set; }
1621
}
1722
}

test/Microsoft.ComponentDetection.Detectors.Tests/NuGetComponentDetectorTests.cs

+61-13
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
using Microsoft.ComponentDetection.Contracts.Internal;
1313
using Microsoft.ComponentDetection.Contracts.TypedComponent;
1414
using Microsoft.ComponentDetection.Detectors.NuGet;
15+
using Microsoft.ComponentDetection.Detectors.Tests.Utilities;
1516
using Microsoft.ComponentDetection.TestsUtilities;
1617
using Microsoft.Extensions.Logging;
1718
using Microsoft.VisualStudio.TestTools.UnitTesting;
@@ -23,14 +24,17 @@
2324
public class NuGetComponentDetectorTests : BaseDetectorTest<NuGetComponentDetector>
2425
{
2526
private static readonly IEnumerable<string> DetectorSearchPattern =
26-
new List<string> { "*.nupkg", "*.nuspec", "nuget.config", "paket.lock" };
27+
new List<string> { "*.nupkg", "*.nuspec", "nuget.config", "packages.lock.json", "paket.lock" };
2728

28-
private readonly Mock<ILogger<NuGetComponentDetector>> mockLogger;
29+
private ILogger<NuGetComponentDetector> logger;
2930

30-
public NuGetComponentDetectorTests()
31+
public TestContext TestContext { get; set; }
32+
33+
[TestInitialize]
34+
public void Setup()
3135
{
32-
this.mockLogger = new Mock<ILogger<NuGetComponentDetector>>();
33-
this.DetectorTestUtility.AddServiceMock(this.mockLogger);
36+
this.logger = new TestLogger<NuGetComponentDetector>(this.TestContext);
37+
this.DetectorTestUtility.AddService(this.logger);
3438
}
3539

3640
[TestMethod]
@@ -114,6 +118,57 @@ public async Task TestNugetDetector_ReturnsValidMixedComponentAsync()
114118
Assert.AreEqual(2, componentRecorder.GetDetectedComponents().Count());
115119
}
116120

121+
[TestMethod]
122+
public async Task TestNugetDetector_ReturnsPackagesLockfileAsync()
123+
{
124+
var lockfile = @"{
125+
""version"": 1,
126+
""dependencies"": {
127+
""net7.0"": {
128+
""Azure.Core"": {
129+
""type"": ""Direct"",
130+
""requested"": ""[1.25.0, )"",
131+
""resolved"": ""1.25.0"",
132+
""contentHash"": ""X8Dd4sAggS84KScWIjEbFAdt2U1KDolQopTPoHVubG2y3CM54f9l6asVrP5Uy384NWXjsspPYaJgz5xHc+KvTA=="",
133+
""dependencies"": {
134+
""Microsoft.Bcl.AsyncInterfaces"": ""1.1.1"",
135+
""System.Diagnostics.DiagnosticSource"": ""4.6.0"",
136+
""System.Memory.Data"": ""1.0.2"",
137+
""System.Numerics.Vectors"": ""4.5.0"",
138+
""System.Text.Encodings.Web"": ""4.7.2"",
139+
""System.Text.Json"": ""4.7.2"",
140+
""System.Threading.Tasks.Extensions"": ""4.5.4""
141+
}
142+
}
143+
},
144+
""net6.0"": {
145+
""Azure.Data.Tables"": {
146+
""type"": ""Direct"",
147+
""requested"": ""[12.5.0, )"",
148+
""resolved"": ""12.5.0"",
149+
""contentHash"": ""XeIxPf+rF1NXkX3NJSB0ZTNgU233vyPXGmaFsR0lUVibtWP/lj+Qu1FcPxoslURcX0KC+UgTb226nqVdHjoweQ=="",
150+
""dependencies"": {
151+
""Azure.Core"": ""1.22.0"",
152+
""System.Text.Json"": ""4.7.2""
153+
}
154+
}
155+
}
156+
}
157+
}";
158+
159+
var (scanResult, componentRecorder) = await this.DetectorTestUtility
160+
.WithFile("packages.lock.json", lockfile)
161+
.ExecuteDetectorAsync();
162+
163+
Assert.AreEqual(ProcessingResultCode.Success, scanResult.ResultCode);
164+
165+
// should be 2 components found; one per framework
166+
var components = new HashSet<string>(componentRecorder.GetDetectedComponents().Select(x => x.Component.Id));
167+
Assert.AreEqual(2, components.Count);
168+
Assert.IsTrue(components.Contains("Azure.Core 1.25.0 - NuGet"));
169+
Assert.IsTrue(components.Contains("Azure.Data.Tables 12.5.0 - NuGet"));
170+
}
171+
117172
[TestMethod]
118173
public async Task TestNugetDetector_ReturnsValidPaketComponentAsync()
119174
{
@@ -170,16 +225,9 @@ public async Task TestNugetDetector_HandlesMalformedComponentsInComponentListAsy
170225
.WithFile("test.nuspec", nuspec)
171226
.WithFile("test.nupkg", validNupkg)
172227
.WithFile("malformed.nupkg", malformedNupkg)
173-
.AddServiceMock(this.mockLogger)
228+
.AddService(this.logger)
174229
.ExecuteDetectorAsync();
175230

176-
this.mockLogger.Verify(x => x.Log(
177-
It.IsAny<LogLevel>(),
178-
It.IsAny<EventId>(),
179-
It.IsAny<It.IsAnyType>(),
180-
It.IsAny<Exception>(),
181-
(Func<It.IsAnyType, Exception, string>)It.IsAny<object>()));
182-
183231
Assert.AreEqual(ProcessingResultCode.Success, scanResult.ResultCode);
184232
Assert.AreEqual(2, componentRecorder.GetDetectedComponents().Count());
185233
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
namespace Microsoft.ComponentDetection.Detectors.Tests.Utilities;
2+
3+
using System;
4+
using Microsoft.Extensions.Logging;
5+
using Microsoft.VisualStudio.TestTools.UnitTesting;
6+
7+
internal class TestLogger<T> : ILogger<T>, IDisposable
8+
{
9+
private readonly TestContext context;
10+
11+
public TestLogger(TestContext context)
12+
=> this.context = context;
13+
14+
public IDisposable BeginScope<TState>(TState state)
15+
where TState : notnull
16+
=> this;
17+
18+
public void Dispose()
19+
{
20+
}
21+
22+
public bool IsEnabled(LogLevel logLevel) => true;
23+
24+
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func<TState, Exception, string> formatter)
25+
=> this.context.WriteLine($"{logLevel} ({eventId}): {formatter(state, exception)}");
26+
}

test/Microsoft.ComponentDetection.TestsUtilities/DetectorTestUtilityBuilder.cs

+7
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,13 @@ public DetectorTestUtilityBuilder()
4949
public DetectorTestUtilityBuilder<T> WithFile(string fileName, string fileContents, IEnumerable<string> searchPatterns = null, string fileLocation = null) =>
5050
this.WithFile(fileName, fileContents.ToStream(), searchPatterns, fileLocation);
5151

52+
public DetectorTestUtilityBuilder<T> AddService<TService>(TService it)
53+
where TService : class
54+
{
55+
this.serviceCollection.AddSingleton(it);
56+
return this;
57+
}
58+
5259
public DetectorTestUtilityBuilder<T> AddServiceMock<TMock>(Mock<TMock> mock)
5360
where TMock : class
5461
{

0 commit comments

Comments
 (0)