PortableIndex.cpp (7861B)
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 <winget/SQLiteStorageBase.h> 7 #include <Microsoft/Schema/IPortableIndex.h> 8 #include <winget/PortableIndex.h> 9 #include <Microsoft/Schema/Portable_1_0/PortableTable.h> 10 #include <winget/PortableFileEntry.h> 11 12 using namespace std::string_literals; 13 using namespace TestCommon; 14 using namespace AppInstaller::Portable; 15 using namespace AppInstaller::Repository::Microsoft; 16 using namespace AppInstaller::SQLite; 17 using namespace AppInstaller::Repository::Microsoft::Schema; 18 19 void CreateFakePortableFile(PortableFileEntry& file) 20 { 21 file.SetFilePath("testPortableFile.exe"); 22 file.FileType = PortableFileType::File; 23 file.SHA256 = "f0e4c2f76c58916ec258f246851bea091d14d4247a2fc3e18694461b1816e13b"; 24 file.SymlinkTarget = "testSymlinkTarget.exe"; 25 } 26 27 TEST_CASE("PortableIndexCreateLatestAndReopen", "[portableIndex]") 28 { 29 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 30 INFO("Using temporary file named: " << tempFile.GetPath()); 31 32 Version versionCreated; 33 34 // Create the index 35 { 36 PortableIndex index = PortableIndex::CreateNew(tempFile, Version::Latest()); 37 versionCreated = index.GetVersion(); 38 } 39 40 // Reopen the index for read only 41 { 42 INFO("Trying with Read"); 43 PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Read); 44 Version versionRead = index.GetVersion(); 45 REQUIRE(versionRead == versionCreated); 46 } 47 48 // Reopen the index for read/write 49 { 50 INFO("Trying with ReadWrite"); 51 PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 52 Version versionRead = index.GetVersion(); 53 REQUIRE(versionRead == versionCreated); 54 } 55 56 // Reopen the index for immutable read 57 { 58 INFO("Trying with Immutable"); 59 PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::Immutable); 60 Version versionRead = index.GetVersion(); 61 REQUIRE(versionRead == versionCreated); 62 } 63 } 64 65 TEST_CASE("PortableIndexAddEntryToTable", "[portableIndex]") 66 { 67 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 68 INFO("Using temporary file named: " << tempFile.GetPath()); 69 70 PortableFileEntry portableFile; 71 CreateFakePortableFile(portableFile); 72 73 { 74 PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); 75 index.AddPortableFile(portableFile); 76 } 77 78 { 79 // Open it directly to directly test table state 80 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 81 REQUIRE(!Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); 82 } 83 84 { 85 PortableIndex index = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 86 index.RemovePortableFile(portableFile); 87 } 88 89 { 90 // Open it directly to directly test table state 91 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 92 REQUIRE(Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); 93 } 94 } 95 96 TEST_CASE("PortableIndex_AddUpdateRemove", "[portableIndex]") 97 { 98 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 99 INFO("Using temporary file named: " << tempFile.GetPath()); 100 101 PortableFileEntry portableFile; 102 CreateFakePortableFile(portableFile); 103 104 PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); 105 index.AddPortableFile(portableFile); 106 107 // Apply changes to portable file 108 std::string updatedHash = "2db8ae7657c6622b04700137740002c51c36588e566651c9f67b4b096c8ad18b"; 109 portableFile.FileType = PortableFileType::Symlink; 110 portableFile.SHA256 = updatedHash; 111 portableFile.SymlinkTarget = "fakeSymlinkTarget.exe"; 112 113 REQUIRE(index.UpdatePortableFile(portableFile)); 114 115 { 116 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 117 auto fileFromIndex = Schema::Portable_V1_0::PortableTable::GetPortableFileById(connection, 1); 118 REQUIRE(fileFromIndex.has_value()); 119 REQUIRE(fileFromIndex->GetFilePath() == portableFile.GetFilePath()); 120 REQUIRE(fileFromIndex->FileType == PortableFileType::Symlink); 121 REQUIRE(fileFromIndex->SHA256 == updatedHash); 122 REQUIRE(fileFromIndex->SymlinkTarget == "fakeSymlinkTarget.exe"); 123 } 124 125 { 126 PortableIndex index2 = PortableIndex::Open(tempFile, SQLiteStorageBase::OpenDisposition::ReadWrite); 127 index2.RemovePortableFile(portableFile); 128 } 129 130 { 131 // Open it directly to directly test table state 132 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 133 REQUIRE(Schema::Portable_V1_0::PortableTable::IsEmpty(connection)); 134 } 135 } 136 137 TEST_CASE("PortableIndex_UpdateFile_CaseInsensitive", "[portableIndex]") 138 { 139 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 140 INFO("Using temporary file named: " << tempFile.GetPath()); 141 142 PortableFileEntry portableFile; 143 CreateFakePortableFile(portableFile); 144 145 PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); 146 index.AddPortableFile(portableFile); 147 148 // By default, portable file path is set to "testPortableFile.exe" 149 // Change file path to all upper case should still successfully update. 150 portableFile.SetFilePath("TESTPORTABLEFILE.exe"); 151 std::string updatedHash = "2db8ae7657c6622b04700137740002c51c36588e566651c9f67b4b096c8ad18b"; 152 portableFile.FileType = PortableFileType::Symlink; 153 portableFile.SHA256 = updatedHash; 154 portableFile.SymlinkTarget = "fakeSymlinkTarget.exe"; 155 156 REQUIRE(index.UpdatePortableFile(portableFile)); 157 158 { 159 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadOnly); 160 auto fileFromIndex = Schema::Portable_V1_0::PortableTable::GetPortableFileById(connection, 1); 161 REQUIRE(fileFromIndex.has_value()); 162 REQUIRE(fileFromIndex->GetFilePath() == portableFile.GetFilePath()); 163 REQUIRE(fileFromIndex->FileType == PortableFileType::Symlink); 164 REQUIRE(fileFromIndex->SHA256 == updatedHash); 165 REQUIRE(fileFromIndex->SymlinkTarget == "fakeSymlinkTarget.exe"); 166 } 167 } 168 169 TEST_CASE("PortableIndex_AddDuplicateFile", "[portableIndex]") 170 { 171 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 172 INFO("Using temporary file named: " << tempFile.GetPath()); 173 174 PortableFileEntry portableFile; 175 CreateFakePortableFile(portableFile); 176 177 PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); 178 index.AddPortableFile(portableFile); 179 180 // Change file path to all upper case. Adding duplicate file should fail. 181 portableFile.SetFilePath("TESTPORTABLEFILE.exe"); 182 REQUIRE_THROWS(index.AddPortableFile(portableFile), ERROR_ALREADY_EXISTS); 183 } 184 185 TEST_CASE("PortableIndex_RemoveWithId", "[portableIndex]") 186 { 187 TempFile tempFile{ "repolibtest_tempdb"s, ".db"s }; 188 INFO("Using temporary file named: " << tempFile.GetPath()); 189 190 PortableFileEntry portableFile; 191 CreateFakePortableFile(portableFile); 192 193 PortableIndex index = PortableIndex::CreateNew(tempFile, { 1, 0 }); 194 index.AddPortableFile(portableFile); 195 196 { 197 Connection connection = Connection::Create(tempFile, Connection::OpenDisposition::ReadWrite); 198 REQUIRE(Portable_V1_0::PortableTable::ExistsById(connection, 1)); 199 Portable_V1_0::PortableTable::DeleteById(connection, 1); 200 REQUIRE_FALSE(Portable_V1_0::PortableTable::ExistsById(connection, 1)); 201 } 202 }