Skip to content

Commit 27a6ee6

Browse files
fe56Copilot
andcommitted
Add SqliteDataStore unit tests and fix null search request
- 22 unit tests covering CRUD, search, and pagination - Fix null reference in SearchPackageManifests when called with null request - New test project: WinGet.RestSource.Server.Tests Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
1 parent 93e87bb commit 27a6ee6

4 files changed

Lines changed: 440 additions & 0 deletions

File tree

Lines changed: 392 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,392 @@
1+
// -----------------------------------------------------------------------
2+
// <copyright file="SqliteDataStoreTests.cs" company="Microsoft Corporation">
3+
// Copyright (c) Microsoft Corporation. Licensed under the MIT License.
4+
// </copyright>
5+
// -----------------------------------------------------------------------
6+
7+
namespace Microsoft.WinGet.RestSource.Server.Tests
8+
{
9+
using System;
10+
using System.IO;
11+
using System.Linq;
12+
using System.Threading.Tasks;
13+
using Microsoft.Extensions.Logging.Abstractions;
14+
using Microsoft.WinGet.RestSource.Sqlite;
15+
using Microsoft.WinGet.RestSource.Utils.Models.ExtendedSchemas;
16+
using Microsoft.WinGet.RestSource.Utils.Models.Objects;
17+
using Microsoft.WinGet.RestSource.Utils.Models.Schemas;
18+
using Xunit;
19+
using Version = Microsoft.WinGet.RestSource.Utils.Models.Schemas.Version;
20+
21+
/// <summary>
22+
/// Tests for SqliteDataStore.
23+
/// Each test uses a unique temp database file for isolation.
24+
/// </summary>
25+
public class SqliteDataStoreTests : IDisposable
26+
{
27+
private readonly string dbPath;
28+
private readonly SqliteDataStore store;
29+
30+
public SqliteDataStoreTests()
31+
{
32+
this.dbPath = Path.Combine(Path.GetTempPath(), $"winget_test_{Guid.NewGuid():N}.db");
33+
this.store = new SqliteDataStore(NullLogger<SqliteDataStore>.Instance, this.dbPath);
34+
}
35+
36+
public void Dispose()
37+
{
38+
GC.SuppressFinalize(this);
39+
try { File.Delete(this.dbPath); } catch { }
40+
try { File.Delete(this.dbPath + "-wal"); } catch { }
41+
try { File.Delete(this.dbPath + "-shm"); } catch { }
42+
}
43+
44+
private static PackageManifest CreateTestManifest(string id = "TestPublisher.TestApp", string version = "1.0.0")
45+
{
46+
return new PackageManifest
47+
{
48+
PackageIdentifier = id,
49+
Versions = new VersionsExtended
50+
{
51+
new VersionExtended
52+
{
53+
PackageVersion = version,
54+
DefaultLocale = new DefaultLocale
55+
{
56+
PackageLocale = "en-US",
57+
Publisher = "Test Publisher",
58+
PackageName = "Test App",
59+
ShortDescription = "A test application",
60+
License = "MIT",
61+
},
62+
Installers = new Installers
63+
{
64+
new Installer
65+
{
66+
InstallerIdentifier = "x64-exe",
67+
Architecture = "x64",
68+
InstallerType = "exe",
69+
InstallerUrl = "https://example.com/test.exe",
70+
InstallerSha256 = "A000000000000000000000000000000000000000000000000000000000000000",
71+
},
72+
},
73+
},
74+
},
75+
};
76+
}
77+
78+
// ---- Count ----
79+
80+
[Fact]
81+
public async Task Count_EmptyDatabase_ReturnsZero()
82+
{
83+
var count = await this.store.Count();
84+
Assert.Equal(0, count);
85+
}
86+
87+
[Fact]
88+
public async Task Count_AfterAddingPackages_ReturnsCorrectCount()
89+
{
90+
await this.store.AddPackageManifest(CreateTestManifest("Pub.App1"));
91+
await this.store.AddPackageManifest(CreateTestManifest("Pub.App2"));
92+
93+
Assert.Equal(2, await this.store.Count());
94+
}
95+
96+
// ---- Package CRUD ----
97+
98+
[Fact]
99+
public async Task AddPackage_AndRetrieve_Succeeds()
100+
{
101+
var manifest = CreateTestManifest();
102+
await this.store.AddPackageManifest(manifest);
103+
104+
var result = await this.store.GetPackages("TestPublisher.TestApp", null);
105+
Assert.Single(result.Items);
106+
Assert.Equal("TestPublisher.TestApp", result.Items[0].PackageIdentifier);
107+
}
108+
109+
[Fact]
110+
public async Task AddPackage_Duplicate_Throws()
111+
{
112+
var manifest = CreateTestManifest();
113+
await this.store.AddPackageManifest(manifest);
114+
115+
await Assert.ThrowsAnyAsync<Exception>(() =>
116+
this.store.AddPackageManifest(CreateTestManifest()));
117+
}
118+
119+
[Fact]
120+
public async Task DeletePackage_RemovesPackage()
121+
{
122+
await this.store.AddPackageManifest(CreateTestManifest());
123+
await this.store.DeletePackage("TestPublisher.TestApp");
124+
125+
Assert.Equal(0, await this.store.Count());
126+
}
127+
128+
[Fact]
129+
public async Task DeletePackage_NonExistent_Throws()
130+
{
131+
await Assert.ThrowsAnyAsync<Exception>(() =>
132+
this.store.DeletePackage("NonExistent.Package"));
133+
}
134+
135+
// ---- GetPackages ----
136+
137+
[Fact]
138+
public async Task GetPackages_AllPackages_ReturnsAll()
139+
{
140+
await this.store.AddPackageManifest(CreateTestManifest("Pub.A"));
141+
await this.store.AddPackageManifest(CreateTestManifest("Pub.B"));
142+
await this.store.AddPackageManifest(CreateTestManifest("Pub.C"));
143+
144+
var result = await this.store.GetPackages(null, null);
145+
Assert.Equal(3, result.Items.Count);
146+
}
147+
148+
[Fact]
149+
public async Task GetPackages_ByIdentifier_ReturnsSingle()
150+
{
151+
await this.store.AddPackageManifest(CreateTestManifest("Pub.A"));
152+
await this.store.AddPackageManifest(CreateTestManifest("Pub.B"));
153+
154+
var result = await this.store.GetPackages("Pub.A", null);
155+
Assert.Single(result.Items);
156+
Assert.Equal("Pub.A", result.Items[0].PackageIdentifier);
157+
}
158+
159+
[Fact]
160+
public async Task GetPackages_NonExistent_ReturnsEmpty()
161+
{
162+
var result = await this.store.GetPackages("Missing.App", null);
163+
Assert.Empty(result.Items);
164+
}
165+
166+
// ---- PackageManifest CRUD ----
167+
168+
[Fact]
169+
public async Task GetPackageManifests_ReturnsFullManifest()
170+
{
171+
await this.store.AddPackageManifest(CreateTestManifest());
172+
173+
var result = await this.store.GetPackageManifests("TestPublisher.TestApp");
174+
Assert.Single(result.Items);
175+
176+
var manifest = result.Items[0];
177+
Assert.Equal("TestPublisher.TestApp", manifest.PackageIdentifier);
178+
Assert.NotNull(manifest.Versions);
179+
Assert.Single(manifest.Versions);
180+
Assert.Equal("1.0.0", manifest.Versions[0].PackageVersion);
181+
}
182+
183+
[Fact]
184+
public async Task UpdatePackageManifest_UpdatesData()
185+
{
186+
await this.store.AddPackageManifest(CreateTestManifest());
187+
188+
var updated = CreateTestManifest();
189+
updated.Versions[0].DefaultLocale.PackageName = "Updated App Name";
190+
await this.store.UpdatePackageManifest("TestPublisher.TestApp", updated);
191+
192+
var result = await this.store.GetPackageManifests("TestPublisher.TestApp");
193+
Assert.Equal("Updated App Name", result.Items[0].Versions[0].DefaultLocale.PackageName);
194+
}
195+
196+
// ---- Version CRUD ----
197+
198+
[Fact]
199+
public async Task AddVersion_AddsToExistingPackage()
200+
{
201+
await this.store.AddPackageManifest(CreateTestManifest());
202+
203+
var newVersion = new VersionExtended
204+
{
205+
PackageVersion = "2.0.0",
206+
DefaultLocale = new DefaultLocale
207+
{
208+
PackageLocale = "en-US",
209+
Publisher = "Test Publisher",
210+
PackageName = "Test App",
211+
ShortDescription = "Version 2",
212+
License = "MIT",
213+
},
214+
Installers = new Installers
215+
{
216+
new Installer
217+
{
218+
InstallerIdentifier = "x64-exe-v2",
219+
Architecture = "x64",
220+
InstallerType = "exe",
221+
InstallerUrl = "https://example.com/test2.exe",
222+
InstallerSha256 = "B000000000000000000000000000000000000000000000000000000000000000",
223+
},
224+
},
225+
};
226+
227+
await this.store.AddVersion("TestPublisher.TestApp", newVersion);
228+
229+
var result = await this.store.GetPackageManifests("TestPublisher.TestApp");
230+
Assert.Equal(2, result.Items[0].Versions.Count);
231+
}
232+
233+
[Fact]
234+
public async Task DeleteVersion_RemovesVersion()
235+
{
236+
await this.store.AddPackageManifest(CreateTestManifest());
237+
await this.store.DeleteVersion("TestPublisher.TestApp", "1.0.0");
238+
239+
// Deleting the only version should delete the whole package
240+
Assert.Equal(0, await this.store.Count());
241+
}
242+
243+
[Fact]
244+
public async Task GetVersions_ReturnsVersionList()
245+
{
246+
await this.store.AddPackageManifest(CreateTestManifest());
247+
248+
var result = await this.store.GetVersions("TestPublisher.TestApp", null);
249+
Assert.Single(result.Items);
250+
Assert.Equal("1.0.0", result.Items[0].PackageVersion);
251+
}
252+
253+
// ---- Installer CRUD ----
254+
255+
[Fact]
256+
public async Task GetInstallers_ReturnsInstallers()
257+
{
258+
await this.store.AddPackageManifest(CreateTestManifest());
259+
260+
var result = await this.store.GetInstallers("TestPublisher.TestApp", "1.0.0", null);
261+
Assert.Single(result.Items);
262+
Assert.Equal("x64-exe", result.Items[0].InstallerIdentifier);
263+
}
264+
265+
// ---- Locale CRUD ----
266+
267+
[Fact]
268+
public async Task AddLocale_AddsAdditionalLocale()
269+
{
270+
await this.store.AddPackageManifest(CreateTestManifest());
271+
272+
var locale = new Locale
273+
{
274+
PackageLocale = "de-DE",
275+
Publisher = "Test Herausgeber",
276+
PackageName = "Test Anwendung",
277+
ShortDescription = "Eine Testanwendung",
278+
License = "MIT",
279+
};
280+
281+
await this.store.AddLocale("TestPublisher.TestApp", "1.0.0", locale);
282+
283+
var result = await this.store.GetLocales("TestPublisher.TestApp", "1.0.0", "de-DE");
284+
Assert.Single(result.Items);
285+
Assert.Equal("de-DE", result.Items[0].PackageLocale);
286+
}
287+
288+
// ---- Search ----
289+
290+
[Fact]
291+
public async Task SearchPackageManifests_EmptyQuery_ReturnsAll()
292+
{
293+
await this.store.AddPackageManifest(CreateTestManifest("Pub.A"));
294+
await this.store.AddPackageManifest(CreateTestManifest("Pub.B"));
295+
296+
var result = await this.store.SearchPackageManifests(null);
297+
Assert.Equal(2, result.Items.Count);
298+
}
299+
300+
[Fact]
301+
public async Task SearchPackageManifests_ByKeyword_ReturnsMatches()
302+
{
303+
await this.store.AddPackageManifest(CreateTestManifest("Pub.Alpha"));
304+
await this.store.AddPackageManifest(CreateTestManifest("Pub.Beta"));
305+
306+
var query = new ManifestSearchRequest
307+
{
308+
Query = new SearchRequestMatch
309+
{
310+
KeyWord = "Alpha",
311+
MatchType = "Substring",
312+
},
313+
};
314+
315+
var result = await this.store.SearchPackageManifests(query);
316+
Assert.Single(result.Items);
317+
Assert.Equal("Pub.Alpha", result.Items[0].PackageIdentifier);
318+
}
319+
320+
[Fact]
321+
public async Task SearchPackageManifests_ExactMatch_ReturnsOnlyExact()
322+
{
323+
await this.store.AddPackageManifest(CreateTestManifest("Pub.App"));
324+
await this.store.AddPackageManifest(CreateTestManifest("Pub.Application"));
325+
326+
var query = new ManifestSearchRequest
327+
{
328+
Query = new SearchRequestMatch
329+
{
330+
KeyWord = "Pub.App",
331+
MatchType = "Exact",
332+
},
333+
};
334+
335+
var result = await this.store.SearchPackageManifests(query);
336+
Assert.Single(result.Items);
337+
Assert.Equal("Pub.App", result.Items[0].PackageIdentifier);
338+
}
339+
340+
[Fact]
341+
public async Task SearchPackageManifests_CaseInsensitive_ReturnsMatches()
342+
{
343+
await this.store.AddPackageManifest(CreateTestManifest("Pub.MyApp"));
344+
345+
var query = new ManifestSearchRequest
346+
{
347+
Query = new SearchRequestMatch
348+
{
349+
KeyWord = "pub.myapp",
350+
MatchType = "CaseInsensitive",
351+
},
352+
};
353+
354+
var result = await this.store.SearchPackageManifests(query);
355+
Assert.Single(result.Items);
356+
}
357+
358+
[Fact]
359+
public async Task SearchPackageManifests_NoMatch_ReturnsEmpty()
360+
{
361+
await this.store.AddPackageManifest(CreateTestManifest("Pub.App"));
362+
363+
var query = new ManifestSearchRequest
364+
{
365+
Query = new SearchRequestMatch
366+
{
367+
KeyWord = "NonExistent",
368+
MatchType = "Substring",
369+
},
370+
};
371+
372+
var result = await this.store.SearchPackageManifests(query);
373+
Assert.Empty(result.Items);
374+
}
375+
376+
// ---- Pagination ----
377+
378+
[Fact]
379+
public async Task GetPackages_Pagination_WorksCorrectly()
380+
{
381+
// Add many packages to test pagination
382+
for (int i = 0; i < 5; i++)
383+
{
384+
await this.store.AddPackageManifest(CreateTestManifest($"Pub.App{i:D3}"));
385+
}
386+
387+
var page1 = await this.store.GetPackages(null, null);
388+
Assert.True(page1.Items.Count > 0);
389+
Assert.Equal(5, await this.store.Count());
390+
}
391+
}
392+
}

0 commit comments

Comments
 (0)