SQLiteIndex.cpp (162789B)
1 // Copyright (c) Microsoft Corporation. 2 // Licensed under the MIT License. 3 #include "pch.h" 4 #include "TestCommon.h" 5 #include <winget/SQLiteWrapper.h> 6 #include <PackageDependenciesValidation.h> 7 #include <ArpVersionValidation.h> 8 #include <Microsoft/SQLiteIndex.h> 9 #include <winget/Manifest.h> 10 #include <AppInstallerStrings.h> 11 #include <winget/SQLiteMetadataTable.h> 12 #include <winget/PackageVersionDataManifest.h> 13 14 #include <Microsoft/Schema/1_0/IdTable.h> 15 #include <Microsoft/Schema/1_0/NameTable.h> 16 #include <Microsoft/Schema/1_0/MonikerTable.h> 17 #include <Microsoft/Schema/1_0/VersionTable.h> 18 #include <Microsoft/Schema/1_0/ChannelTable.h> 19 #include <Microsoft/Schema/1_0/PathPartTable.h> 20 #include <Microsoft/Schema/1_0/ManifestTable.h> 21 #include <Microsoft/Schema/1_0/TagsTable.h> 22 #include <Microsoft/Schema/1_0/CommandsTable.h> 23 #include <Microsoft/Schema/1_0/SearchResultsTable.h> 24 #include <Microsoft/Schema/1_4/DependenciesTable.h> 25 #include <Microsoft/Schema/2_0/Interface.h> 26 #include <Microsoft/Schema/2_0/PackageUpdateTrackingTable.h> 27 28 using namespace std::string_literals; 29 using namespace std::string_view_literals; 30 using namespace TestCommon; 31 using namespace AppInstaller::Manifest; 32 using namespace AppInstaller::Repository; 33 using namespace AppInstaller::Repository::Microsoft; 34 using namespace AppInstaller::SQLite; 35 using namespace AppInstaller::Utility; 36 37 using UtilityVersion = AppInstaller::Utility::Version; 38 using SQLiteVersion = AppInstaller::SQLite::Version; 39 40 SQLiteIndex CreateTestIndex(const std::string& filePath, std::optional<SQLiteVersion> version = {}) 41 { 42 // If no specific version requested, then use generator to run against the last 3 versions. 43 if (!version) 44 { 45 SQLiteVersion latestVersion{ 2, 0 }; 46 SQLiteVersion versionMinus1 = SQLiteVersion{ 1, 7 }; 47 SQLiteVersion versionMinus2 = SQLiteVersion{ 1, 6 }; 48 49 version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }, SQLiteVersion{ latestVersion }); 50 } 51 52 return SQLiteIndex::CreateNew(filePath, version.value()); 53 } 54 55 SQLiteVersion TestPrepareForRead(SQLiteIndex& index) 56 { 57 SQLiteVersion latestVersion{ 2, 0 }; 58 SQLiteVersion versionMinus1 = SQLiteVersion{ 1, 7 }; 59 SQLiteVersion versionMinus2 = SQLiteVersion{ 1, 6 }; 60 61 index.PrepareForPackaging(); 62 63 if (index.GetVersion() == versionMinus2) 64 { 65 // Degenerate case where we don't need to do anything 66 } 67 else if (index.GetVersion() == versionMinus1) 68 { 69 SQLiteVersion version = GENERATE_COPY(SQLiteVersion{ versionMinus2 }, SQLiteVersion{ versionMinus1 }); 70 71 if (version != versionMinus1) 72 { 73 index.ForceVersion(version); 74 return version; 75 } 76 } 77 else if (index.GetVersion() == latestVersion) 78 { 79 // This crosses major versions, so leave it at 2.0 always 80 } 81 82 return index.GetVersion(); 83 } 84 85 std::string GetPathFromManifest(Manifest& manifest) 86 { 87 auto publisher = manifest.Id; 88 AppInstaller::Utility::FindAndReplace(publisher, ".", "/"); 89 90 return AppInstaller::Utility::ToLower(publisher).append("/").append(manifest.Version); 91 } 92 93 void CreateFakeManifest(Manifest& manifest, string_t publisher, string_t version = "1.0.0") 94 { 95 manifest.Installers.push_back({}); 96 manifest.Id = publisher.append(".").append("Id"); 97 manifest.DefaultLocalization.Add<Localization::PackageName>(publisher.append(" Name")); 98 manifest.Moniker = "testmoniker"; 99 manifest.Version = version; 100 manifest.Channel = "test"; 101 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 102 manifest.Installers[0].Commands = { "test1", "test2" }; 103 } 104 105 SQLiteIndex SimpleTestSetup(const std::string& filePath, Manifest& manifest, std::optional<SQLiteVersion> version = {}) 106 { 107 SQLiteIndex index = CreateTestIndex(filePath, version); 108 109 string_t publisher = "Test"; 110 CreateFakeManifest(manifest, publisher); 111 112 auto relativePath = GetPathFromManifest(manifest); 113 114 index.AddManifest(manifest, relativePath); 115 116 return index; 117 } 118 119 struct IndexFields 120 { 121 IndexFields( 122 std::string id, 123 std::string name, 124 std::string moniker, 125 std::string version, 126 std::string channel, 127 std::vector<NormalizedString> tags, 128 std::vector<NormalizedString> commands, 129 std::string path 130 ) : 131 Id(std::move(id)), 132 Name(std::move(name)), 133 Moniker(std::move(moniker)), 134 Version(std::move(version)), 135 Channel(std::move(channel)), 136 Tags(std::move(tags)), 137 Commands(std::move(commands)), 138 Path(std::move(path)) 139 {} 140 141 IndexFields( 142 std::string id, 143 std::string name, 144 std::string moniker, 145 std::string version, 146 std::string channel, 147 std::vector<NormalizedString> tags, 148 std::vector<NormalizedString> commands, 149 std::string path, 150 std::vector<NormalizedString> packageFamilyNames, 151 std::vector<NormalizedString> productCodes 152 ) : 153 Id(std::move(id)), 154 Name(std::move(name)), 155 Moniker(std::move(moniker)), 156 Version(std::move(version)), 157 Channel(std::move(channel)), 158 Tags(std::move(tags)), 159 Commands(std::move(commands)), 160 Path(std::move(path)), 161 PackageFamilyNames(std::move(packageFamilyNames)), 162 ProductCodes(std::move(productCodes)) 163 {} 164 165 IndexFields( 166 std::string id, 167 std::string name, 168 std::string publisher, 169 std::string moniker, 170 std::string version, 171 std::string channel, 172 std::vector<NormalizedString> tags, 173 std::vector<NormalizedString> commands, 174 std::string path, 175 std::vector<NormalizedString> packageFamilyNames, 176 std::vector<NormalizedString> productCodes 177 ) : 178 Id(std::move(id)), 179 Name(std::move(name)), 180 Publisher(std::move(publisher)), 181 Moniker(std::move(moniker)), 182 Version(std::move(version)), 183 Channel(std::move(channel)), 184 Tags(std::move(tags)), 185 Commands(std::move(commands)), 186 Path(std::move(path)), 187 PackageFamilyNames(std::move(packageFamilyNames)), 188 ProductCodes(std::move(productCodes)) 189 {} 190 191 IndexFields( 192 std::string id, 193 std::string name, 194 std::string publisher, 195 std::string moniker, 196 std::string version, 197 std::string channel, 198 std::vector<NormalizedString> tags, 199 std::vector<NormalizedString> commands, 200 std::string path, 201 std::vector<NormalizedString> packageFamilyNames, 202 std::vector<NormalizedString> productCodes, 203 std::string arpName, 204 std::string arpPublisher 205 ) : 206 Id(std::move(id)), 207 Name(std::move(name)), 208 Publisher(std::move(publisher)), 209 Moniker(std::move(moniker)), 210 Version(std::move(version)), 211 Channel(std::move(channel)), 212 Tags(std::move(tags)), 213 Commands(std::move(commands)), 214 Path(std::move(path)), 215 PackageFamilyNames(std::move(packageFamilyNames)), 216 ProductCodes(std::move(productCodes)), 217 ArpName(std::move(arpName)), 218 ArpPublisher(std::move(arpPublisher)) 219 {} 220 221 std::string Id; 222 std::string Name; 223 std::string Publisher; 224 std::string Moniker; 225 std::string Version; 226 std::string Channel; 227 std::vector<NormalizedString> Tags; 228 std::vector<NormalizedString> Commands; 229 std::string Path; 230 std::vector<NormalizedString> PackageFamilyNames; 231 std::vector<NormalizedString> ProductCodes; 232 std::string ArpName; 233 std::string ArpPublisher; 234 }; 235 236 SQLiteIndex SearchTestSetup(const std::string& filePath, std::initializer_list<IndexFields> data = {}, std::optional<SQLiteVersion> version = {}) 237 { 238 SQLiteIndex index = CreateTestIndex(filePath, version); 239 240 Manifest manifest; 241 242 auto addFunc = [&](const IndexFields& d) 243 { 244 manifest.Id = d.Id; 245 manifest.DefaultLocalization.Add<Localization::PackageName>(d.Name); 246 manifest.DefaultLocalization.Add<Localization::Publisher>(d.Publisher); 247 manifest.Moniker = d.Moniker; 248 manifest.Version = d.Version; 249 manifest.DefaultLocalization.Add<Localization::Tags>(d.Tags); 250 251 manifest.Installers.resize(std::max(d.PackageFamilyNames.size(), d.ProductCodes.size())); 252 253 if (manifest.Installers.size() == 0) 254 { 255 manifest.Installers.push_back({}); 256 } 257 258 manifest.Channel = d.Channel; 259 manifest.Installers[0].Commands = d.Commands; 260 261 for (size_t i = 0; i < d.PackageFamilyNames.size(); ++i) 262 { 263 manifest.Installers[i].PackageFamilyName = d.PackageFamilyNames[i]; 264 } 265 266 for (size_t i = 0; i < d.ProductCodes.size(); ++i) 267 { 268 manifest.Installers[i].ProductCode = d.ProductCodes[i]; 269 } 270 271 if (!d.ArpName.empty() || !d.ArpPublisher.empty()) 272 { 273 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 274 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayName = d.ArpName; 275 manifest.Installers[0].AppsAndFeaturesEntries[0].Publisher = d.ArpPublisher; 276 } 277 278 index.AddManifest(manifest, d.Path); 279 }; 280 281 for (const auto& d : data) 282 { 283 addFunc(d); 284 } 285 286 return index; 287 } 288 289 bool ArePackageFamilyNameAndProductCodeSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 290 { 291 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 292 return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); 293 } 294 295 bool AreNormalizedNameAndPublisherSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 296 { 297 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 298 return (index.GetVersion() >= SQLiteVersion{ 1, 2 } && testVersion >= SQLiteVersion{ 1, 2 }); 299 } 300 301 bool IsManifestMetadataSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 302 { 303 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 304 return (index.GetVersion() >= SQLiteVersion{ 1, 1 } && testVersion >= SQLiteVersion{ 1, 1 }); 305 } 306 307 bool AreManifestHashesSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 308 { 309 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 310 return (index.GetVersion() >= SQLiteVersion{ 1, 3 } && testVersion >= SQLiteVersion{ 1, 3 } && index.GetVersion() < SQLiteVersion{ 2, 0 }); 311 } 312 313 bool AreArpVersionsSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 314 { 315 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 316 return (index.GetVersion() >= SQLiteVersion{ 1, 5 } && testVersion >= SQLiteVersion{ 1, 5 }); 317 } 318 319 bool AreArpVersionsNullable(const SQLiteIndex& index) 320 { 321 UNSCOPED_INFO("Index " << index.GetVersion()); 322 return (index.GetVersion() >= SQLiteVersion{ 2, 0 }); 323 } 324 325 bool IsMapDataFoldingSupported(const SQLiteIndex& index, const SQLiteVersion& testVersion) 326 { 327 UNSCOPED_INFO("Index " << index.GetVersion() << " | Test " << testVersion); 328 return (index.GetVersion() >= SQLiteVersion{ 1, 7 } && testVersion >= SQLiteVersion{ 1, 7 }); 329 } 330 331 bool IsMapDataFolded(const SQLiteIndex& index) 332 { 333 UNSCOPED_INFO("Index " << index.GetVersion()); 334 return (index.GetVersion() >= SQLiteVersion{ 1, 7 }); 335 } 336 337 bool AreVersionKeysSupported(const SQLiteIndex& index) 338 { 339 UNSCOPED_INFO("Index " << index.GetVersion()); 340 return (index.GetVersion() < SQLiteVersion{ 2, 0 }); 341 } 342 343 bool AreChannelsSupported(const SQLiteIndex& index) 344 { 345 UNSCOPED_INFO("Index " << index.GetVersion()); 346 return (index.GetVersion() < SQLiteVersion{ 2, 0 }); 347 } 348 349 bool AreManifestPathsSupported(const SQLiteIndex& index) 350 { 351 UNSCOPED_INFO("Index " << index.GetVersion()); 352 return (index.GetVersion() < SQLiteVersion{ 2, 0 }); 353 } 354 355 std::string GetPropertyStringByKey(const SQLiteIndex& index, rowid_t primaryId, PackageVersionProperty property) 356 { 357 auto result = index.GetPropertyByPrimaryId(primaryId, property); 358 REQUIRE(result); 359 return result.value(); 360 } 361 362 std::string GetPropertyStringByKey(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property, std::string_view version, std::string_view channel) 363 { 364 if (AreVersionKeysSupported(index)) 365 { 366 auto manifestId = index.GetManifestIdByKey(id, version, channel); 367 REQUIRE(manifestId); 368 auto result = index.GetPropertyByPrimaryId(manifestId.value(), property); 369 REQUIRE(result); 370 return result.value(); 371 } 372 else 373 { 374 return GetPropertyStringByKey(index, id, property); 375 } 376 } 377 378 std::string GetPropertyStringById(const SQLiteIndex& index, rowid_t id, PackageVersionProperty property) 379 { 380 if (AreVersionKeysSupported(index)) 381 { 382 auto versions = index.GetVersionKeysById(id); 383 REQUIRE(!versions.empty()); 384 return GetPropertyStringByKey(index, versions[0].ManifestId, property); 385 } 386 else 387 { 388 return GetPropertyStringByKey(index, id, property); 389 } 390 } 391 392 std::string GetIdStringById(const SQLiteIndex& index, rowid_t id) 393 { 394 return GetPropertyStringById(index, id, PackageVersionProperty::Id); 395 } 396 397 std::string GetNameStringById(const SQLiteIndex& index, rowid_t id) 398 { 399 return GetPropertyStringById(index, id, PackageVersionProperty::Name); 400 } 401 402 std::string GetPathStringByKey(const SQLiteIndex& index, rowid_t id, std::string_view version, std::string_view channel) 403 { 404 return GetPropertyStringByKey(index, id, PackageVersionProperty::RelativePath, version, channel); 405 } 406 407 TEST_CASE("SQLiteIndexCreateLatestAndReopen", "[sqliteindex]") 408 { 409 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 410 INFO("Using temporary file named: " << tempFile.GetPath()); 411 412 SQLiteVersion versionCreated; 413 414 // Create the index 415 { 416 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, SQLiteVersion::Latest()); 417 versionCreated = index.GetVersion(); 418 } 419 420 // Reopen the index for read only 421 { 422 INFO("Trying with Read"); 423 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); 424 SQLiteVersion versionRead = index.GetVersion(); 425 REQUIRE(versionRead == versionCreated); 426 } 427 428 // Reopen the index for read/write 429 { 430 INFO("Trying with ReadWrite"); 431 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 432 SQLiteVersion versionRead = index.GetVersion(); 433 REQUIRE(versionRead == versionCreated); 434 } 435 436 // Reopen the index for immutable read 437 { 438 INFO("Trying with Immutable"); 439 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); 440 SQLiteVersion versionRead = index.GetVersion(); 441 REQUIRE(versionRead == versionCreated); 442 } 443 } 444 445 TEST_CASE("SQLiteIndexCreateAndAddManifest", "[sqliteindex]") 446 { 447 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 448 INFO("Using temporary file named: " << tempFile.GetPath()); 449 450 Manifest manifest; 451 std::string relativePath = "test/id/1.0.0.yaml"; 452 453 SQLiteIndex index = SimpleTestSetup(tempFile, manifest, SQLiteVersion::Latest()); 454 } 455 456 TEST_CASE("SQLiteIndexCreateAndAddManifestFile", "[sqliteindex]") 457 { 458 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 459 INFO("Using temporary file named: " << tempFile.GetPath()); 460 461 SQLiteIndex index = CreateTestIndex(tempFile); 462 463 TestDataFile manifestFile{ "Manifest-Good.yaml" }; 464 std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; 465 466 index.AddManifest(manifestFile, manifestPath); 467 } 468 469 TEST_CASE("SQLiteIndexCreateAndAddManifestDuplicate", "[sqliteindex]") 470 { 471 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 472 INFO("Using temporary file named: " << tempFile.GetPath()); 473 474 Manifest manifest; 475 476 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 477 auto relativePath = GetPathFromManifest(manifest); 478 479 // Attempting to add the same manifest at a different path should fail. 480 REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yaml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); 481 482 // Attempting to add the same manifest with a differently cased Id at a different path should fail. 483 manifest.Id = ToLower(manifest.Id); 484 REQUIRE_THROWS_HR(index.AddManifest(manifest, "differentpath.yaml"), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); 485 486 // Attempting to add a different manifest at the same path should fail. 487 manifest.Id += "-new"; 488 REQUIRE_THROWS_HR(index.AddManifest(manifest, relativePath), HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS)); 489 } 490 491 TEST_CASE("SQLiteIndex_VersionReferencedByDependenciesClearsUnusedVersionAndKeepUsedVersion", "[sqliteindex][V1_4]") 492 { 493 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 494 INFO("Using temporary file named: " << tempFile.GetPath()); 495 496 Manifest dependencyManifest1, dependencyManifest2, manifest, isolatedManifest; 497 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 498 499 auto& publisher2 = "Test2"; 500 CreateFakeManifest(dependencyManifest2, publisher2); 501 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 502 503 auto& publisher3 = "Test3"; 504 CreateFakeManifest(manifest, publisher3); 505 std::string dependencyOnlyVersion = "0.0.5"; 506 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 507 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, dependencyOnlyVersion)); 508 509 index.AddManifest(manifest, GetPathFromManifest(manifest)); 510 511 // Create a new manifest that depends on v0.0.5 512 auto& publisher4 = "Test4"; 513 CreateFakeManifest(isolatedManifest, publisher4); 514 isolatedManifest.Version = dependencyOnlyVersion; 515 index.AddManifest(isolatedManifest); 516 517 index.RemoveManifest(isolatedManifest); 518 // After deletion that the version(v0.0.5) must be present because it still referenced via dependencies table. 519 { 520 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 521 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, dependencyOnlyVersion).has_value()); 522 } 523 524 index.RemoveManifest(manifest); 525 // Now, that we've deleted the manifest depending on version(v0.0.5), it should be absent. 526 { 527 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 528 REQUIRE(!Schema::V1_0::VersionTable::SelectIdByValue(connection, dependencyOnlyVersion).has_value()); 529 } 530 index.RemoveManifest(dependencyManifest1); 531 index.RemoveManifest(dependencyManifest2); 532 533 // Final sanity check, nothing should be in the version table. 534 { 535 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 536 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 537 } 538 } 539 540 TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies", "[sqliteindex][V1_4]") 541 { 542 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 543 INFO("Using temporary file named: " << tempFile.GetPath()); 544 545 Manifest dependencyManifest1, dependencyManifest2, manifest; 546 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 547 548 auto& publisher2 = "Test2"; 549 CreateFakeManifest(dependencyManifest2, publisher2); 550 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 551 552 auto& publisher3 = "Test3"; 553 CreateFakeManifest(manifest, publisher3); 554 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 555 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); 556 557 index.AddManifest(manifest, GetPathFromManifest(manifest)); 558 index.UpdateManifest(manifest, GetPathFromManifest(manifest)); 559 index.RemoveManifest(manifest); 560 } 561 562 TEST_CASE("SQLiteIndex_AddManifestWithDependencies_MissingPackage", "[sqliteindex][V1_4]") 563 { 564 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 565 INFO("Using temporary file named: " << tempFile.GetPath()); 566 567 Manifest dependencyManifest1, dependencyManifest2, manifest; 568 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 569 570 // Publisher2 is not present 571 auto& publisher2 = "Test2"; 572 CreateFakeManifest(dependencyManifest2, publisher2); 573 574 auto& publisher3 = "Test3"; 575 CreateFakeManifest(manifest, publisher3); 576 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 577 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); 578 579 REQUIRE_THROWS_HR(index.AddManifest(manifest, GetPathFromManifest(manifest)), APPINSTALLER_CLI_ERROR_MISSING_PACKAGE); 580 } 581 582 TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_MissingVersion", "[sqliteindex][V1_4]") 583 { 584 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 585 INFO("Using temporary file named: " << tempFile.GetPath()); 586 587 Manifest dependencyManifest1, dependencyManifest2, manifest; 588 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 589 590 auto& publisher2 = "Test2"; 591 CreateFakeManifest(dependencyManifest2, publisher2); 592 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 593 594 auto& publisher3 = "Test3"; 595 CreateFakeManifest(manifest, publisher3); 596 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "0.0.1")); 597 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "0.0.2")); 598 599 index.AddManifest(manifest, GetPathFromManifest(manifest)); 600 index.UpdateManifest(manifest, GetPathFromManifest(manifest)); 601 index.RemoveManifest(manifest); 602 } 603 604 TEST_CASE("SQLiteIndex_AddUpdateRemoveManifestWithDependencies_EmptyManifestVersion", "[sqliteindex][V1_4]") 605 { 606 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 607 INFO("Using temporary file named: " << tempFile.GetPath()); 608 609 Manifest dependencyManifest1, dependencyManifest2, manifest; 610 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 611 612 auto& publisher2 = "Test2"; 613 CreateFakeManifest(dependencyManifest2, publisher2); 614 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 615 616 auto& publisher3 = "Test3"; 617 CreateFakeManifest(manifest, publisher3); 618 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id)); 619 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id)); 620 621 index.AddManifest(manifest, GetPathFromManifest(manifest)); 622 index.UpdateManifest(manifest, GetPathFromManifest(manifest)); 623 index.RemoveManifest(manifest); 624 } 625 626 TEST_CASE("SQLiteIndex_DependenciesTable_CheckConsistency", "[sqliteindex][V1_4]") 627 { 628 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 629 INFO("Using temporary file named: " << tempFile.GetPath()); 630 631 { 632 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 633 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 634 635 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 636 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 637 638 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 639 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 640 641 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 642 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 643 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 644 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 645 646 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 647 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 648 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 649 } 650 651 { 652 // Open it directly to modify the table 653 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 654 rowid_t nonExistentRowId = 40; 655 rowid_t nonExistentManifest = 41; 656 rowid_t nonExistentVersion = 42; 657 rowid_t nonExistentPackageId = 43; 658 659 Builder::StatementBuilder builder; 660 builder.InsertInto(Schema::V1_4::DependenciesTable::TableName()) 661 .Values(nonExistentRowId, nonExistentManifest, nonExistentVersion, nonExistentPackageId); 662 builder.Execute(connection); 663 } 664 665 { 666 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 667 668 REQUIRE(!index.CheckConsistency(true)); 669 } 670 671 TempFile tempFile2{ "repolibtest_tempdb"s, ".db"s }; 672 INFO("Using temporary file named: " << tempFile2.GetPath()); 673 674 { 675 SQLiteIndex index = CreateTestIndex(tempFile2, SQLiteVersion::Latest()); 676 677 Manifest manifest; 678 manifest.Id = "Foo"; 679 manifest.Version = "10.0"; 680 681 index.AddManifest(manifest, "path"); 682 683 REQUIRE(index.CheckConsistency(true)); 684 685 // Add dependency that does not require min version 686 Manifest manifestWithDependency1; 687 manifestWithDependency1.Id = "Bar1"; 688 manifestWithDependency1.Version = "10.0"; 689 manifestWithDependency1.Installers.push_back({}); 690 manifestWithDependency1.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id)); 691 692 index.AddManifest(manifestWithDependency1, "path1"); 693 694 REQUIRE(index.CheckConsistency(true)); 695 696 // Add dependency with min version satisfied 697 Manifest manifestWithDependency2; 698 manifestWithDependency2.Id = "Bar2"; 699 manifestWithDependency2.Version = "10.0"; 700 manifestWithDependency2.Installers.push_back({}); 701 manifestWithDependency2.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id, "1.0")); 702 703 index.AddManifest(manifestWithDependency2, "path2"); 704 705 REQUIRE(index.CheckConsistency(true)); 706 707 // Add dependency with min version not satisfied 708 Manifest manifestWithDependency3; 709 manifestWithDependency3.Id = "Bar3"; 710 manifestWithDependency3.Version = "10.0"; 711 manifestWithDependency3.Installers.push_back({}); 712 manifestWithDependency3.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, manifest.Id, "11.0")); 713 714 index.AddManifest(manifestWithDependency3, "path3"); 715 716 REQUIRE_FALSE(index.CheckConsistency(true)); 717 } 718 } 719 720 TEST_CASE("SQLiteIndex_RemoveManifestFile_NotPresent", "[sqliteindex]") 721 { 722 SQLiteIndex index = CreateTestIndex(SQLITE_MEMORY_DB_CONNECTION_TARGET); 723 724 TestDataFile manifestFile{ "Manifest-Good.yaml" }; 725 std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; 726 727 REQUIRE_THROWS_HR(index.RemoveManifest(manifestFile, manifestPath), E_NOT_SET); 728 } 729 730 TEST_CASE("SQLiteIndex_RemoveManifest", "[sqliteindex][V1_0]") 731 { 732 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 733 INFO("Using temporary file named: " << tempFile.GetPath()); 734 735 std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; 736 Manifest manifest1; 737 manifest1.Installers.push_back({}); 738 manifest1.Id = "test.id"; 739 manifest1.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 740 manifest1.Moniker = "testmoniker"; 741 manifest1.Version = "1.0.0"; 742 manifest1.Channel = "test"; 743 manifest1.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 744 manifest1.Installers[0].Commands = { "test1", "test2" }; 745 746 std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; 747 Manifest manifest2; 748 manifest2.Installers.push_back({}); 749 manifest2.Id = "test.woah"; 750 manifest2.DefaultLocalization.Add<Localization::PackageName>("Test Name WOAH"); 751 manifest2.Moniker = "testmoniker"; 752 manifest2.Version = "1.0.0"; 753 manifest2.Channel = "test"; 754 manifest2.DefaultLocalization.Add<Localization::Tags>({}); 755 manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; 756 757 { 758 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 759 760 index.AddManifest(manifest1, manifest1Path); 761 index.AddManifest(manifest2, manifest2Path); 762 763 // Now remove manifest1 764 index.RemoveManifest(manifest1, manifest1Path); 765 } 766 767 { 768 // Open it directly to directly test table state 769 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 770 771 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 772 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 773 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 774 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 775 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 776 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 777 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 778 // Because manifest2 had no tags 779 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 780 REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); 781 } 782 783 { 784 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 785 786 // Now remove manifest2 787 index.RemoveManifest(manifest2, manifest2Path); 788 } 789 790 // Open it directly to directly test table state 791 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 792 793 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 794 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 795 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 796 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 797 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 798 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 799 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 800 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 801 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 802 } 803 804 TEST_CASE("SQLiteIndex_RemoveManifestWithDependencies", "[sqliteindex][V1_4]") 805 { 806 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 807 INFO("Using temporary file named: " << tempFile.GetPath()); 808 809 Manifest dependencyManifest1, dependencyManifest2, manifest; 810 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 811 812 auto& publisher2 = "Test2"; 813 CreateFakeManifest(dependencyManifest2, publisher2); 814 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 815 816 auto& publisher3 = "Test3"; 817 CreateFakeManifest(manifest, publisher3); 818 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 819 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); 820 821 index.AddManifest(manifest, GetPathFromManifest(manifest)); 822 823 index.RemoveManifest(manifest, GetPathFromManifest(manifest)); 824 } 825 826 TEST_CASE("SQLiteIndex_ValidateManifestWithDependencies", "[sqliteindex][V1_4]") 827 { 828 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 829 INFO("Using temporary file named: " << tempFile.GetPath()); 830 831 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 832 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 833 834 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 835 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 836 837 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 838 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 839 840 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 841 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 842 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 843 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 844 845 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 846 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 847 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 848 REQUIRE(PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest)); 849 } 850 851 TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesHasLoops", "[sqliteindex][V1_4]") 852 { 853 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 854 INFO("Using temporary file named: " << tempFile.GetPath()); 855 856 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 857 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 858 859 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 860 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 861 862 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 863 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 864 865 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 866 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 867 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 868 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 869 870 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 871 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 872 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 873 index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); 874 875 levelThreeManifest.Installers.push_back(ManifestInstaller{}); 876 levelThreeManifest.Installers[1].Dependencies.Add(Dependency(DependencyType::Package, topLevelManifest.Id, "1.0.0")); 877 REQUIRE_THROWS_HR( 878 PackageDependenciesValidation::ValidateManifestDependencies(&index, levelThreeManifest), 879 APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); 880 } 881 882 TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesMissingNode", "[sqliteindex][V1_4]") 883 { 884 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 885 INFO("Using temporary file named: " << tempFile.GetPath()); 886 887 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 888 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 889 890 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 891 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 892 893 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 894 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 895 896 // This node is missing, because it's not in the index. 897 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 898 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 899 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 900 901 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 902 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 903 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 904 REQUIRE_THROWS_HR( 905 PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest), 906 APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); 907 } 908 909 TEST_CASE("SQLiteIndex_ValidateManifestWithDependenciesNoSuitableMinVersion", "[sqliteindex][V1_4]") 910 { 911 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 912 INFO("Using temporary file named: " << tempFile.GetPath()); 913 914 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 915 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 916 917 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 918 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 919 920 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 921 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 922 923 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 924 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 925 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 926 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 927 928 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 929 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 930 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "2.0.0")); 931 932 REQUIRE_THROWS_HR( 933 PackageDependenciesValidation::ValidateManifestDependencies(&index, topLevelManifest), 934 APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); 935 } 936 937 TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken", "[sqliteindex][V1_4]") 938 { 939 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 940 INFO("Using temporary file named: " << tempFile.GetPath()); 941 942 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest; 943 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 944 945 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 946 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 947 948 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 949 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 950 951 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 952 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 953 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 954 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 955 956 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 957 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 958 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 959 index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); 960 961 REQUIRE_THROWS_HR( 962 PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifest), 963 APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); 964 } 965 966 TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureNotBroken", "[sqliteindex][V1_4]") 967 { 968 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 969 INFO("Using temporary file named: " << tempFile.GetPath()); 970 971 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; 972 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 973 974 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 975 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 976 977 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "1.0.0")); 978 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 979 980 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 981 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 982 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 983 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 984 985 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 986 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 987 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 988 index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); 989 990 constexpr std::string_view levelThreeManifestV2Publisher = "Test"; 991 CreateFakeManifest(levelThreeManifestV2, levelThreeManifestV2Publisher, "2.0.0"); 992 index.AddManifest(levelThreeManifestV2, GetPathFromManifest(levelThreeManifestV2)); 993 994 REQUIRE(PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifest)); 995 } 996 997 TEST_CASE("SQLiteIndex_ValidateManifestWhenManifestIsDependency_StructureBroken_NoSuitableOldManifest", "[sqliteindex][V1_4]") 998 { 999 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1000 INFO("Using temporary file named: " << tempFile.GetPath()); 1001 1002 Manifest levelOneManifest, levelTwoManifest, levelThreeManifest, topLevelManifest, levelThreeManifestV2; 1003 SQLiteIndex index = SimpleTestSetup(tempFile, levelThreeManifest, SQLiteVersion::Latest()); 1004 1005 constexpr std::string_view levelThreeManifestV2Publisher = "Test"; 1006 CreateFakeManifest(levelThreeManifestV2, levelThreeManifestV2Publisher, "2.0.0"); 1007 index.AddManifest(levelThreeManifestV2, GetPathFromManifest(levelThreeManifestV2)); 1008 1009 constexpr std::string_view levelTwoManifestPublisher = "LevelTwoManifest"; 1010 CreateFakeManifest(levelTwoManifest, levelTwoManifestPublisher); 1011 1012 levelTwoManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelThreeManifest.Id, "2.0.0")); 1013 index.AddManifest(levelTwoManifest, GetPathFromManifest(levelTwoManifest)); 1014 1015 constexpr std::string_view levelOneManifestPublisher = "LevelOneManifest"; 1016 CreateFakeManifest(levelOneManifest, levelOneManifestPublisher); 1017 levelOneManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelTwoManifest.Id, "1.0.0")); 1018 index.AddManifest(levelOneManifest, GetPathFromManifest(levelOneManifest)); 1019 1020 constexpr std::string_view topLevelManifestPublisher = "TopLevelManifest"; 1021 CreateFakeManifest(topLevelManifest, topLevelManifestPublisher); 1022 topLevelManifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, levelOneManifest.Id, "1.0.0")); 1023 index.AddManifest(topLevelManifest, GetPathFromManifest(topLevelManifest)); 1024 1025 REQUIRE_THROWS( 1026 PackageDependenciesValidation::VerifyDependenciesStructureForManifestDelete(&index, levelThreeManifestV2), 1027 APPINSTALLER_CLI_ERROR_DEPENDENCIES_VALIDATION_FAILED); 1028 } 1029 1030 TEST_CASE("SQLiteIndex_RemoveManifest_EnsureConsistentRowId", "[sqliteindex][V1_7]") 1031 { 1032 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1033 INFO("Using temporary file named: " << tempFile.GetPath()); 1034 1035 std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; 1036 Manifest manifest1; 1037 manifest1.Installers.push_back({}); 1038 manifest1.Id = "test.id"; 1039 manifest1.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 1040 manifest1.Moniker = "testmoniker"; 1041 manifest1.Version = "1.0.0"; 1042 manifest1.Channel = "test"; 1043 manifest1.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1044 manifest1.Installers[0].Commands = { "test1", "test2" }; 1045 1046 std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; 1047 Manifest manifest2; 1048 manifest2.Installers.push_back({}); 1049 manifest2.Id = "test.woah"; 1050 manifest2.DefaultLocalization.Add<Localization::PackageName>("Test Name WOAH"); 1051 manifest2.Moniker = "testmoniker"; 1052 manifest2.Version = "1.0.0"; 1053 manifest2.Channel = "test"; 1054 manifest2.DefaultLocalization.Add<Localization::Tags>({}); 1055 manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; 1056 1057 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion{ 1, 7 }); 1058 1059 index.AddManifest(manifest1, manifest1Path); 1060 index.AddManifest(manifest2, manifest2Path); 1061 1062 // Get the second manifest's id for validating consistency 1063 SearchRequest request; 1064 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, manifest2.Id)); 1065 auto result = index.Search(request); 1066 1067 REQUIRE(result.Matches.size() == 1); 1068 auto manifest2IdRowId = result.Matches[0].first; 1069 1070 auto rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); 1071 REQUIRE(rowId); 1072 auto manifest2RowId = rowId.value(); 1073 1074 // Now remove manifest1 and prepare 1075 index.RemoveManifest(manifest1, manifest1Path); 1076 index.PrepareForPackaging(); 1077 1078 // Checking consistency will also uncover issues, but not potentially the same ones as below. 1079 REQUIRE(index.CheckConsistency(true)); 1080 1081 // Repeat search to ensure consistent ids 1082 result = index.Search(request); 1083 REQUIRE(result.Matches.size() == 1); 1084 REQUIRE(result.Matches[0].first == manifest2IdRowId); 1085 1086 rowId = index.GetManifestIdByKey(manifest2IdRowId, {}, {}); 1087 REQUIRE(rowId); 1088 REQUIRE(rowId.value() == manifest2RowId); 1089 1090 REQUIRE(manifest2.Id == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Id)); 1091 REQUIRE(manifest2.DefaultLocalization.Get<Localization::PackageName>() == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Name)); 1092 REQUIRE(manifest2.Moniker == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Moniker)); 1093 REQUIRE(manifest2.Version == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Version)); 1094 REQUIRE(manifest2.Channel == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::Channel)); 1095 REQUIRE(manifest2Path == index.GetPropertyByPrimaryId(manifest2RowId, PackageVersionProperty::RelativePath)); 1096 } 1097 1098 TEST_CASE("SQLiteIndex_RemoveManifestFile", "[sqliteindex][V1_0]") 1099 { 1100 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1101 INFO("Using temporary file named: " << tempFile.GetPath()); 1102 1103 { 1104 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1105 1106 TestDataFile manifestFile{ "Manifest-Good.yaml" }; 1107 std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; 1108 1109 index.AddManifest(manifestFile, manifestPath); 1110 1111 // Now remove that manifest 1112 index.RemoveManifest(manifestFile, manifestPath); 1113 } 1114 1115 // Open it directly to directly test table state 1116 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1117 1118 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1119 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1120 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1121 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1122 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1123 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1124 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 1125 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1126 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1127 } 1128 1129 TEST_CASE("SQLiteIndex_UpdateManifest", "[sqliteindex][V1_4]") 1130 { 1131 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1132 INFO("Using temporary file named: " << tempFile.GetPath()); 1133 1134 std::string manifestPath = "test/id/test.id-1.0.0.yaml"; 1135 Manifest manifest; 1136 manifest.Installers.push_back({}); 1137 manifest.Id = "test.id"; 1138 manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); 1139 manifest.Moniker = "testmoniker"; 1140 manifest.Version = "1.0.0"; 1141 manifest.Channel = "test"; 1142 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1143 manifest.Installers[0].Commands = { "test1", "test2" }; 1144 1145 1146 { 1147 auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); 1148 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); 1149 1150 index.AddManifest(manifest, manifestPath); 1151 } 1152 1153 { 1154 // Open it directly to directly test table state 1155 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1156 1157 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1158 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1159 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1160 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1161 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1162 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1163 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1164 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1165 REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); 1166 } 1167 1168 { 1169 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1170 1171 // Update with no updates should return false 1172 REQUIRE(!index.UpdateManifest(manifest, manifestPath)); 1173 1174 manifest.DefaultLocalization.Add<Localization::Description>("description2"); 1175 1176 // Update with no indexed updates should return false 1177 REQUIRE(!index.UpdateManifest(manifest, manifestPath)); 1178 1179 // Update with indexed changes 1180 manifest.DefaultLocalization.Add<Localization::PackageName>("Test Name2"); 1181 manifest.Moniker = "testmoniker2"; 1182 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2", "t3" }); 1183 manifest.Installers[0].Commands = {}; 1184 1185 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1186 } 1187 1188 { 1189 // Open it directly to directly test table state 1190 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1191 1192 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1193 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1194 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1195 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1196 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1197 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1198 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1199 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1200 // The update removed all commands 1201 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1202 } 1203 1204 { 1205 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1206 1207 // Now remove manifest2 1208 index.RemoveManifest(manifest, manifestPath); 1209 } 1210 1211 // Open it directly to directly test table state 1212 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1213 1214 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1215 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1216 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1217 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1218 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1219 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1220 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 1221 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1222 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1223 } 1224 1225 TEST_CASE("SQLiteIndex_UpdateManifestWithDependencies", "[sqliteindex][V1_4]") 1226 { 1227 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1228 INFO("Using temporary file named: " << tempFile.GetPath()); 1229 1230 Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; 1231 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 1232 1233 auto& publisher2 = "Test2"; 1234 CreateFakeManifest(dependencyManifest2, publisher2); 1235 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 1236 1237 auto& publisher3 = "Test3"; 1238 CreateFakeManifest(manifest, publisher3); 1239 const std::string dependencyPath3 = GetPathFromManifest(manifest); 1240 1241 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 1242 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); 1243 1244 index.AddManifest(manifest, dependencyPath3); 1245 1246 auto& publisher4 = "Test4"; 1247 CreateFakeManifest(updateManifest, publisher4); 1248 index.AddManifest(updateManifest, GetPathFromManifest(updateManifest)); 1249 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, updateManifest.Id, "1.0.0")); 1250 1251 REQUIRE(index.UpdateManifest(manifest, dependencyPath3)); 1252 } 1253 1254 TEST_CASE("SQLiteIndex_UpdateManifestWithDependenciesDeleteAndAdd", "[sqliteindex][V1_4]") 1255 { 1256 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1257 INFO("Using temporary file named: " << tempFile.GetPath()); 1258 1259 Manifest dependencyManifest1, dependencyManifest2, manifest, updateManifest; 1260 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 1261 1262 auto& publisher2 = "Test2"; 1263 CreateFakeManifest(dependencyManifest2, publisher2); 1264 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 1265 1266 auto& publisher3 = "Test3"; 1267 CreateFakeManifest(manifest, publisher3); 1268 const std::string dependencyPath3 = GetPathFromManifest(manifest); 1269 1270 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 1271 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest2.Id, "1.0.0")); 1272 1273 index.AddManifest(manifest, dependencyPath3); 1274 1275 manifest.Installers[0].Dependencies.Clear(); 1276 1277 auto& publisher4 = "Test4"; 1278 CreateFakeManifest(updateManifest, publisher4); 1279 index.AddManifest(updateManifest, GetPathFromManifest(updateManifest)); 1280 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, updateManifest.Id, "1.0.0")); 1281 1282 REQUIRE(index.UpdateManifest(manifest, dependencyPath3)); 1283 } 1284 1285 TEST_CASE("SQLiteIndex_UpdateManifestChangePath", "[sqliteindex][V1_0]") 1286 { 1287 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1288 INFO("Using temporary file named: " << tempFile.GetPath()); 1289 1290 std::string manifestPath = "test/id/test.id-1.0.0.yaml"; 1291 Manifest manifest; 1292 manifest.Installers.push_back({}); 1293 manifest.Id = "test.id"; 1294 manifest.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 1295 manifest.Moniker = "testmoniker"; 1296 manifest.Version = "1.0.0"; 1297 manifest.Channel = "test"; 1298 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1299 manifest.Installers[0].Commands = { "test1", "test2" }; 1300 1301 { 1302 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1303 1304 index.AddManifest(manifest, manifestPath); 1305 } 1306 1307 { 1308 // Open it directly to directly test table state 1309 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1310 1311 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1312 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1313 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1314 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1315 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1316 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1317 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1318 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1319 REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); 1320 } 1321 1322 { 1323 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1324 1325 manifestPath = "test/newid/test.newid-1.0.0.yaml"; 1326 1327 // Update with path update should indicate change 1328 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1329 } 1330 1331 { 1332 // Open it directly to directly test table state 1333 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1334 1335 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1336 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1337 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1338 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1339 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1340 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1341 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1342 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1343 REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); 1344 } 1345 1346 { 1347 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1348 1349 // Now remove manifest, with unknown path 1350 index.RemoveManifest(manifest, ""); 1351 } 1352 1353 // Open it directly to directly test table state 1354 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1355 1356 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1357 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1358 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1359 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1360 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1361 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1362 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 1363 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1364 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1365 } 1366 1367 TEST_CASE("SQLiteIndex_UpdateManifest_Pathless", "[sqliteindex][V1_0]") 1368 { 1369 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1370 INFO("Using temporary file named: " << tempFile.GetPath()); 1371 1372 Manifest manifest; 1373 manifest.Installers.push_back({}); 1374 manifest.Id = "test.id"; 1375 manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); 1376 manifest.Moniker = "testmoniker"; 1377 manifest.Version = "1.0.0"; 1378 manifest.Channel = "test"; 1379 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1380 manifest.Installers[0].Commands = { "test1", "test2" }; 1381 1382 { 1383 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1384 1385 index.AddManifest(manifest); 1386 } 1387 1388 { 1389 // Open it directly to directly test table state 1390 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1391 1392 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1393 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1394 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1395 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1396 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1397 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1398 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1399 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1400 REQUIRE(!Schema::V1_0::CommandsTable::IsEmpty(connection)); 1401 } 1402 1403 { 1404 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1405 1406 // Update with no updates should return false 1407 REQUIRE(!index.UpdateManifest(manifest)); 1408 1409 manifest.DefaultLocalization.Add<Localization::Description>("description2"); 1410 1411 // Update with no indexed updates should return false 1412 REQUIRE(!index.UpdateManifest(manifest)); 1413 1414 // Update with indexed changes 1415 manifest.DefaultLocalization.Add<Localization::PackageName>("Test Name2"); 1416 manifest.Moniker = "testmoniker2"; 1417 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2", "t3" }); 1418 manifest.Installers[0].Commands = {}; 1419 1420 REQUIRE(index.UpdateManifest(manifest)); 1421 } 1422 1423 { 1424 // Open it directly to directly test table state 1425 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1426 1427 REQUIRE(!Schema::V1_0::ManifestTable::IsEmpty(connection)); 1428 REQUIRE(!Schema::V1_0::IdTable::IsEmpty(connection)); 1429 REQUIRE(!Schema::V1_0::NameTable::IsEmpty(connection)); 1430 REQUIRE(!Schema::V1_0::MonikerTable::IsEmpty(connection)); 1431 REQUIRE(!Schema::V1_0::VersionTable::IsEmpty(connection)); 1432 REQUIRE(!Schema::V1_0::ChannelTable::IsEmpty(connection)); 1433 REQUIRE(!Schema::V1_0::PathPartTable::IsEmpty(connection)); 1434 REQUIRE(!Schema::V1_0::TagsTable::IsEmpty(connection)); 1435 // The update removed all commands 1436 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1437 } 1438 1439 { 1440 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1441 1442 // Now remove manifest2 1443 index.RemoveManifest(manifest); 1444 } 1445 1446 // Open it directly to directly test table state 1447 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1448 1449 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1450 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1451 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1452 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1453 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1454 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1455 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1456 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1457 } 1458 1459 TEST_CASE("SQLiteIndex_UpdateManifestChangeCase", "[sqliteindex][V1_0]") 1460 { 1461 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1462 INFO("Using temporary file named: " << tempFile.GetPath()); 1463 1464 std::string manifestPath = "test/id/test.id-1.0.0.yaml"; 1465 Manifest manifest; 1466 manifest.Installers.push_back({}); 1467 manifest.Id = "test.id"; 1468 manifest.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 1469 manifest.Moniker = "testmoniker"; 1470 manifest.Version = "1.0.0-test"; 1471 manifest.Channel = "test"; 1472 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1473 manifest.Installers[0].Commands = { "test1", "test2" }; 1474 1475 { 1476 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1477 1478 index.AddManifest(manifest, manifestPath); 1479 } 1480 1481 { 1482 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1483 1484 manifest.Id = "Test.Id"; 1485 1486 // Update with path update should indicate change 1487 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1488 } 1489 1490 { 1491 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1492 1493 manifest.Version = "1.0.0-Test"; 1494 1495 // Update with path update should indicate change 1496 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1497 } 1498 1499 { 1500 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1501 1502 manifest.Channel = "Test"; 1503 1504 // Update with path update should indicate change 1505 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1506 } 1507 1508 { 1509 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1510 1511 manifest.DefaultLocalization.Add<Localization::PackageName>("test name"); 1512 1513 // Update with path update should indicate change 1514 REQUIRE(index.UpdateManifest(manifest, manifestPath)); 1515 } 1516 1517 { 1518 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1519 1520 // Now remove manifest, with unknown path 1521 index.RemoveManifest(manifest, ""); 1522 } 1523 1524 // Open it directly to directly test table state 1525 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1526 1527 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1528 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1529 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1530 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1531 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1532 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1533 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 1534 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1535 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1536 } 1537 1538 TEST_CASE("SQLiteIndex_IdCaseInsensitivity", "[sqliteindex][V1_0]") 1539 { 1540 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1541 INFO("Using temporary file named: " << tempFile.GetPath()); 1542 1543 std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; 1544 Manifest manifest1; 1545 manifest1.Installers.push_back({}); 1546 manifest1.Id = "test.id"; 1547 manifest1.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 1548 manifest1.Moniker = "testmoniker"; 1549 manifest1.Version = "1.0.0"; 1550 manifest1.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 1551 manifest1.Installers[0].Commands = { "test1", "test2" }; 1552 1553 std::string manifest2Path = "test/id/test.id-2.0.0.yaml"; 1554 Manifest manifest2 = manifest1; 1555 manifest2.Id = "Test.Id"; 1556 manifest1.Version = "2.0.0"; 1557 1558 { 1559 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1560 1561 index.AddManifest(manifest1, manifest1Path); 1562 1563 auto results = index.Search({}); 1564 REQUIRE(results.Matches.size() == 1); 1565 REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); 1566 } 1567 1568 { 1569 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1570 1571 index.AddManifest(manifest2, manifest2Path); 1572 1573 auto results = index.Search({}); 1574 REQUIRE(results.Matches.size() == 1); 1575 REQUIRE(manifest2.Id == GetIdStringById(index, results.Matches[0].first)); 1576 } 1577 1578 { 1579 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1580 1581 manifest1.Id = "TEST.ID"; 1582 1583 REQUIRE(index.UpdateManifest(manifest1, manifest1Path)); 1584 1585 auto results = index.Search({}); 1586 REQUIRE(results.Matches.size() == 1); 1587 REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); 1588 } 1589 1590 { 1591 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1592 1593 index.RemoveManifest(manifest1, manifest1Path); 1594 1595 auto results = index.Search({}); 1596 REQUIRE(results.Matches.size() == 1); 1597 REQUIRE(manifest1.Id == GetIdStringById(index, results.Matches[0].first)); 1598 } 1599 1600 { 1601 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 1602 1603 index.RemoveManifest(manifest2, manifest2Path); 1604 1605 auto results = index.Search({}); 1606 REQUIRE(results.Matches.empty()); 1607 } 1608 1609 // Open it directly to directly test table state 1610 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1611 1612 REQUIRE(Schema::V1_0::ManifestTable::IsEmpty(connection)); 1613 REQUIRE(Schema::V1_0::IdTable::IsEmpty(connection)); 1614 REQUIRE(Schema::V1_0::NameTable::IsEmpty(connection)); 1615 REQUIRE(Schema::V1_0::MonikerTable::IsEmpty(connection)); 1616 REQUIRE(Schema::V1_0::VersionTable::IsEmpty(connection)); 1617 REQUIRE(Schema::V1_0::ChannelTable::IsEmpty(connection)); 1618 REQUIRE(Schema::V1_0::PathPartTable::IsEmpty(connection)); 1619 REQUIRE(Schema::V1_0::TagsTable::IsEmpty(connection)); 1620 REQUIRE(Schema::V1_0::CommandsTable::IsEmpty(connection)); 1621 } 1622 1623 TEST_CASE("PathPartTable_EnsurePathExists_Negative_Paths", "[sqliteindex][V1_0]") 1624 { 1625 // Open it directly to directly test pathpart table 1626 Connection connection = Connection::Create(SQLITE_MEMORY_DB_CONNECTION_TARGET, Connection::OpenDisposition::Create); 1627 1628 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"()", false), E_INVALIDARG); 1629 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(\)", false), E_INVALIDARG); 1630 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(/)", false), E_INVALIDARG); 1631 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:)", false), E_INVALIDARG); 1632 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:\\)", false), E_INVALIDARG); 1633 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(C:\temp\path\file.txt)", false), E_INVALIDARG); 1634 REQUIRE_THROWS_HR(Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(\temp\path\file.txt)", false), E_INVALIDARG); 1635 } 1636 1637 TEST_CASE("PathPartTable_EnsurePathExists", "[sqliteindex][V1_0]") 1638 { 1639 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1640 INFO("Using temporary file named: " << tempFile.GetPath()); 1641 1642 // Create the index 1643 { 1644 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 0 }); 1645 SQLiteVersion versionCreated = index.GetVersion(); 1646 REQUIRE(versionCreated == SQLiteVersion{ 1, 0 }); 1647 } 1648 1649 // Open it directly to directly test pathpart table 1650 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 1651 1652 // attempt to find path that doesn't exist 1653 auto result0 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", false); 1654 REQUIRE(!std::get<0>(result0)); 1655 1656 // add path 1657 auto result1 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", true); 1658 REQUIRE(std::get<0>(result1)); 1659 1660 // Second time trying to create should return false and same id 1661 auto result2 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", true); 1662 REQUIRE(!std::get<0>(result2)); 1663 REQUIRE(std::get<1>(result1) == std::get<1>(result2)); 1664 1665 // Trying to find but not create should return true because it exists 1666 auto result3 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\c.txt)", false); 1667 REQUIRE(std::get<0>(result3)); 1668 REQUIRE(std::get<1>(result1) == std::get<1>(result3)); 1669 1670 // attempt to find a different file 1671 auto result4 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d.txt)", false); 1672 REQUIRE(!std::get<0>(result4)); 1673 1674 // add a different file 1675 auto result5 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d.txt)", true); 1676 REQUIRE(std::get<0>(result5)); 1677 REQUIRE(std::get<1>(result1) != std::get<1>(result5)); 1678 1679 // add the same file but deeper 1680 auto result6 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\b\d\c.txt)", true); 1681 REQUIRE(std::get<0>(result6)); 1682 REQUIRE(std::get<1>(result1) != std::get<1>(result6)); 1683 1684 // get the deeper file with extra separators 1685 auto result7 = Schema::V1_0::PathPartTable::EnsurePathExists(connection, R"(a\\b\d\\c.txt)", true); 1686 REQUIRE(!std::get<0>(result7)); 1687 REQUIRE(std::get<1>(result6) == std::get<1>(result7)); 1688 } 1689 1690 TEST_CASE("SQLiteIndex_PrepareForPackaging", "[sqliteindex]") 1691 { 1692 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1693 INFO("Using temporary file named: " << tempFile.GetPath()); 1694 1695 SQLiteIndex index = CreateTestIndex(tempFile); 1696 1697 TestDataFile manifestFile{ "Manifest-Good.yaml" }; 1698 std::filesystem::path manifestPath{ "microsoft/msixsdk/microsoft.msixsdk-1.7.32.yaml" }; 1699 1700 index.AddManifest(manifestFile, manifestPath); 1701 1702 index.PrepareForPackaging(); 1703 } 1704 1705 TEST_CASE("SQLiteIndex_Search_IdExactMatch", "[sqliteindex]") 1706 { 1707 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1708 INFO("Using temporary file named: " << tempFile.GetPath()); 1709 1710 Manifest manifest; 1711 std::string relativePath = "test/id/1.0.0.yaml"; 1712 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1713 1714 TestPrepareForRead(index); 1715 1716 SearchRequest request; 1717 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1718 1719 auto results = index.Search(request); 1720 REQUIRE(results.Matches.size() == 1); 1721 REQUIRE(results.Matches[0].second.Field == PackageMatchField::Id); 1722 REQUIRE(results.Matches[0].second.Type == MatchType::Exact); 1723 REQUIRE(results.Matches[0].second.Value == manifest.Id); 1724 } 1725 1726 TEST_CASE("SQLiteIndex_Search_MultipleMatch", "[sqliteindex]") 1727 { 1728 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1729 INFO("Using temporary file named: " << tempFile.GetPath()); 1730 1731 Manifest manifest; 1732 std::string relativePath = "test/id/1.0.0.yaml"; 1733 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1734 1735 manifest.Version = "2.0.0"; 1736 index.AddManifest(manifest, relativePath + "2"); 1737 1738 TestPrepareForRead(index); 1739 1740 SearchRequest request; 1741 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1742 1743 auto results = index.Search(request); 1744 REQUIRE(results.Matches.size() == 1); 1745 1746 if (AreVersionKeysSupported(index)) 1747 { 1748 auto result = index.GetVersionKeysById(results.Matches[0].first); 1749 REQUIRE(result.size() == 2); 1750 } 1751 else 1752 { 1753 REQUIRE_THROWS_HR(index.GetVersionKeysById(results.Matches[0].first), E_NOT_VALID_STATE); 1754 } 1755 } 1756 1757 TEST_CASE("SQLiteIndex_Search_NoMatch", "[sqliteindex]") 1758 { 1759 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1760 INFO("Using temporary file named: " << tempFile.GetPath()); 1761 1762 Manifest manifest; 1763 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1764 1765 TestPrepareForRead(index); 1766 1767 SearchRequest request; 1768 request.Query = RequestMatch(MatchType::Exact, "THIS DOES NOT MATCH ANYTHING!"); 1769 1770 auto results = index.Search(request); 1771 REQUIRE(results.Matches.size() == 0); 1772 } 1773 1774 TEST_CASE("SQLiteIndex_IdString", "[sqliteindex]") 1775 { 1776 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1777 INFO("Using temporary file named: " << tempFile.GetPath()); 1778 1779 Manifest manifest; 1780 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1781 1782 TestPrepareForRead(index); 1783 1784 SearchRequest request; 1785 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1786 1787 auto results = index.Search(request); 1788 REQUIRE(results.Matches.size() == 1); 1789 1790 auto result = GetIdStringById(index, results.Matches[0].first); 1791 REQUIRE(result == manifest.Id); 1792 } 1793 1794 TEST_CASE("SQLiteIndex_NameString", "[sqliteindex]") 1795 { 1796 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1797 INFO("Using temporary file named: " << tempFile.GetPath()); 1798 1799 Manifest manifest; 1800 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1801 1802 TestPrepareForRead(index); 1803 1804 SearchRequest request; 1805 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1806 1807 auto results = index.Search(request); 1808 REQUIRE(results.Matches.size() == 1); 1809 1810 auto result = GetNameStringById(index, results.Matches[0].first); 1811 REQUIRE(result == manifest.DefaultLocalization.Get<Localization::PackageName>()); 1812 } 1813 1814 TEST_CASE("SQLiteIndex_PathString", "[sqliteindex]") 1815 { 1816 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1817 INFO("Using temporary file named: " << tempFile.GetPath()); 1818 1819 Manifest manifest; 1820 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1821 auto relativePath = GetPathFromManifest(manifest); 1822 1823 TestPrepareForRead(index); 1824 1825 if (!AreManifestPathsSupported(index)) 1826 { 1827 return; 1828 } 1829 1830 SearchRequest request; 1831 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1832 1833 auto results = index.Search(request); 1834 REQUIRE(results.Matches.size() == 1); 1835 1836 auto specificResult = GetPathStringByKey(index, results.Matches[0].first, manifest.Version, manifest.Channel); 1837 REQUIRE(specificResult == relativePath); 1838 1839 auto latestResult = GetPathStringByKey(index, results.Matches[0].first, "", manifest.Channel); 1840 REQUIRE(latestResult == relativePath); 1841 } 1842 1843 TEST_CASE("SQLiteIndex_PathlessString", "[sqliteindex]") 1844 { 1845 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1846 INFO("Using temporary file named: " << tempFile.GetPath()); 1847 1848 Manifest manifest; 1849 std::string relativePath; 1850 1851 SQLiteIndex index = CreateTestIndex(tempFile); 1852 CreateFakeManifest(manifest, "Test"); 1853 index.AddManifest(manifest); 1854 1855 TestPrepareForRead(index); 1856 1857 if (!AreManifestPathsSupported(index)) 1858 { 1859 return; 1860 } 1861 1862 SearchRequest request; 1863 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1864 1865 auto results = index.Search(request); 1866 REQUIRE(results.Matches.size() == 1); 1867 1868 auto specificResult = GetPathStringByKey(index, results.Matches[0].first, manifest.Version, manifest.Channel); 1869 REQUIRE(specificResult == relativePath); 1870 1871 auto latestResult = GetPathStringByKey(index, results.Matches[0].first, "", manifest.Channel); 1872 REQUIRE(latestResult == relativePath); 1873 } 1874 1875 TEST_CASE("SQLiteIndex_Versions", "[sqliteindex]") 1876 { 1877 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1878 INFO("Using temporary file named: " << tempFile.GetPath()); 1879 1880 Manifest manifest; 1881 std::string relativePath = "test/id/1.0.0.yaml"; 1882 SQLiteIndex index = SimpleTestSetup(tempFile, manifest); 1883 1884 TestPrepareForRead(index); 1885 1886 SearchRequest request; 1887 request.Query = RequestMatch(MatchType::Exact, manifest.Id); 1888 1889 auto results = index.Search(request); 1890 REQUIRE(results.Matches.size() == 1); 1891 1892 if (AreVersionKeysSupported(index)) 1893 { 1894 auto result = index.GetVersionKeysById(results.Matches[0].first); 1895 REQUIRE(result.size() == 1); 1896 REQUIRE(result[0].VersionAndChannel.GetVersion().ToString() == manifest.Version); 1897 REQUIRE(result[0].VersionAndChannel.GetChannel().ToString() == manifest.Channel); 1898 } 1899 else 1900 { 1901 REQUIRE_THROWS_HR(index.GetVersionKeysById(results.Matches[0].first), E_NOT_VALID_STATE); 1902 } 1903 } 1904 1905 TEST_CASE("SQLiteIndex_Search_VersionSorting", "[sqliteindex]") 1906 { 1907 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1908 INFO("Using temporary file named: " << tempFile.GetPath()); 1909 1910 std::vector<VersionAndChannel> sortedList = 1911 { 1912 { UtilityVersion("15.0.0"), Channel("") }, 1913 { UtilityVersion("14.0.0"), Channel("") }, 1914 { UtilityVersion("13.2.0"), Channel("") }, 1915 { UtilityVersion("13.2.0-bugfix"), Channel("") }, 1916 { UtilityVersion("13.0.0"), Channel("") }, 1917 { UtilityVersion("16.0.0"), Channel("alpha") }, 1918 { UtilityVersion("15.8.0"), Channel("alpha") }, 1919 { UtilityVersion("15.1.0"), Channel("beta") }, 1920 }; 1921 1922 SQLiteIndex index = SearchTestSetup(tempFile, { 1923 { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, 1924 { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, 1925 { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, 1926 { "Id", "Name", "Moniker", "13.2.0", "", {}, { "Command" }, "Path4" }, 1927 { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, 1928 { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, 1929 { "Id", "Name", "Moniker", "13.2.0-bugfix", "", { "foo" }, { "com3" }, "Path7" }, 1930 { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, 1931 }); 1932 1933 TestPrepareForRead(index); 1934 1935 if (!AreChannelsSupported(index)) 1936 { 1937 return; 1938 } 1939 1940 SearchRequest request; 1941 request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); 1942 1943 auto results = index.Search(request); 1944 REQUIRE(results.Matches.size() == 1); 1945 1946 auto result = index.GetVersionKeysById(results.Matches[0].first); 1947 REQUIRE(result.size() == sortedList.size()); 1948 1949 for (size_t i = 0; i < result.size(); ++i) 1950 { 1951 const VersionAndChannel& sortedVAC = sortedList[i]; 1952 const VersionAndChannel& resultVAC = result[i].VersionAndChannel; 1953 1954 INFO(i); 1955 REQUIRE(sortedVAC.GetVersion().ToString() == resultVAC.GetVersion().ToString()); 1956 REQUIRE(sortedVAC.GetChannel().ToString() == resultVAC.GetChannel().ToString()); 1957 } 1958 } 1959 1960 TEST_CASE("SQLiteIndex_PathString_VersionSorting", "[sqliteindex]") 1961 { 1962 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 1963 INFO("Using temporary file named: " << tempFile.GetPath()); 1964 1965 std::vector<VersionAndChannel> sortedList = 1966 { 1967 { UtilityVersion("15.0.0"), Channel("") }, 1968 { UtilityVersion("14.0.0"), Channel("") }, 1969 { UtilityVersion("13.2.0"), Channel("") }, 1970 { UtilityVersion("13.2.0-bugfix"), Channel("") }, 1971 { UtilityVersion("13.0.0"), Channel("") }, 1972 { UtilityVersion("16.0.0"), Channel("alpha") }, 1973 { UtilityVersion("15.8.0"), Channel("alpha") }, 1974 { UtilityVersion("15.1.0"), Channel("beta") }, 1975 }; 1976 1977 SQLiteIndex index = SearchTestSetup(tempFile, { 1978 { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, 1979 { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, 1980 { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, 1981 { "Id", "Name", "Moniker", "13.2.0", "", {}, { "Command" }, "Path4" }, 1982 { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, 1983 { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, 1984 { "Id", "Name", "Moniker", "13.2.0-bugfix", "", { "foo" }, { "com3" }, "Path7" }, 1985 { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, 1986 }); 1987 1988 TestPrepareForRead(index); 1989 1990 if (!AreChannelsSupported(index)) 1991 { 1992 return; 1993 } 1994 1995 SearchRequest request; 1996 request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); 1997 1998 auto results = index.Search(request); 1999 REQUIRE(results.Matches.size() == 1); 2000 2001 auto result = GetPathStringByKey(index, results.Matches[0].first, "", ""); 2002 REQUIRE(result == "Path3"); 2003 2004 result = GetPathStringByKey(index, results.Matches[0].first, "", "alpha"); 2005 REQUIRE(result == "Path2"); 2006 2007 result = GetPathStringByKey(index, results.Matches[0].first, "", "beta"); 2008 REQUIRE(result == "Path5"); 2009 2010 auto nonResult = index.GetManifestIdByKey(results.Matches[0].first, "", "gamma"); 2011 REQUIRE(!nonResult.has_value()); 2012 } 2013 2014 TEST_CASE("SQLiteIndex_PathString_CaseInsensitive", "[sqliteindex]") 2015 { 2016 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2017 INFO("Using temporary file named: " << tempFile.GetPath()); 2018 2019 SQLiteIndex index = SearchTestSetup(tempFile, { 2020 { "Id", "Name", "Moniker", "14.0.0", "", { "foot" }, { "com34" }, "Path1" }, 2021 { "Id", "Name", "Moniker", "16.0.0", "alpha", { "floor" }, { "com3" }, "Path2" }, 2022 { "Id", "Name", "Moniker", "15.0.0", "", {}, { "Command" }, "Path3" }, 2023 { "Id", "Name", "Moniker", "13.2.0-BUGFIX", "", {}, { "Command" }, "Path4" }, 2024 { "Id", "Name", "Moniker", "15.1.0", "beta", { "foo" }, { "com3" }, "Path5" }, 2025 { "Id", "Name", "Moniker", "15.8.0", "alpha", { "foo" }, { "com3" }, "Path6" }, 2026 { "Id", "Name", "Moniker", "13.2.0-bugfix", "beta", { "foo" }, { "com3" }, "Path7" }, 2027 { "Id", "Name", "Moniker", "13.0.0", "", { "foo" }, { "com3" }, "Path8" }, 2028 }); 2029 2030 TestPrepareForRead(index); 2031 2032 if (!AreChannelsSupported(index)) 2033 { 2034 return; 2035 } 2036 2037 SearchRequest request; 2038 request.Filters.emplace_back(PackageMatchField::Id, MatchType::Exact, "Id"); 2039 2040 auto results = index.Search(request); 2041 REQUIRE(results.Matches.size() == 1); 2042 2043 auto result = index.GetManifestIdByKey(results.Matches[0].first, "", "Alpha"); 2044 REQUIRE(result.has_value()); 2045 2046 result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", ""); 2047 REQUIRE(result.has_value()); 2048 2049 result = index.GetManifestIdByKey(results.Matches[0].first, "13.2.0-BugFix", "BETA"); 2050 REQUIRE(result.has_value()); 2051 } 2052 2053 TEST_CASE("SQLiteIndex_SearchResultsTableSearches", "[sqliteindex][V1_0]") 2054 { 2055 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2056 INFO("Using temporary file named: " << tempFile.GetPath()); 2057 2058 Manifest manifest; 2059 { 2060 (void)SimpleTestSetup(tempFile, manifest, SQLiteVersion{ 1, 0 }); 2061 } 2062 2063 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 2064 Schema::V1_0::SearchResultsTable search(connection); 2065 2066 std::string value = "test"; 2067 2068 // Perform every type of field and match search 2069 PackageMatchFilter filter(PackageMatchField::Id, MatchType::Exact, value); 2070 2071 for (auto field : { PackageMatchField::Id, PackageMatchField::Name, PackageMatchField::Moniker, PackageMatchField::Tag, PackageMatchField::Command }) 2072 { 2073 filter.Field = field; 2074 2075 for (auto match : { MatchType::Exact, MatchType::Fuzzy, MatchType::FuzzySubstring, MatchType::Substring, MatchType::Wildcard }) 2076 { 2077 filter.Type = match; 2078 search.SearchOnField(filter); 2079 } 2080 } 2081 } 2082 2083 TEST_CASE("SQLiteIndex_Search_EmptySearch", "[sqliteindex]") 2084 { 2085 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2086 INFO("Using temporary file named: " << tempFile.GetPath()); 2087 2088 SQLiteIndex index = SearchTestSetup(tempFile,{ 2089 { "Id1", "Name", "Moniker", "Version1", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2090 { "Id1", "Name", "Moniker", "Version2", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2091 { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2092 { "Id3", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path4" }, 2093 }); 2094 2095 TestPrepareForRead(index); 2096 2097 SearchRequest request; 2098 2099 auto results = index.Search(request); 2100 REQUIRE(results.Matches.size() == 3); 2101 } 2102 2103 TEST_CASE("SQLiteIndex_Search_Exact", "[sqliteindex]") 2104 { 2105 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2106 INFO("Using temporary file named: " << tempFile.GetPath()); 2107 2108 SQLiteIndex index = SearchTestSetup(tempFile, { 2109 { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2110 { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2111 }); 2112 2113 TestPrepareForRead(index); 2114 2115 SearchRequest request; 2116 request.Query = RequestMatch(MatchType::Exact, "Id"); 2117 2118 auto results = index.Search(request); 2119 REQUIRE(results.Matches.size() == 1); 2120 } 2121 2122 TEST_CASE("SQLiteIndex_Search_Substring", "[sqliteindex]") 2123 { 2124 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2125 INFO("Using temporary file named: " << tempFile.GetPath()); 2126 2127 SQLiteIndex index = SearchTestSetup(tempFile, { 2128 { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2129 { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2130 }); 2131 2132 TestPrepareForRead(index); 2133 2134 SearchRequest request; 2135 request.Query = RequestMatch(MatchType::Substring, "Id"); 2136 2137 auto results = index.Search(request); 2138 REQUIRE(results.Matches.size() == 2); 2139 } 2140 2141 TEST_CASE("SQLiteIndex_Search_ExactBeforeSubstring", "[sqliteindex]") 2142 { 2143 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2144 INFO("Using temporary file named: " << tempFile.GetPath()); 2145 2146 SQLiteIndex index = SearchTestSetup(tempFile, { 2147 { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2148 { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2149 }); 2150 2151 TestPrepareForRead(index); 2152 2153 SearchRequest request; 2154 request.Query = RequestMatch(MatchType::Substring, "Id"); 2155 2156 auto results = index.Search(request); 2157 REQUIRE(results.Matches.size() == 2); 2158 2159 REQUIRE(GetIdStringById(index, results.Matches[0].first) == "Id"); 2160 REQUIRE(GetIdStringById(index, results.Matches[1].first) == "Id2"); 2161 } 2162 2163 TEST_CASE("SQLiteIndex_Search_SingleFilter", "[sqliteindex]") 2164 { 2165 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2166 INFO("Using temporary file named: " << tempFile.GetPath()); 2167 2168 SQLiteIndex index = SearchTestSetup(tempFile, { 2169 { "Id", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2170 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2171 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2172 }); 2173 2174 TestPrepareForRead(index); 2175 2176 SearchRequest request; 2177 request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "a"); 2178 2179 auto results = index.Search(request); 2180 REQUIRE(results.Matches.size() == 2); 2181 2182 request.Filters[0].Value = "e"; 2183 2184 results = index.Search(request); 2185 REQUIRE(results.Matches.size() == 1); 2186 } 2187 2188 TEST_CASE("SQLiteIndex_Search_Multimatch", "[sqliteindex]") 2189 { 2190 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2191 INFO("Using temporary file named: " << tempFile.GetPath()); 2192 2193 SQLiteIndex index = SearchTestSetup(tempFile, { 2194 { "Id1", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2195 { "Id1", "Name1", "Moniker", "Version1", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2196 { "Id2", "Name", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path3" }, 2197 { "Id2", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path4" }, 2198 { "Id3", "Name", "Moniker", "Version1", "", { "Tag" }, { "Command" }, "Path5" }, 2199 { "Id3", "Name", "Moniker", "Version2", "", { "Tag" }, { "Command" }, "Path6" }, 2200 { "Id3", "Name", "Moniker", "Version3", "", { "Tag" }, { "Command" }, "Path7" }, 2201 }); 2202 2203 TestPrepareForRead(index); 2204 2205 SearchRequest request; 2206 // An empty string should match all substrings 2207 request.Query = RequestMatch(MatchType::Substring, ""); 2208 2209 auto results = index.Search(request); 2210 REQUIRE(results.Matches.size() == 3); 2211 } 2212 2213 TEST_CASE("SQLiteIndex_Search_QueryAndFilter", "[sqliteindex]") 2214 { 2215 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2216 INFO("Using temporary file named: " << tempFile.GetPath()); 2217 2218 SQLiteIndex index = SearchTestSetup(tempFile, { 2219 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2220 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2221 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2222 }); 2223 2224 TestPrepareForRead(index); 2225 2226 SearchRequest request; 2227 request.Query = RequestMatch(MatchType::Substring, "Id"); 2228 request.Filters.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); 2229 2230 auto results = index.Search(request); 2231 REQUIRE(results.Matches.size() == 1); 2232 2233 auto result = GetIdStringById(index, results.Matches[0].first); 2234 REQUIRE(result == "Id2"); 2235 } 2236 2237 TEST_CASE("SQLiteIndex_Search_QueryAndMultipleFilters", "[sqliteindex]") 2238 { 2239 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2240 INFO("Using temporary file named: " << tempFile.GetPath()); 2241 2242 SQLiteIndex index = SearchTestSetup(tempFile, { 2243 { "Id1", "Name", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path1" }, 2244 { "Id1", "Name1", "Moniker", "Version1", "Channel", { "floor" }, { "com3" }, "Path2" }, 2245 { "Id2", "Name", "Moniker", "Version", "", {}, { "Command" }, "Path3" }, 2246 { "Id2", "Name", "Moniker", "Version", "Channel", {}, { "Command" }, "Path4" }, 2247 { "Id3", "Tagit", "Moniker", "Version1", "", { "foo" }, { "com3" }, "Path5" }, 2248 { "Id3", "Tagit", "Moniker", "Version2", "", { "foo" }, { "com3" }, "Path6" }, 2249 { "Id3", "Tagit", "new", "Version3", "", { "foo" }, { "com3" }, "Path7" }, 2250 }); 2251 2252 TestPrepareForRead(index); 2253 2254 SearchRequest request; 2255 request.Query = RequestMatch(MatchType::Substring, "tag"); 2256 request.Filters.emplace_back(PackageMatchField::Command, MatchType::Exact, "com3"); 2257 request.Filters.emplace_back(PackageMatchField::Tag, MatchType::Substring, "foo"); 2258 request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::Substring, "new"); 2259 2260 auto results = index.Search(request); 2261 REQUIRE(results.Matches.size() == 1); 2262 2263 auto result = GetIdStringById(index, results.Matches[0].first); 2264 REQUIRE(result == "Id3"); 2265 } 2266 2267 TEST_CASE("SQLiteIndex_Search_SimpleICULike", "[sqliteindex]") 2268 { 2269 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2270 INFO("Using temporary file named: " << tempFile.GetPath()); 2271 2272 // Insert decomposed character: [upper] A + umlaut 2273 SQLiteIndex index = SearchTestSetup(tempFile, { 2274 { u8"\x41\x308wesomeApp", "HasUmlaut", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path1" }, 2275 { u8"AwesomeApp", "Nope", "Moniker", "Version", "Channel", { "foot" }, { "com34" }, "Path2" }, 2276 }); 2277 2278 TestPrepareForRead(index); 2279 2280 SearchRequest request; 2281 // Search for anything containing: [lower] a + umlaut 2282 request.Filters.emplace_back(PackageMatchField::Id, MatchType::Substring, u8"\xE4"); 2283 2284 auto results = index.Search(request); 2285 REQUIRE(results.Matches.size() == 1); 2286 2287 auto result = GetNameStringById(index, results.Matches[0].first); 2288 REQUIRE(result == "HasUmlaut"); 2289 } 2290 2291 TEST_CASE("SQLiteIndex_Search_MaximumResults_Equal", "[sqliteindex]") 2292 { 2293 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2294 INFO("Using temporary file named: " << tempFile.GetPath()); 2295 2296 SQLiteIndex index = SearchTestSetup(tempFile, { 2297 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2298 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2299 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2300 }); 2301 2302 TestPrepareForRead(index); 2303 2304 SearchRequest request; 2305 request.MaximumResults = 3; 2306 2307 auto results = index.Search(request); 2308 REQUIRE(results.Matches.size() == 3); 2309 REQUIRE(!results.Truncated); 2310 } 2311 2312 TEST_CASE("SQLiteIndex_Search_MaximumResults_Less", "[sqliteindex]") 2313 { 2314 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2315 INFO("Using temporary file named: " << tempFile.GetPath()); 2316 2317 SQLiteIndex index = SearchTestSetup(tempFile, { 2318 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2319 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2320 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2321 }); 2322 2323 TestPrepareForRead(index); 2324 2325 SearchRequest request; 2326 request.MaximumResults = 2; 2327 2328 auto results = index.Search(request); 2329 REQUIRE(results.Matches.size() == 2); 2330 REQUIRE(results.Truncated); 2331 } 2332 2333 TEST_CASE("SQLiteIndex_Search_MaximumResults_Greater", "[sqliteindex]") 2334 { 2335 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2336 INFO("Using temporary file named: " << tempFile.GetPath()); 2337 2338 SQLiteIndex index = SearchTestSetup(tempFile, { 2339 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2340 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2341 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2342 }); 2343 2344 TestPrepareForRead(index); 2345 2346 SearchRequest request; 2347 request.MaximumResults = 4; 2348 2349 auto results = index.Search(request); 2350 REQUIRE(results.Matches.size() == 3); 2351 REQUIRE(!results.Truncated); 2352 } 2353 2354 TEST_CASE("SQLiteIndex_Search_QueryAndInclusion", "[sqliteindex]") 2355 { 2356 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2357 INFO("Using temporary file named: " << tempFile.GetPath()); 2358 2359 SQLiteIndex index = SearchTestSetup(tempFile, { 2360 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2361 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2362 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2363 }); 2364 2365 TestPrepareForRead(index); 2366 2367 SearchRequest request; 2368 request.Query = RequestMatch(MatchType::CaseInsensitive, "id3"); 2369 request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); 2370 2371 auto results = index.Search(request); 2372 REQUIRE(results.Matches.size() == 3); 2373 } 2374 2375 TEST_CASE("SQLiteIndex_Search_InclusionOnly", "[sqliteindex]") 2376 { 2377 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2378 INFO("Using temporary file named: " << tempFile.GetPath()); 2379 2380 SQLiteIndex index = SearchTestSetup(tempFile, { 2381 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2382 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2383 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2384 }); 2385 2386 TestPrepareForRead(index); 2387 2388 SearchRequest request; 2389 request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); 2390 2391 auto results = index.Search(request); 2392 REQUIRE(results.Matches.size() == 2); 2393 } 2394 2395 TEST_CASE("SQLiteIndex_Search_InclusionAndFilter", "[sqliteindex]") 2396 { 2397 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2398 INFO("Using temporary file named: " << tempFile.GetPath()); 2399 2400 SQLiteIndex index = SearchTestSetup(tempFile, { 2401 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2402 { "Id2", "Na", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2403 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2404 }); 2405 2406 TestPrepareForRead(index); 2407 2408 SearchRequest request; 2409 request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "Na"); 2410 request.Filters.emplace_back(PackageMatchField::Name, MatchType::CaseInsensitive, "name"); 2411 2412 auto results = index.Search(request); 2413 REQUIRE(results.Matches.size() == 1); 2414 2415 auto result = GetIdStringById(index, results.Matches[0].first); 2416 REQUIRE(result == "Nope"); 2417 } 2418 2419 TEST_CASE("SQLiteIndex_Search_QueryInclusionAndFilter", "[sqliteindex]") 2420 { 2421 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2422 INFO("Using temporary file named: " << tempFile.GetPath()); 2423 2424 SQLiteIndex index = SearchTestSetup(tempFile, { 2425 { "Nope", "Name", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2426 { "Id2", "Na", "monicka", "Version", "Channel", { "Tag" }, { "Command" }, "Path2" }, 2427 { "Id3", "No", "moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2428 }); 2429 2430 TestPrepareForRead(index); 2431 2432 SearchRequest request; 2433 request.Query = RequestMatch(MatchType::Substring, "id3"); 2434 request.Inclusions.emplace_back(PackageMatchField::Name, MatchType::Substring, "na"); 2435 request.Filters.emplace_back(PackageMatchField::Moniker, MatchType::CaseInsensitive, "MONIKER"); 2436 2437 auto results = index.Search(request); 2438 REQUIRE(results.Matches.size() == 2); 2439 } 2440 2441 TEST_CASE("SQLiteIndex_Search_CaseInsensitive", "[sqliteindex]") 2442 { 2443 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2444 INFO("Using temporary file named: " << tempFile.GetPath()); 2445 2446 SQLiteIndex index = SearchTestSetup(tempFile, { 2447 { "Nope", "id3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2448 { "Id2", "Na", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2" }, 2449 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2450 }); 2451 2452 TestPrepareForRead(index); 2453 2454 SearchRequest request; 2455 request.Query = RequestMatch(MatchType::CaseInsensitive, "id3"); 2456 2457 auto results = index.Search(request); 2458 REQUIRE(results.Matches.size() == 3); 2459 } 2460 2461 TEST_CASE("SQLiteIndex_Search_StartsWith", "[sqliteindex]") 2462 { 2463 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2464 INFO("Using temporary file named: " << tempFile.GetPath()); 2465 2466 SQLiteIndex index = SearchTestSetup(tempFile, { 2467 { "NopeId", "id3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1" }, 2468 { "Id2", "Na", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2" }, 2469 { "Id3", "No", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3" }, 2470 }); 2471 2472 TestPrepareForRead(index); 2473 2474 SearchRequest request; 2475 request.Inclusions.push_back(PackageMatchFilter(PackageMatchField::Id, MatchType::StartsWith, "id")); 2476 2477 auto results = index.Search(request); 2478 REQUIRE(results.Matches.size() == 2); 2479 } 2480 2481 TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameSubstring", "[sqliteindex]") 2482 { 2483 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2484 INFO("Using temporary file named: " << tempFile.GetPath()); 2485 2486 SQLiteIndex index = SearchTestSetup(tempFile, { 2487 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2488 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2489 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2490 }); 2491 2492 SQLiteVersion testVersion = TestPrepareForRead(index); 2493 2494 SearchRequest request; 2495 request.Query = RequestMatch(MatchType::Substring, "PFN"); 2496 2497 auto results = index.Search(request); 2498 REQUIRE(results.Matches.size() == 0); 2499 } 2500 2501 TEST_CASE("SQLiteIndex_Search_Query_ProductCodeSubstring", "[sqliteindex]") 2502 { 2503 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2504 INFO("Using temporary file named: " << tempFile.GetPath()); 2505 2506 SQLiteIndex index = SearchTestSetup(tempFile, { 2507 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2508 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2509 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2510 }); 2511 2512 SQLiteVersion testVersion = TestPrepareForRead(index); 2513 2514 SearchRequest request; 2515 request.Query = RequestMatch(MatchType::Substring, "PC"); 2516 2517 auto results = index.Search(request); 2518 REQUIRE(results.Matches.size() == 0); 2519 } 2520 2521 TEST_CASE("SQLiteIndex_Search_Query_PackageFamilyNameMatch", "[sqliteindex]") 2522 { 2523 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2524 INFO("Using temporary file named: " << tempFile.GetPath()); 2525 2526 SQLiteIndex index = SearchTestSetup(tempFile, { 2527 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2528 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2529 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2530 }); 2531 2532 SQLiteVersion testVersion = TestPrepareForRead(index); 2533 2534 SearchRequest request; 2535 request.Query = RequestMatch(MatchType::Substring, "pfn1"); 2536 2537 auto results = index.Search(request); 2538 2539 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2540 { 2541 REQUIRE(results.Matches.size() == 1); 2542 } 2543 else 2544 { 2545 REQUIRE(results.Matches.size() == 0); 2546 } 2547 } 2548 2549 TEST_CASE("SQLiteIndex_Search_Query_ProductCodeMatch", "[sqliteindex]") 2550 { 2551 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2552 INFO("Using temporary file named: " << tempFile.GetPath()); 2553 2554 SQLiteIndex index = SearchTestSetup(tempFile, { 2555 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2556 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2557 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2558 }); 2559 2560 SQLiteVersion testVersion = TestPrepareForRead(index); 2561 2562 SearchRequest request; 2563 request.Query = RequestMatch(MatchType::Substring, "pc2"); 2564 2565 auto results = index.Search(request); 2566 2567 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2568 { 2569 REQUIRE(results.Matches.size() == 1); 2570 } 2571 else 2572 { 2573 REQUIRE(results.Matches.size() == 0); 2574 } 2575 } 2576 2577 TEST_CASE("SQLiteIndex_Search_PackageFamilyNameSubstring", "[sqliteindex]") 2578 { 2579 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2580 INFO("Using temporary file named: " << tempFile.GetPath()); 2581 2582 SQLiteIndex index = SearchTestSetup(tempFile, { 2583 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2584 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2585 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2586 }); 2587 2588 SQLiteVersion testVersion = TestPrepareForRead(index); 2589 2590 SearchRequest request; 2591 request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Substring, "PFN"); 2592 2593 auto results = index.Search(request); 2594 2595 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2596 { 2597 REQUIRE(results.Matches.size() == 3); 2598 } 2599 else 2600 { 2601 REQUIRE(results.Matches.size() == 0); 2602 } 2603 } 2604 2605 TEST_CASE("SQLiteIndex_Search_ProductCodeSubstring", "[sqliteindex]") 2606 { 2607 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2608 INFO("Using temporary file named: " << tempFile.GetPath()); 2609 2610 SQLiteIndex index = SearchTestSetup(tempFile, { 2611 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2612 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2613 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2614 }); 2615 2616 SQLiteVersion testVersion = TestPrepareForRead(index); 2617 2618 SearchRequest request; 2619 request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Substring, "PC"); 2620 2621 auto results = index.Search(request); 2622 2623 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2624 { 2625 REQUIRE(results.Matches.size() == 3); 2626 } 2627 else 2628 { 2629 REQUIRE(results.Matches.size() == 0); 2630 } 2631 } 2632 2633 TEST_CASE("SQLiteIndex_Search_PackageFamilyNameMatch", "[sqliteindex]") 2634 { 2635 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2636 INFO("Using temporary file named: " << tempFile.GetPath()); 2637 2638 SQLiteIndex index = SearchTestSetup(tempFile, { 2639 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2640 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2641 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2642 }); 2643 2644 SQLiteVersion testVersion = TestPrepareForRead(index); 2645 2646 SearchRequest request; 2647 request.Inclusions.emplace_back(PackageMatchField::PackageFamilyName, MatchType::Exact, "pfn1"); 2648 2649 auto results = index.Search(request); 2650 2651 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2652 { 2653 REQUIRE(results.Matches.size() == 1); 2654 } 2655 else 2656 { 2657 REQUIRE(results.Matches.size() == 0); 2658 } 2659 } 2660 2661 TEST_CASE("SQLiteIndex_Search_ProductCodeMatch", "[sqliteindex]") 2662 { 2663 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2664 INFO("Using temporary file named: " << tempFile.GetPath()); 2665 2666 SQLiteIndex index = SearchTestSetup(tempFile, { 2667 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 2668 { "Id2", "Name2", "Moniker", "Version", "Channel", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 2669 { "Id3", "Name3", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 2670 }); 2671 2672 SQLiteVersion testVersion = TestPrepareForRead(index); 2673 2674 SearchRequest request; 2675 request.Inclusions.emplace_back(PackageMatchField::ProductCode, MatchType::Exact, "pc2"); 2676 2677 auto results = index.Search(request); 2678 2679 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2680 { 2681 REQUIRE(results.Matches.size() == 1); 2682 } 2683 else 2684 { 2685 REQUIRE(results.Matches.size() == 0); 2686 } 2687 } 2688 2689 TEST_CASE("SQLiteIndex_CheckConsistency_Failure", "[sqliteindex][V1_1]") 2690 { 2691 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2692 INFO("Using temporary file named: " << tempFile.GetPath()); 2693 2694 std::string manifest1Path = "test/id/test.id-1.0.0.yaml"; 2695 Manifest manifest1; 2696 manifest1.Installers.push_back({}); 2697 manifest1.Id = "test.id"; 2698 manifest1.DefaultLocalization.Add<Localization::PackageName>("Test Name"); 2699 manifest1.Moniker = "testmoniker"; 2700 manifest1.Version = "1.0.0"; 2701 manifest1.Channel = "test"; 2702 manifest1.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 2703 manifest1.Installers[0].Commands = { "test1", "test2" }; 2704 2705 std::string manifest2Path = "test/woah/test.id-1.0.0.yaml"; 2706 Manifest manifest2; 2707 manifest2.Installers.push_back({}); 2708 manifest2.Id = "test.woah"; 2709 manifest2.DefaultLocalization.Add<Localization::PackageName>("Test Name WOAH"); 2710 manifest2.Moniker = "testmoniker"; 2711 manifest2.Version = "1.0.0"; 2712 manifest2.Channel = "test"; 2713 manifest2.DefaultLocalization.Add<Localization::Tags>({}); 2714 manifest2.Installers[0].Commands = { "test1", "test2", "test3" }; 2715 2716 rowid_t manifestRowId = 0; 2717 2718 { 2719 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, { 1, 1 }); 2720 2721 index.AddManifest(manifest1, manifest1Path); 2722 index.AddManifest(manifest2, manifest2Path); 2723 2724 // Get the first manifest's id for removal 2725 SearchRequest request; 2726 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Id, MatchType::Exact, manifest1.Id)); 2727 auto result = index.Search(request); 2728 2729 REQUIRE(result.Matches.size() == 1); 2730 manifestRowId = result.Matches[0].first; 2731 } 2732 2733 { 2734 // Open it directly to modify the table 2735 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 2736 2737 Builder::StatementBuilder builder; 2738 builder.DeleteFrom(Schema::V1_0::IdTable::TableName()).Where(RowIDName).Equals(manifestRowId); 2739 builder.Execute(connection); 2740 } 2741 2742 { 2743 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 2744 2745 REQUIRE(!index.CheckConsistency(true)); 2746 } 2747 } 2748 2749 TEST_CASE("SQLiteIndex_GetMultiProperty_PackageFamilyName", "[sqliteindex]") 2750 { 2751 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2752 INFO("Using temporary file named: " << tempFile.GetPath()); 2753 2754 SQLiteIndex index = SearchTestSetup(tempFile, { 2755 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", { "PFN1", "PFN2" }, {} }, 2756 }); 2757 2758 SQLiteVersion testVersion = TestPrepareForRead(index); 2759 2760 SearchRequest request; 2761 2762 auto results = index.Search(request); 2763 REQUIRE(results.Matches.size() == 1); 2764 2765 auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::PackageFamilyName); 2766 2767 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2768 { 2769 REQUIRE(props.size() == 2); 2770 REQUIRE(std::find(props.begin(), props.end(), FoldCase("PFN1"sv)) != props.end()); 2771 REQUIRE(std::find(props.begin(), props.end(), FoldCase("PFN2"sv)) != props.end()); 2772 } 2773 else 2774 { 2775 REQUIRE(props.empty()); 2776 } 2777 } 2778 2779 TEST_CASE("SQLiteIndex_GetMultiProperty_ProductCode", "[sqliteindex]") 2780 { 2781 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2782 INFO("Using temporary file named: " << tempFile.GetPath()); 2783 2784 SQLiteIndex index = SearchTestSetup(tempFile, { 2785 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2786 }); 2787 2788 SQLiteVersion testVersion = TestPrepareForRead(index); 2789 2790 SearchRequest request; 2791 2792 auto results = index.Search(request); 2793 REQUIRE(results.Matches.size() == 1); 2794 2795 auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::ProductCode); 2796 2797 if (ArePackageFamilyNameAndProductCodeSupported(index, testVersion)) 2798 { 2799 REQUIRE(props.size() == 2); 2800 REQUIRE(std::find(props.begin(), props.end(), FoldCase("PC1"sv)) != props.end()); 2801 REQUIRE(std::find(props.begin(), props.end(), FoldCase("PC2"sv)) != props.end()); 2802 } 2803 else 2804 { 2805 REQUIRE(props.empty()); 2806 } 2807 } 2808 2809 TEST_CASE("SQLiteIndex_GetMultiProperty_Tag", "[sqliteindex]") 2810 { 2811 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2812 INFO("Using temporary file named: " << tempFile.GetPath()); 2813 2814 SQLiteIndex index = SearchTestSetup(tempFile, { 2815 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag1", "Tag2" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2816 }); 2817 2818 SQLiteVersion testVersion = TestPrepareForRead(index); 2819 2820 SearchRequest request; 2821 2822 auto results = index.Search(request); 2823 REQUIRE(results.Matches.size() == 1); 2824 2825 auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::Tag); 2826 2827 REQUIRE(props.size() == 2); 2828 REQUIRE(std::find(props.begin(), props.end(), "Tag1") != props.end()); 2829 REQUIRE(std::find(props.begin(), props.end(), "Tag2") != props.end()); 2830 } 2831 2832 TEST_CASE("SQLiteIndex_GetMultiProperty_Command", "[sqliteindex]") 2833 { 2834 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2835 INFO("Using temporary file named: " << tempFile.GetPath()); 2836 2837 SQLiteIndex index = SearchTestSetup(tempFile, { 2838 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag1", "Tag2" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2839 }); 2840 2841 SQLiteVersion testVersion = TestPrepareForRead(index); 2842 2843 SearchRequest request; 2844 2845 auto results = index.Search(request); 2846 REQUIRE(results.Matches.size() == 1); 2847 2848 auto props = index.GetMultiPropertyByPrimaryId(results.Matches[0].first, PackageVersionMultiProperty::Command); 2849 2850 REQUIRE(props.size() == 1); 2851 REQUIRE(props[0] == "Command"); 2852 } 2853 2854 TEST_CASE("SQLiteIndex_ManifestMetadata", "[sqliteindex][V1_7]") 2855 { 2856 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2857 INFO("Using temporary file named: " << tempFile.GetPath()); 2858 2859 SQLiteIndex index = SearchTestSetup(tempFile, { 2860 { "Id1", "Name1", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2861 { "Id2", "Name2", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", { "PFN1", "PFN2" }, {} }, 2862 }, SQLiteVersion{ 1, 7 }); 2863 2864 SQLiteVersion testVersion = TestPrepareForRead(index); 2865 2866 SearchRequest request; 2867 2868 auto results = index.Search(request); 2869 REQUIRE(results.Matches.size() == 2); 2870 2871 for (const auto [id, match] : results.Matches) 2872 { 2873 REQUIRE(index.GetMetadataByManifestId(id).empty()); 2874 } 2875 2876 auto manifestId1 = results.Matches[0].first; 2877 auto manifestId2 = results.Matches[1].first; 2878 2879 std::string metadataValue = "data about data"; 2880 2881 index.SetMetadataByManifestId(manifestId1, PackageVersionMetadata::InstalledType, metadataValue); 2882 2883 if (IsManifestMetadataSupported(index, testVersion)) 2884 { 2885 auto metadataResult = index.GetMetadataByManifestId(manifestId1); 2886 REQUIRE(metadataResult.size() == 1); 2887 REQUIRE(metadataResult[0].first == PackageVersionMetadata::InstalledType); 2888 REQUIRE(metadataResult[0].second == metadataValue); 2889 } 2890 else 2891 { 2892 REQUIRE(index.GetMetadataByManifestId(manifestId1).empty()); 2893 } 2894 2895 REQUIRE(index.GetMetadataByManifestId(manifestId2).empty()); 2896 } 2897 2898 TEST_CASE("SQLiteIndex_NormNameAndPublisher_Exact", "[sqliteindex]") 2899 { 2900 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2901 INFO("Using temporary file named: " << tempFile.GetPath()); 2902 2903 std::string testName = "Name"; 2904 std::string testPublisher = "Publisher"; 2905 2906 SQLiteIndex index = SearchTestSetup(tempFile, { 2907 { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2908 }); 2909 2910 SQLiteVersion testVersion = TestPrepareForRead(index); 2911 2912 SearchRequest request; 2913 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName, testPublisher)); 2914 2915 auto results = index.Search(request); 2916 2917 if (AreNormalizedNameAndPublisherSupported(index, testVersion)) 2918 { 2919 REQUIRE(results.Matches.size() == 1); 2920 } 2921 else 2922 { 2923 REQUIRE(results.Matches.empty()); 2924 } 2925 } 2926 2927 TEST_CASE("SQLiteIndex_NormNameAndPublisher_Simple", "[sqliteindex]") 2928 { 2929 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2930 INFO("Using temporary file named: " << tempFile.GetPath()); 2931 2932 std::string testName = "Name"; 2933 std::string testPublisher = "Publisher"; 2934 2935 SQLiteIndex index = SearchTestSetup(tempFile, { 2936 { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2937 }); 2938 2939 SQLiteVersion testVersion = TestPrepareForRead(index); 2940 2941 SearchRequest request; 2942 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher + " Corporation")); 2943 2944 auto results = index.Search(request); 2945 2946 if (AreNormalizedNameAndPublisherSupported(index, testVersion)) 2947 { 2948 REQUIRE(results.Matches.size() == 1); 2949 } 2950 else 2951 { 2952 REQUIRE(results.Matches.empty()); 2953 } 2954 } 2955 2956 TEST_CASE("SQLiteIndex_NormNameAndPublisher_Complex", "[sqliteindex]") 2957 { 2958 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2959 INFO("Using temporary file named: " << tempFile.GetPath()); 2960 2961 std::string testName = "Name"; 2962 std::string testPublisher = "Publisher"; 2963 2964 SQLiteIndex index = SearchTestSetup(tempFile, { 2965 { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" } }, 2966 { "Id2", testName, "Different Publisher", "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path2", {}, { "PC1", "PC2" } }, 2967 }); 2968 2969 SQLiteVersion testVersion = TestPrepareForRead(index); 2970 2971 SearchRequest request; 2972 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, testName + " 1.0", testPublisher)); 2973 2974 auto results = index.Search(request); 2975 2976 if (AreNormalizedNameAndPublisherSupported(index, testVersion)) 2977 { 2978 REQUIRE(results.Matches.size() == 1); 2979 } 2980 else 2981 { 2982 REQUIRE(results.Matches.empty()); 2983 } 2984 } 2985 2986 TEST_CASE("SQLiteIndex_NormNameAndPublisher_AppsAndFeatures", "[sqliteindex]") 2987 { 2988 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 2989 INFO("Using temporary file named: " << tempFile.GetPath()); 2990 2991 std::string testName = "Name"; 2992 std::string testPublisher = "Publisher"; 2993 std::string arpTestName = "Other Thing"; 2994 std::string arpTestPublisher = "Big Company Name"; 2995 2996 SQLiteIndex index = SearchTestSetup(tempFile, { 2997 { "Id1", testName, testPublisher, "Moniker", "Version", "Channel", { "Tag" }, { "Command" }, "Path1", {}, { "PC1", "PC2" }, arpTestName, arpTestPublisher }, 2998 }); 2999 3000 SQLiteVersion testVersion = TestPrepareForRead(index); 3001 3002 SearchRequest request; 3003 request.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::NormalizedNameAndPublisher, MatchType::Exact, arpTestName, arpTestPublisher)); 3004 3005 auto results = index.Search(request); 3006 3007 if (AreNormalizedNameAndPublisherSupported(index, testVersion)) 3008 { 3009 REQUIRE(results.Matches.size() == 1); 3010 } 3011 else 3012 { 3013 REQUIRE(results.Matches.empty()); 3014 } 3015 } 3016 3017 TEST_CASE("SQLiteIndex_ManifestHash_Present", "[sqliteindex]") 3018 { 3019 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3020 INFO("Using temporary file named: " << tempFile.GetPath()); 3021 3022 uint8_t data[4] = { 1, 2, 3, 4 }; 3023 SHA256::HashBuffer hash = SHA256::ComputeHash(data, sizeof(data)); 3024 3025 SQLiteIndex index = CreateTestIndex(tempFile); 3026 3027 Manifest manifest; 3028 manifest.Id = "Foo"; 3029 manifest.Version = "Bar"; 3030 manifest.StreamSha256 = hash; 3031 index.AddManifest(manifest, "path"); 3032 3033 SQLiteVersion testVersion = TestPrepareForRead(index); 3034 3035 auto results = index.Search({}); 3036 REQUIRE(results.Matches.size() == 1); 3037 3038 auto hashResult = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ManifestSHA256Hash); 3039 3040 // Regardless of what hash, it should still be a SHA256 hash 3041 REQUIRE(hashResult); 3042 auto hashResultBytes = SHA256::ConvertToBytes(hashResult.value()); 3043 REQUIRE(hash.size() == hashResultBytes.size()); 3044 3045 if (AreManifestHashesSupported(index, testVersion)) 3046 { 3047 REQUIRE(std::equal(hash.begin(), hash.end(), hashResultBytes.begin())); 3048 } 3049 } 3050 3051 TEST_CASE("SQLiteIndex_ManifestHash_Missing", "[sqliteindex]") 3052 { 3053 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3054 INFO("Using temporary file named: " << tempFile.GetPath()); 3055 3056 SQLiteIndex index = CreateTestIndex(tempFile); 3057 3058 Manifest manifest; 3059 manifest.Id = "Foo"; 3060 manifest.Version = "Bar"; 3061 index.AddManifest(manifest, "path"); 3062 3063 SQLiteVersion testVersion = TestPrepareForRead(index); 3064 3065 auto results = index.Search({}); 3066 REQUIRE(results.Matches.size() == 1); 3067 3068 auto hashResult = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ManifestSHA256Hash); 3069 3070 if (AreManifestHashesSupported(index, testVersion)) 3071 { 3072 REQUIRE(!hashResult); 3073 } 3074 } 3075 3076 TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_Add", "[sqliteindex]") 3077 { 3078 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3079 INFO("Using temporary file named: " << tempFile.GetPath()); 3080 3081 SQLiteIndex index = CreateTestIndex(tempFile); 3082 3083 Manifest manifest; 3084 manifest.Id = "Foo"; 3085 manifest.Version = "Bar"; 3086 manifest.Installers.push_back({}); 3087 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3088 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3089 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3090 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3091 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3092 3093 index.AddManifest(manifest, "path"); 3094 3095 SQLiteVersion testVersion = TestPrepareForRead(index); 3096 3097 auto results = index.Search({}); 3098 REQUIRE(results.Matches.size() == 1); 3099 3100 auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); 3101 auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); 3102 3103 if (AreArpVersionsSupported(index, testVersion)) 3104 { 3105 REQUIRE(arpMin); 3106 REQUIRE(UtilityVersion(arpMin.value()) == UtilityVersion(manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion)); 3107 REQUIRE(arpMax); 3108 REQUIRE(UtilityVersion(arpMax.value()) == UtilityVersion(manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion)); 3109 } 3110 else 3111 { 3112 REQUIRE_FALSE(arpMin); 3113 REQUIRE_FALSE(arpMax); 3114 } 3115 } 3116 3117 TEST_CASE("SQLiteIndex_ManifestArpVersion_Present_AddThenUpdate", "[sqliteindex]") 3118 { 3119 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3120 INFO("Using temporary file named: " << tempFile.GetPath()); 3121 3122 SQLiteIndex index = CreateTestIndex(tempFile); 3123 3124 Manifest manifest; 3125 manifest.Id = "Foo"; 3126 manifest.Version = "Bar"; 3127 manifest.Installers.push_back({}); 3128 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3129 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3130 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3131 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3132 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3133 3134 index.AddManifest(manifest, "path"); 3135 3136 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.1"; 3137 3138 index.UpdateManifest(manifest, "path"); 3139 3140 SQLiteVersion testVersion = TestPrepareForRead(index); 3141 3142 auto results = index.Search({}); 3143 REQUIRE(results.Matches.size() == 1); 3144 3145 auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); 3146 auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); 3147 3148 if (AreArpVersionsSupported(index, testVersion)) 3149 { 3150 REQUIRE(arpMin); 3151 REQUIRE(arpMin.value() == "1.1"); 3152 REQUIRE(arpMax); 3153 REQUIRE(arpMax.value() == "1.1"); 3154 } 3155 else 3156 { 3157 REQUIRE_FALSE(arpMin); 3158 REQUIRE_FALSE(arpMax); 3159 } 3160 } 3161 3162 TEST_CASE("SQLiteIndex_ManifestArpVersion_Empty", "[sqliteindex]") 3163 { 3164 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3165 INFO("Using temporary file named: " << tempFile.GetPath()); 3166 3167 SQLiteIndex index = CreateTestIndex(tempFile); 3168 3169 Manifest manifest; 3170 manifest.Id = "Foo"; 3171 manifest.Version = "Bar"; 3172 index.AddManifest(manifest, "path"); 3173 3174 SQLiteVersion testVersion = TestPrepareForRead(index); 3175 3176 auto results = index.Search({}); 3177 REQUIRE(results.Matches.size() == 1); 3178 3179 auto arpMin = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMinVersion); 3180 auto arpMax = index.GetPropertyByPrimaryId(results.Matches[0].first, PackageVersionProperty::ArpMaxVersion); 3181 3182 if (AreArpVersionsSupported(index, testVersion) && !AreArpVersionsNullable(index)) 3183 { 3184 REQUIRE(arpMin); 3185 REQUIRE(arpMin.value() == ""); 3186 REQUIRE(arpMax); 3187 REQUIRE(arpMax.value() == ""); 3188 } 3189 else 3190 { 3191 REQUIRE_FALSE(arpMin); 3192 REQUIRE_FALSE(arpMax); 3193 } 3194 } 3195 3196 TEST_CASE("SQLiteIndex_RemoveManifestArpVersionKeepUsedDeleteUnused", "[sqliteindex]") 3197 { 3198 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3199 INFO("Using temporary file named: " << tempFile.GetPath()); 3200 3201 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); 3202 3203 Manifest manifest; 3204 manifest.Id = "Foo"; 3205 manifest.Version = "10.0"; 3206 manifest.Installers.push_back({}); 3207 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3208 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3209 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3210 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3211 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3212 3213 index.AddManifest(manifest, "path"); 3214 3215 Manifest manifest2; 3216 manifest2.Id = "Foo2"; 3217 manifest2.Version = "1.0"; 3218 manifest2.Installers.push_back({}); 3219 manifest2.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3220 manifest2.Installers[0].AppsAndFeaturesEntries.push_back({}); 3221 manifest2.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "10.0"; 3222 3223 index.AddManifest(manifest2, "path2"); 3224 3225 // Before removing, "10.0", "1.0" and "1.1" should all exist. 3226 { 3227 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 3228 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "10.0").has_value()); 3229 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.0").has_value()); 3230 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.1").has_value()); 3231 } 3232 3233 index.RemoveManifest(manifest); 3234 3235 // After removing the first manifest, "10.0" and "1.0" should still stay, "1.1" should be removed. 3236 { 3237 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 3238 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "10.0").has_value()); 3239 REQUIRE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.0").has_value()); 3240 REQUIRE_FALSE(Schema::V1_0::VersionTable::SelectIdByValue(connection, "1.1").has_value()); 3241 } 3242 } 3243 3244 TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_AddThrows", "[sqliteindex]") 3245 { 3246 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3247 INFO("Using temporary file named: " << tempFile.GetPath()); 3248 3249 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); 3250 3251 Manifest manifest; 3252 manifest.Id = "Foo"; 3253 manifest.Version = "10.0"; 3254 manifest.DefaultLocalization.Add<Localization::PackageName>("ArpVersionCheckConsistencyTest"); 3255 manifest.Moniker = "testmoniker"; 3256 manifest.Installers.push_back({}); 3257 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3258 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3259 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3260 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3261 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3262 3263 index.AddManifest(manifest, "path"); 3264 3265 REQUIRE(index.CheckConsistency(true)); 3266 3267 // Add a conflicting one 3268 manifest.Version = "10.1"; 3269 3270 REQUIRE_THROWS_HR(index.AddManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); 3271 } 3272 3273 TEST_CASE("SQLiteIndex_ManifestArpVersionConflict_UpdateThrows", "[sqliteindex]") 3274 { 3275 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3276 INFO("Using temporary file named: " << tempFile.GetPath()); 3277 3278 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); 3279 3280 Manifest manifest; 3281 manifest.Id = "Foo"; 3282 manifest.Version = "10.0"; 3283 manifest.DefaultLocalization.Add<Localization::PackageName>("ArpVersionCheckConsistencyTest"); 3284 manifest.Moniker = "testmoniker"; 3285 manifest.Installers.push_back({}); 3286 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3287 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3288 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3289 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3290 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3291 3292 index.AddManifest(manifest, "path"); 3293 REQUIRE(index.CheckConsistency(true)); 3294 3295 // Add another version 3296 manifest.Version = "10.1"; 3297 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "2.0"; 3298 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; 3299 3300 index.AddManifest(manifest, "path2"); 3301 REQUIRE(index.CheckConsistency(true)); 3302 3303 // Update to a conflict 3304 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3305 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "2.1"; 3306 3307 REQUIRE_THROWS_HR(index.UpdateManifest(manifest, "path2"), APPINSTALLER_CLI_ERROR_ARP_VERSION_VALIDATION_FAILED); 3308 } 3309 3310 TEST_CASE("SQLiteIndex_ManifestArpVersion_ValidateManifestAgainstIndex", "[sqliteindex]") 3311 { 3312 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3313 INFO("Using temporary file named: " << tempFile.GetPath()); 3314 3315 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); 3316 3317 Manifest manifest; 3318 manifest.Id = "Foo"; 3319 manifest.Version = "10.0"; 3320 manifest.Installers.push_back({}); 3321 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3322 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3323 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3324 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3325 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3326 3327 index.AddManifest(manifest, "path"); 3328 3329 // Updating same version should not result in failure. 3330 REQUIRE_NOTHROW(ValidateManifestArpVersion(&index, manifest)); 3331 3332 // Add different version should result in failure. 3333 manifest.Version = "10.1"; 3334 REQUIRE_THROWS(ValidateManifestArpVersion(&index, manifest)); 3335 } 3336 3337 TEST_CASE("SQLiteIndex_CheckConsistency_FindEmbeddedNull", "[sqliteindex]") 3338 { 3339 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3340 INFO("Using temporary file named: " << tempFile.GetPath()); 3341 3342 SQLiteIndex index = CreateTestIndex(tempFile, SQLiteVersion::Latest()); 3343 3344 Manifest manifest; 3345 manifest.Id = "Foo"; 3346 manifest.Version = "10.0"; 3347 manifest.Installers.push_back({}); 3348 manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3349 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3350 manifest.Installers[0].AppsAndFeaturesEntries[0].DisplayVersion = "1.0"; 3351 manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3352 manifest.Installers[0].AppsAndFeaturesEntries[1].DisplayVersion = "1.1"; 3353 3354 index.AddManifest(manifest, "path"); 3355 3356 // Inject a null character using SQL without binding since we block it 3357 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 3358 Statement update = Statement::Create(connection, "Update versions set version = '10.0'||char(0)||'After Null' where version = '10.0'"); 3359 update.Execute(); 3360 3361 REQUIRE(!index.CheckConsistency(true)); 3362 } 3363 3364 TEST_CASE("SQLiteIndex_MapDataFolding_Tags", "[sqliteindex][mapdatafolding]") 3365 { 3366 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3367 INFO("Using temporary file named: " << tempFile.GetPath()); 3368 3369 std::string tag1 = "Tag1"; 3370 std::string tag2 = "Tag2"; 3371 3372 SQLiteIndex index = SearchTestSetup(tempFile, { 3373 { "Id", "Name", "Publisher", "Moniker", "Version1", "", { tag1 }, { "Command" }, "Path1", {}, { "PC1" } }, 3374 { "Id", "Name", "Publisher", "Moniker", "Version2", "", { tag2 }, { "Command" }, "Path2", {}, { "PC2" } }, 3375 }); 3376 3377 SQLiteVersion testVersion = TestPrepareForRead(index); 3378 3379 SearchRequest request1; 3380 request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Tag, MatchType::Exact, tag1)); 3381 auto results1 = index.Search(request1); 3382 3383 SearchRequest request2; 3384 request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::Tag, MatchType::Exact, tag2)); 3385 auto results2 = index.Search(request2); 3386 3387 REQUIRE(results1.Matches.size() == 1); 3388 REQUIRE(results2.Matches.size() == 1); 3389 REQUIRE(results1.Matches[0].first == results2.Matches[0].first); 3390 } 3391 3392 TEST_CASE("SQLiteIndex_MapDataFolding_PFNs", "[sqliteindex][mapdatafolding]") 3393 { 3394 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3395 INFO("Using temporary file named: " << tempFile.GetPath()); 3396 3397 std::string pfn1 = "PFN1"; 3398 std::string pfn2 = "PFN2"; 3399 3400 SQLiteIndex index = SearchTestSetup(tempFile, { 3401 { "Id", "Name", "Publisher", "Moniker", "Version1", "", { }, { "Command" }, "Path1", { pfn1 }, { } }, 3402 { "Id", "Name", "Publisher", "Moniker", "Version2", "", { }, { "Command" }, "Path2", { pfn2 }, { } }, 3403 }); 3404 3405 SQLiteVersion testVersion = TestPrepareForRead(index); 3406 3407 if (!AreChannelsSupported(index)) 3408 { 3409 return; 3410 } 3411 3412 SearchRequest request1; 3413 request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pfn1)); 3414 auto results1 = index.Search(request1); 3415 3416 SearchRequest request2; 3417 request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::PackageFamilyName, MatchType::Exact, pfn2)); 3418 auto results2 = index.Search(request2); 3419 3420 REQUIRE(results1.Matches.size() == 1); 3421 REQUIRE(results2.Matches.size() == 1); 3422 REQUIRE(results1.Matches[0].first == results2.Matches[0].first); 3423 3424 auto versionKeys = index.GetVersionKeysById(results1.Matches[0].first); 3425 REQUIRE(versionKeys.size() == 2); 3426 3427 auto manifestId1 = versionKeys[0].ManifestId; 3428 auto manifestId2 = versionKeys[1].ManifestId; 3429 3430 auto pfnValues1 = index.GetMultiPropertyByPrimaryId(manifestId1, PackageVersionMultiProperty::PackageFamilyName); 3431 auto pfnValues2 = index.GetMultiPropertyByPrimaryId(manifestId2, PackageVersionMultiProperty::PackageFamilyName); 3432 3433 if (IsMapDataFoldingSupported(index, testVersion)) 3434 { 3435 REQUIRE(pfnValues1.size() == 2); 3436 REQUIRE(pfnValues2.size() == 2); 3437 REQUIRE(pfnValues1[0] != pfnValues1[1]); 3438 } 3439 else if (IsMapDataFolded(index)) 3440 { 3441 if (manifestId1 > manifestId2) 3442 { 3443 REQUIRE(pfnValues1.size() == 2); 3444 REQUIRE(pfnValues2.size() == 0); 3445 REQUIRE(pfnValues1[0] != pfnValues1[1]); 3446 } 3447 else 3448 { 3449 REQUIRE(pfnValues1.size() == 0); 3450 REQUIRE(pfnValues2.size() == 2); 3451 REQUIRE(pfnValues2[0] != pfnValues2[1]); 3452 } 3453 } 3454 else 3455 { 3456 REQUIRE(pfnValues1.size() == 1); 3457 REQUIRE(pfnValues2.size() == 1); 3458 REQUIRE(pfnValues1[0] != pfnValues2[0]); 3459 } 3460 } 3461 3462 TEST_CASE("SQLiteIndex_MapDataFolding_ProductCodes", "[sqliteindex][mapdatafolding]") 3463 { 3464 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3465 INFO("Using temporary file named: " << tempFile.GetPath()); 3466 3467 std::string pc1 = "PC1"; 3468 std::string pc2 = "PC2"; 3469 3470 SQLiteIndex index = SearchTestSetup(tempFile, { 3471 { "Id", "Name", "Publisher", "Moniker", "Version1", "", { }, { "Command" }, "Path1", { }, { pc1 } }, 3472 { "Id", "Name", "Publisher", "Moniker", "Version2", "", { }, { "Command" }, "Path2", { }, { pc2 } }, 3473 }); 3474 3475 SQLiteVersion testVersion = TestPrepareForRead(index); 3476 3477 if (!AreChannelsSupported(index)) 3478 { 3479 return; 3480 } 3481 3482 SearchRequest request1; 3483 request1.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pc1)); 3484 auto results1 = index.Search(request1); 3485 3486 SearchRequest request2; 3487 request2.Inclusions.emplace_back(PackageMatchFilter(PackageMatchField::ProductCode, MatchType::Exact, pc2)); 3488 auto results2 = index.Search(request2); 3489 3490 REQUIRE(results1.Matches.size() == 1); 3491 REQUIRE(results2.Matches.size() == 1); 3492 REQUIRE(results1.Matches[0].first == results2.Matches[0].first); 3493 3494 auto versionKeys = index.GetVersionKeysById(results1.Matches[0].first); 3495 REQUIRE(versionKeys.size() == 2); 3496 3497 auto manifestId1 = versionKeys[0].ManifestId; 3498 auto manifestId2 = versionKeys[1].ManifestId; 3499 3500 auto pcValues1 = index.GetMultiPropertyByPrimaryId(manifestId1, PackageVersionMultiProperty::ProductCode); 3501 auto pcValues2 = index.GetMultiPropertyByPrimaryId(manifestId2, PackageVersionMultiProperty::ProductCode); 3502 3503 REQUIRE(pcValues1.size() == 1); 3504 REQUIRE(pcValues2.size() == 1); 3505 REQUIRE(pcValues1[0] != pcValues2[0]); 3506 } 3507 3508 TEST_CASE("SQLiteIndex_FilePath_Memory", "[sqliteindex]") 3509 { 3510 SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET); 3511 auto contextData = index.GetContextData(); 3512 REQUIRE(!contextData.Contains(Schema::Property::DatabaseFilePath)); 3513 } 3514 3515 TEST_CASE("SQLiteIndex_FilePath_Create", "[sqliteindex]") 3516 { 3517 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3518 3519 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile); 3520 auto contextData = index.GetContextData(); 3521 REQUIRE(contextData.Contains(Schema::Property::DatabaseFilePath)); 3522 REQUIRE(contextData.Get<Schema::Property::DatabaseFilePath>() == tempFile.GetPath()); 3523 } 3524 3525 TEST_CASE("SQLiteIndex_FilePath_Open", "[sqliteindex]") 3526 { 3527 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3528 3529 { 3530 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile); 3531 } 3532 3533 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); 3534 auto contextData = index.GetContextData(); 3535 REQUIRE(contextData.Contains(Schema::Property::DatabaseFilePath)); 3536 REQUIRE(contextData.Get<Schema::Property::DatabaseFilePath>() == tempFile.GetPath()); 3537 } 3538 3539 TEST_CASE("SQLiteIndex_MigrateTo_Unsupported", "[sqliteindex][V1_7]") 3540 { 3541 SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET, SQLiteVersion{ 1, 6 }); 3542 REQUIRE(!index.MigrateTo(SQLiteVersion{ 1, 7 })); 3543 } 3544 3545 TEST_CASE("SQLiteIndex_MigrateTo_Empty", "[sqliteindex][V2_0]") 3546 { 3547 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3548 INFO("Using temporary file named: " << tempFile.GetPath()); 3549 3550 { 3551 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, SQLiteVersion{ 1, 7 }); 3552 REQUIRE(index.MigrateTo(SQLiteVersion{ 2, 0 })); 3553 REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); 3554 } 3555 3556 { 3557 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); 3558 REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); 3559 } 3560 } 3561 3562 TEST_CASE("SQLiteIndex_MigrateTo_Data", "[sqliteindex][V2_0]") 3563 { 3564 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3565 INFO("Using temporary file named: " << tempFile.GetPath()); 3566 3567 std::string packageId1 = "Id1"; 3568 std::string packageId2 = "Id2"; 3569 std::string packageId3 = "Id3"; 3570 3571 SQLiteIndex index = SearchTestSetup(tempFile, { 3572 { packageId1, "Name1", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path1", { "PFN1" }, { "PC1" } }, 3573 { packageId2, "Name2", "Moniker", "Version", "", { "ID3" }, { "Command" }, "Path2", { "PFN2" }, { "PC2" } }, 3574 { packageId3, "Name3", "Moniker", "Version", "", { "Tag" }, { "Command" }, "Path3", { "PFN3" }, { "PC3" } }, 3575 }); 3576 3577 auto preMigrationVersion = index.GetVersion(); 3578 3579 if (preMigrationVersion == SQLiteVersion{ 1, 7 }) 3580 { 3581 REQUIRE(index.MigrateTo(SQLiteVersion{ 2, 0 })); 3582 REQUIRE(index.GetVersion() == SQLiteVersion{ 2, 0 }); 3583 3584 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 3585 auto updateData = Schema::V2_0::PackageUpdateTrackingTable::GetUpdatesSince(connection, 0); 3586 3587 REQUIRE(updateData.size() == 3); 3588 REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId1; }) == 1); 3589 REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId2; }) == 1); 3590 REQUIRE(std::count_if(updateData.begin(), updateData.end(), [&](const auto& x) { return x.PackageIdentifier == packageId3; }) == 1); 3591 } 3592 else 3593 { 3594 REQUIRE(!index.MigrateTo(SQLiteVersion{ 2, 0 })); 3595 REQUIRE(index.GetVersion() == preMigrationVersion); 3596 } 3597 } 3598 3599 TEST_CASE("SQLiteIndex_Property_IntermediateFilePath", "[sqliteindex]") 3600 { 3601 SQLiteIndex index = SQLiteIndex::CreateNew(SQLITE_MEMORY_DB_CONNECTION_TARGET); 3602 std::filesystem::path intermediateFilePath = "A:\\Path"; 3603 index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediateFilePath.u8string()); 3604 3605 auto contextData = index.GetContextData(); 3606 REQUIRE(contextData.Contains(Schema::Property::IntermediateFileOutputPath)); 3607 REQUIRE(contextData.Get<Schema::Property::IntermediateFileOutputPath>() == intermediateFilePath); 3608 } 3609 3610 struct ManifestAndPath 3611 { 3612 Manifest Manifest; 3613 std::string Path; 3614 }; 3615 3616 void CreateFakeManifestAndPath( 3617 ManifestAndPath& manifestAndPath, 3618 const string_t& publisher, 3619 std::string_view version = "1.0.0", 3620 std::optional<std::string_view> arpMinVersion = {}, 3621 std::optional<std::string_view> arpMaxVersion = {}) 3622 { 3623 CreateFakeManifest(manifestAndPath.Manifest, publisher, version); 3624 manifestAndPath.Path = ConvertToUTF8(CreateNewGuidNameWString()); 3625 manifestAndPath.Manifest.StreamSha256 = SHA256::ComputeHash(manifestAndPath.Path); 3626 3627 if (arpMinVersion) 3628 { 3629 manifestAndPath.Manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3630 manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3631 manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.back().DisplayVersion = arpMinVersion.value(); 3632 } 3633 3634 if (arpMaxVersion) 3635 { 3636 manifestAndPath.Manifest.Installers[0].BaseInstallerType = InstallerTypeEnum::Exe; 3637 manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.push_back({}); 3638 manifestAndPath.Manifest.Installers[0].AppsAndFeaturesEntries.back().DisplayVersion = arpMaxVersion.value(); 3639 } 3640 } 3641 3642 std::filesystem::path GetOnlyChild(const std::filesystem::path& parent) 3643 { 3644 auto parentDirectoryIterator = std::filesystem::directory_iterator{ parent }; 3645 std::filesystem::path result = parentDirectoryIterator->path(); 3646 REQUIRE(++parentDirectoryIterator == std::filesystem::directory_iterator{}); 3647 return result; 3648 } 3649 3650 void CheckIntermediates(const std::filesystem::path& baseDirectory, const std::vector<std::vector<ManifestAndPath>>& expectedIntermediatesData, std::chrono::seconds sleep = 1s) 3651 { 3652 std::filesystem::path intermediatesDirectory = baseDirectory / "packages"; 3653 3654 size_t intermediatePackageCount = std::count_if(std::filesystem::directory_iterator{ intermediatesDirectory }, std::filesystem::directory_iterator{}, [](const auto&){ return true; }); 3655 REQUIRE(intermediatePackageCount == expectedIntermediatesData.size()); 3656 3657 for (const auto& versions : expectedIntermediatesData) 3658 { 3659 REQUIRE(!versions.empty()); 3660 INFO(versions[0].Manifest.Id); 3661 std::filesystem::path packageDirectory = intermediatesDirectory / ConvertToUTF16(versions[0].Manifest.Id); 3662 3663 REQUIRE(std::filesystem::exists(packageDirectory)); 3664 std::filesystem::path hashDirectory = GetOnlyChild(packageDirectory); 3665 3666 std::filesystem::path versionDataFile = GetOnlyChild(hashDirectory); 3667 std::ifstream versionDataStream{ versionDataFile, std::ios_base::in | std::ios_base::binary }; 3668 auto versionDataBytes = ReadEntireStreamAsByteArray(versionDataStream); 3669 3670 PackageVersionDataManifest versionDataManifest; 3671 versionDataManifest.Deserialize(PackageVersionDataManifest::CreateDecompressor().Decompress(versionDataBytes)); 3672 3673 const auto& versionDataVersions = versionDataManifest.Versions(); 3674 REQUIRE(versionDataVersions.size() == versions.size()); 3675 3676 for (const auto& manifestAndPath : versions) 3677 { 3678 const auto& versionDataItr = std::find_if(versionDataVersions.begin(), versionDataVersions.end(), [&](const auto& v) { return v.Version == manifestAndPath.Manifest.Version; }); 3679 REQUIRE(versionDataItr != versionDataVersions.end()); 3680 const auto& versionData = *versionDataItr; 3681 3682 REQUIRE(manifestAndPath.Path == versionData.ManifestRelativePath); 3683 REQUIRE(SHA256::ConvertToString(manifestAndPath.Manifest.StreamSha256) == versionData.ManifestHash); 3684 3685 auto versionRange = manifestAndPath.Manifest.GetArpVersionRange(); 3686 if (!versionRange.IsEmpty()) 3687 { 3688 REQUIRE(versionData.ArpMinVersion); 3689 REQUIRE(versionRange.GetMinVersion() == versionData.ArpMinVersion.value()); 3690 REQUIRE(versionData.ArpMaxVersion); 3691 REQUIRE(versionRange.GetMaxVersion() == versionData.ArpMaxVersion.value()); 3692 } 3693 } 3694 } 3695 3696 // This is needed to force the timestamp to roll over to a new value for the next call to this function. 3697 // An alternate solution would be to hook the timestamp function and control the values it returns 3698 // so that we can advance/halt time arbitrarily. 3699 std::this_thread::sleep_for(sleep); 3700 } 3701 3702 void PrepareAndCheckIntermediates(const std::filesystem::path& baseFile, const std::filesystem::path& preparedFile, const std::vector<std::vector<ManifestAndPath>>& expectedIntermediatesData, std::chrono::seconds sleep = 1s) 3703 { 3704 TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; 3705 INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); 3706 3707 std::filesystem::copy_file(baseFile, preparedFile, std::filesystem::copy_options::overwrite_existing); 3708 3709 SQLiteIndex index = SQLiteIndex::Open(preparedFile.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); 3710 index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); 3711 index.PrepareForPackaging(); 3712 3713 CheckIntermediates(intermediatesDirectory, expectedIntermediatesData, sleep); 3714 } 3715 3716 TEST_CASE("SQLiteIndex_V2_0_UsageFlow_Simple", "[sqliteindex][V2_0]") 3717 { 3718 TempFile baseFile{ "v2_0_index_tempdb"s, ".db"s }; 3719 TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; 3720 INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); 3721 3722 // Create empty index 3723 std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 2, 0 }); 3724 3725 std::string publisher = "Publisher"; 3726 ManifestAndPath manifest1; 3727 CreateFakeManifestAndPath(manifest1, publisher, "1.0"); 3728 3729 { 3730 // Open existing file to add a manifest 3731 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3732 index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); 3733 index.AddManifest(manifest1.Manifest, manifest1.Path); 3734 } 3735 3736 PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }, 0s); 3737 } 3738 3739 TEST_CASE("SQLiteIndex_V2_0_UsageFlow_Complex", "[sqliteindex][V2_0]") 3740 { 3741 TempFile baseFile{ "v2_0_index_tempdb"s, ".db"s }; 3742 TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; 3743 INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); 3744 3745 // Create empty index 3746 std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 2, 0 }); 3747 3748 // Open existing file to add a new package 3749 std::string Publisher1 = "Publisher1"; 3750 ManifestAndPath manifest1; 3751 CreateFakeManifestAndPath(manifest1, Publisher1, "1.0"); 3752 3753 { 3754 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3755 index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); 3756 index.AddManifest(manifest1.Manifest, manifest1.Path); 3757 } 3758 3759 PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }); 3760 3761 // Open existing file to add another new package 3762 ManifestAndPath manifest2; 3763 CreateFakeManifestAndPath(manifest2, "Publisher2", "1.0"); 3764 3765 { 3766 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3767 index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); 3768 index.AddManifest(manifest2.Manifest, manifest2.Path); 3769 } 3770 3771 PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 } }); 3772 3773 // Open existing file to add a new version of existing package 3774 ManifestAndPath manifest3; 3775 CreateFakeManifestAndPath(manifest3, Publisher1, "2.0"); 3776 3777 { 3778 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3779 index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); 3780 index.AddManifest(manifest3.Manifest, manifest3.Path); 3781 } 3782 3783 PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1, manifest3 } }); 3784 3785 // Open existing file to add a new version of existing package and update an existing version 3786 manifest2.Manifest.StreamSha256 = SHA256::ComputeHash(manifest2.Manifest.Id); 3787 3788 ManifestAndPath manifest4; 3789 CreateFakeManifestAndPath(manifest4, Publisher1, "3.0"); 3790 3791 { 3792 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3793 index.SetProperty(SQLiteIndex::Property::PackageUpdateTrackingBaseTime, ""); 3794 index.UpdateManifest(manifest2.Manifest, manifest2.Path); 3795 index.AddManifest(manifest4.Manifest, manifest4.Path); 3796 } 3797 3798 PrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3, manifest4 } }, 0s); 3799 } 3800 3801 void MigratePrepareAndCheckIntermediates(const std::filesystem::path& baseFile, const std::filesystem::path& preparedFile, const std::vector<std::vector<ManifestAndPath>>& expectedIntermediatesData) 3802 { 3803 TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; 3804 INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); 3805 3806 std::filesystem::copy_file(baseFile, preparedFile, std::filesystem::copy_options::overwrite_existing); 3807 3808 SQLiteIndex index = SQLiteIndex::Open(preparedFile.u8string(), SQLiteStorageBase::OpenDisposition::ReadWrite); 3809 index.MigrateTo({ 2, 0 }); 3810 index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); 3811 index.PrepareForPackaging(); 3812 3813 CheckIntermediates(intermediatesDirectory, expectedIntermediatesData, 0s); 3814 } 3815 3816 TEST_CASE("SQLiteIndex_V2_0_UsageFlow_ComplexMigration", "[sqliteindex][V2_0]") 3817 { 3818 TempFile baseFile{ "v1_7_index_tempdb"s, ".db"s }; 3819 TempFile preparedFile{ "v2_0_index_prepared_tempdb"s, ".db"s }; 3820 INFO("Using files named: [" << baseFile.GetPath() << "] and [" << preparedFile.GetPath() << "]"); 3821 3822 // Create empty index 3823 std::ignore = SQLiteIndex::CreateNew(baseFile, SQLiteVersion{ 1, 7 }); 3824 3825 // Open existing file to add a new package 3826 std::string Publisher1 = "Publisher1"; 3827 ManifestAndPath manifest1; 3828 CreateFakeManifestAndPath(manifest1, Publisher1, "1.0"); 3829 3830 { 3831 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3832 index.AddManifest(manifest1.Manifest, manifest1.Path); 3833 } 3834 3835 MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest1 } }); 3836 3837 // Open existing file to add another new package 3838 ManifestAndPath manifest2; 3839 CreateFakeManifestAndPath(manifest2, "Publisher2", "1.0"); 3840 3841 { 3842 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3843 index.AddManifest(manifest2.Manifest, manifest2.Path); 3844 } 3845 3846 MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1 } }); 3847 3848 // Open existing file to add a new version of existing package 3849 ManifestAndPath manifest3; 3850 CreateFakeManifestAndPath(manifest3, Publisher1, "2.0"); 3851 3852 { 3853 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3854 index.AddManifest(manifest3.Manifest, manifest3.Path); 3855 } 3856 3857 MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3 } }); 3858 3859 // Open existing file to add a new version of existing package and update an existing version 3860 manifest2.Manifest.StreamSha256 = SHA256::ComputeHash(manifest2.Manifest.Id); 3861 3862 ManifestAndPath manifest4; 3863 CreateFakeManifestAndPath(manifest4, Publisher1, "3.0"); 3864 3865 { 3866 SQLiteIndex index = SQLiteIndex::Open(baseFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3867 index.UpdateManifest(manifest2.Manifest, manifest2.Path); 3868 index.AddManifest(manifest4.Manifest, manifest4.Path); 3869 } 3870 3871 MigratePrepareAndCheckIntermediates(baseFile, preparedFile, { { manifest2 }, { manifest1, manifest3, manifest4 } }); 3872 } 3873 3874 TEST_CASE("SQLiteIndex_DependencyWithCaseMismatch", "[sqliteindex][V1_4]") 3875 { 3876 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3877 INFO("Using temporary file named: " << tempFile.GetPath()); 3878 3879 Manifest dependencyManifest1, dependencyManifest2, manifest; 3880 SQLiteIndex index = SimpleTestSetup(tempFile, dependencyManifest1, SQLiteVersion::Latest()); 3881 3882 // Must contain some upper case 3883 auto& publisher2 = "Test2"; 3884 CreateFakeManifest(dependencyManifest2, publisher2); 3885 index.AddManifest(dependencyManifest2, GetPathFromManifest(dependencyManifest2)); 3886 3887 auto& publisher3 = "Test3"; 3888 CreateFakeManifest(manifest, publisher3); 3889 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, dependencyManifest1.Id, "1.0.0")); 3890 manifest.Installers[0].Dependencies.Add(Dependency(DependencyType::Package, ToLower(dependencyManifest2.Id), "1.0.0")); 3891 3892 index.AddManifest(manifest, GetPathFromManifest(manifest)); 3893 } 3894 3895 TEST_CASE("SQLiteIndex_AddOrUpdateManifest", "[sqliteindex]") 3896 { 3897 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3898 INFO("Using temporary file named: " << tempFile.GetPath()); 3899 3900 std::string manifestPath = "test/id/test.id-1.0.0.yaml"; 3901 Manifest manifest; 3902 manifest.Installers.push_back({}); 3903 manifest.Id = "test.id"; 3904 manifest.DefaultLocalization.Add < Localization::PackageName>("Test Name"); 3905 manifest.Moniker = "testmoniker"; 3906 manifest.Version = "1.0.0"; 3907 manifest.Channel = "test"; 3908 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2" }); 3909 manifest.Installers[0].Commands = { "test1", "test2" }; 3910 3911 { 3912 auto version = GENERATE(SQLiteVersion{ 1, 0 }, SQLiteVersion::Latest()); 3913 SQLiteIndex index = SQLiteIndex::CreateNew(tempFile, version); 3914 3915 REQUIRE(index.AddOrUpdateManifest(manifest, manifestPath)); 3916 } 3917 3918 { 3919 SQLiteIndex index = SQLiteIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 3920 3921 // Update should return false 3922 REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); 3923 3924 manifest.DefaultLocalization.Add<Localization::Description>("description2"); 3925 3926 // Update should return false 3927 REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); 3928 3929 // Update with indexed changes should still return false 3930 manifest.DefaultLocalization.Add<Localization::PackageName>("Test Name2"); 3931 manifest.Moniker = "testmoniker2"; 3932 manifest.DefaultLocalization.Add<Localization::Tags>({ "t1", "t2", "t3" }); 3933 manifest.Installers[0].Commands = {}; 3934 3935 REQUIRE(!index.AddOrUpdateManifest(manifest, manifestPath)); 3936 } 3937 } 3938 3939 TEST_CASE("SQLiteIndex_VersionStringPreserved", "[sqliteindex]") 3940 { 3941 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 3942 INFO("Using temporary file named: " << tempFile.GetPath()); 3943 3944 Manifest manifest; 3945 SQLiteIndex index = CreateTestIndex(tempFile); 3946 3947 string_t publisher = "Test"; 3948 std::string version = GENERATE("1.0", "1.10"); 3949 3950 CreateFakeManifest(manifest, publisher, version); 3951 index.AddManifest(manifest, GetPathFromManifest(manifest)); 3952 3953 TempDirectory intermediatesDirectory{ "v2_0_intermediates" }; 3954 INFO("Intermediates directory: " << intermediatesDirectory.GetPath()); 3955 3956 index.SetProperty(SQLiteIndex::Property::IntermediateFileOutputPath, intermediatesDirectory); 3957 index.PrepareForPackaging(); 3958 3959 auto results = index.Search({}); 3960 REQUIRE(results.Matches.size() == 1); 3961 3962 std::string extractedVersion = GetPropertyStringById(index, results.Matches[0].first, PackageVersionProperty::Version); 3963 3964 REQUIRE(extractedVersion == version); 3965 }