diff --git a/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.cpp b/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.cpp index a58b3526c..9f065277a 100644 --- a/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.cpp +++ b/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.cpp @@ -1,4 +1,3 @@ - /* * Infomaniak kDrive - Desktop * Copyright (C) 2023-2024 Infomaniak Network SA @@ -29,9 +28,11 @@ #define API_TIMEOUT 900 +constexpr std::string endOfFileDelimiter = "#EOF"; + namespace KDC { -SnapshotItemHandler::SnapshotItemHandler(log4cplus::Logger logger) : _logger(logger){}; +SnapshotItemHandler::SnapshotItemHandler(log4cplus::Logger logger) : _logger(logger) {}; void SnapshotItemHandler::logError(const std::wstring &methodName, const std::wstring &stdErrorType, const std::string &str, const std::exception &exc) { @@ -188,7 +189,7 @@ void SnapshotItemHandler::readSnapshotItemFields(SnapshotItem &item, const std:: } } -bool SnapshotItemHandler::getItem(SnapshotItem &item, std::stringstream &ss, bool &error, bool &ignore) { +bool SnapshotItemHandler::getItem(SnapshotItem &item, std::stringstream &ss, bool &error, bool &ignore, bool &eof) { error = false; ignore = false; @@ -209,8 +210,13 @@ bool SnapshotItemHandler::getItem(SnapshotItem &item, std::stringstream &ss, boo } } - ParsingState state; + if (line == endOfFileDelimiter) { + LOG_INFO(_logger, "End of file reached"); + eof = true; + return false; + } + ParsingState state; while (state.readNextLine) { state.readNextLine = false; @@ -256,8 +262,8 @@ bool SnapshotItemHandler::getItem(SnapshotItem &item, std::stringstream &ss, boo CsvFullFileListWithCursorJob::CsvFullFileListWithCursorJob(int driveDbId, const NodeId &dirId, std::unordered_set blacklist /*= {}*/, bool zip /*= true*/) : - AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0), - _dirId(dirId), _blacklist(blacklist), _zip(zip), _snapshotItemHandler(_logger) { + AbstractTokenNetworkJob(ApiType::Drive, 0, 0, driveDbId, 0), _dirId(dirId), _blacklist(blacklist), _zip(zip), + _snapshotItemHandler(_logger) { _httpMethod = Poco::Net::HTTPRequest::HTTP_GET; _customTimeout = API_TIMEOUT + 15; @@ -266,11 +272,11 @@ CsvFullFileListWithCursorJob::CsvFullFileListWithCursorJob(int driveDbId, const } } -bool CsvFullFileListWithCursorJob::getItem(SnapshotItem &item, bool &error, bool &ignore) { +bool CsvFullFileListWithCursorJob::getItem(SnapshotItem &item, bool &error, bool &ignore, bool &eof) { error = false; ignore = false; - return _snapshotItemHandler.getItem(item, _ss, error, ignore); + return _snapshotItemHandler.getItem(item, _ss, error, ignore, eof); } std::string CsvFullFileListWithCursorJob::getCursor() { @@ -286,7 +292,7 @@ std::string CsvFullFileListWithCursorJob::getSpecificUrl() { void CsvFullFileListWithCursorJob::setQueryParameters(Poco::URI &uri, bool &canceled) { uri.addQueryParameter("directory_id", _dirId); uri.addQueryParameter("recursive", "true"); - uri.addQueryParameter("format", "csv"); + uri.addQueryParameter("format", "safe_csv"); if (!_blacklist.empty()) { std::string str = Utility::list2str(_blacklist); if (!str.empty()) { @@ -307,28 +313,17 @@ bool CsvFullFileListWithCursorJob::handleResponse(std::istream &is) { // Check that the stringstream is not empty (network issues) _ss.seekg(0, std::ios_base::end); - int length = _ss.tellg(); + const auto length = _ss.tellg(); if (length == 0) { LOG_ERROR(_logger, "Reply " << jobId() << " received with empty content."); return false; } - // Check that the stringstream ends by a LF (0x0A) - _ss.seekg(length - 1, std::ios_base::beg); - char lastChar = 0x00; - _ss.read(&lastChar, 1); _ss.seekg(0, std::ios_base::beg); - if (lastChar != 0x0A) { - LOGW_WARN(_logger, L"Reply " << jobId() << L" received with bad content - length=" << length << L" value=" - << Utility::s2ws(_ss.str()).c_str()); - return false; - } - if (isExtendedLog()) { LOGW_DEBUG(_logger, L"Reply " << jobId() << L" received - length=" << length << L" value=" << Utility::s2ws(_ss.str()).c_str()); } - return true; } diff --git a/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.h b/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.h index d376754d8..034a6bcf7 100644 --- a/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.h +++ b/src/libsyncengine/jobs/network/API_v2/csvfullfilelistwithcursorjob.h @@ -58,9 +58,10 @@ class SnapshotItemHandler { * @param ss stringstream containg the CSV file * @param error `true` if parsing fails * @param ignore `true` if a line is ignored due to a non critical parsing issue + * @param eof `true` if the end of file (i.e. a line containg the end of file delimiter) has been reached * @return `true` if there are more lines to be read */ - bool getItem(SnapshotItem &item, std::stringstream &ss, bool &error, bool &ignore); + bool getItem(SnapshotItem &item, std::stringstream &ss, bool &error, bool &ignore, bool &eof); private: bool _ignoreFirstLine = true; @@ -81,9 +82,10 @@ class CsvFullFileListWithCursorJob : public AbstractTokenNetworkJob { * @param item : item extracted from line of the CSV file * @param error : blocking error, stop the process * @param ignore : parsing issue, non blocking, just ignore the item + * @param eof : whether the end of file has been reached or not * @return if return == true, continue parsing */ - bool getItem(SnapshotItem &item, bool &error, bool &ignore); + bool getItem(SnapshotItem &item, bool &error, bool &ignore, bool &eof); std::string getCursor(); private: diff --git a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp index c9a4b4e11..90263deca 100644 --- a/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp +++ b/src/libsyncengine/update_detection/file_system_observer/remotefilesystemobserverworker.cpp @@ -45,8 +45,7 @@ namespace KDC { RemoteFileSystemObserverWorker::RemoteFileSystemObserverWorker(std::shared_ptr syncPal, const std::string &name, const std::string &shortName) : - FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Remote), - _driveDbId(syncPal->driveDbId()) {} + FileSystemObserverWorker(syncPal, name, shortName, ReplicaSide::Remote), _driveDbId(syncPal->driveDbId()) {} RemoteFileSystemObserverWorker::~RemoteFileSystemObserverWorker() { LOG_SYNCPAL_DEBUG(_logger, "~RemoteFileSystemObserverWorker"); @@ -317,13 +316,16 @@ ExitCode RemoteFileSystemObserverWorker::getItemsInDir(const NodeId &dirId, cons SnapshotItem item; bool error = false; bool ignore = false; + bool eof = false; std::unordered_set existingFiles; uint64_t itemCount = 0; - while (job->getItem(item, error, ignore)) { + while (job->getItem(item, error, ignore, eof)) { if (ignore) { continue; } + if (eof) break; + itemCount++; if (error) { @@ -374,6 +376,14 @@ ExitCode RemoteFileSystemObserverWorker::getItemsInDir(const NodeId &dirId, cons } } + if (!eof) { + const std::string msg = "Failed to parse CSV reply: missing EOF delimiter"; + LOG_SYNCPAL_WARN(_logger, msg.c_str()); + SentryHandler::instance()->captureMessage(SentryLevel::Warning, "RemoteFileSystemObserverWorker::getItemsInDir", "msg"); + setExitCause(ExitCause::FullListParsingError); + return ExitCode::NetworkError; + } + // Delete orphans std::unordered_set nodeIds; _snapshot->ids(nodeIds); diff --git a/test/libsyncengine/jobs/network/testnetworkjobs.cpp b/test/libsyncengine/jobs/network/testnetworkjobs.cpp index 4de05dec1..3c00703bb 100644 --- a/test/libsyncengine/jobs/network/testnetworkjobs.cpp +++ b/test/libsyncengine/jobs/network/testnetworkjobs.cpp @@ -451,7 +451,8 @@ void TestNetworkJobs::testFullFileListWithCursorCsv() { SnapshotItem item; bool error = false; bool ignore = false; - while (job.getItem(item, error, ignore)) { + bool eof = false; + while (job.getItem(item, error, ignore, eof)) { if (ignore) { continue; } @@ -463,6 +464,7 @@ void TestNetworkJobs::testFullFileListWithCursorCsv() { CPPUNIT_ASSERT(!cursor.empty()); CPPUNIT_ASSERT(counter == 5); + CPPUNIT_ASSERT(eof); } void TestNetworkJobs::testFullFileListWithCursorCsvZip() { @@ -475,7 +477,8 @@ void TestNetworkJobs::testFullFileListWithCursorCsvZip() { SnapshotItem item; bool error = false; bool ignore = false; - while (job.getItem(item, error, ignore)) { + bool eof = false; + while (job.getItem(item, error, ignore, eof)) { if (ignore) { continue; } @@ -487,6 +490,7 @@ void TestNetworkJobs::testFullFileListWithCursorCsvZip() { CPPUNIT_ASSERT(!cursor.empty()); CPPUNIT_ASSERT(counter == 5); + CPPUNIT_ASSERT(eof); } void TestNetworkJobs::testFullFileListWithCursorJsonBlacklist() { @@ -525,7 +529,8 @@ void TestNetworkJobs::testFullFileListWithCursorCsvBlacklist() { SnapshotItem item; bool error = false; bool ignore = false; - while (job.getItem(item, error, ignore)) { + bool eof = false; + while (job.getItem(item, error, ignore, eof)) { if (ignore) { continue; } @@ -537,6 +542,29 @@ void TestNetworkJobs::testFullFileListWithCursorCsvBlacklist() { CPPUNIT_ASSERT(!cursor.empty()); CPPUNIT_ASSERT(counter == 0); + CPPUNIT_ASSERT(eof); +} + +void TestNetworkJobs::testFullFileListWithCursorMissingEof() { + CsvFullFileListWithCursorJob job(_driveDbId, "1"); + ExitCode exitCode = job.runSynchronously(); + CPPUNIT_ASSERT(exitCode == ExitCode::Ok); + + int counter = 0; + const std::string cursor = job.getCursor(); + SnapshotItem item; + bool error = false; + bool ignore = false; + bool eof = false; + // Call getItem only once to simulate a troncated CSV file + job.getItem(item, error, ignore, eof); + if (item.parentId() == pictureDirRemoteId) { + counter++; + } + + CPPUNIT_ASSERT(!cursor.empty()); + CPPUNIT_ASSERT_LESS(5, counter); + CPPUNIT_ASSERT(!eof); } void TestNetworkJobs::testGetInfoUser() { diff --git a/test/libsyncengine/jobs/network/testnetworkjobs.h b/test/libsyncengine/jobs/network/testnetworkjobs.h index 87015a863..e8ba9086c 100644 --- a/test/libsyncengine/jobs/network/testnetworkjobs.h +++ b/test/libsyncengine/jobs/network/testnetworkjobs.h @@ -42,6 +42,7 @@ class TestNetworkJobs : public CppUnit::TestFixture { CPPUNIT_TEST(testFullFileListWithCursorCsvZip); CPPUNIT_TEST(testFullFileListWithCursorJsonBlacklist); CPPUNIT_TEST(testFullFileListWithCursorCsvBlacklist); + CPPUNIT_TEST(testFullFileListWithCursorMissingEof); CPPUNIT_TEST(testGetInfoUser); CPPUNIT_TEST(testGetInfoDrive); CPPUNIT_TEST(testThumbnail); @@ -78,6 +79,7 @@ class TestNetworkJobs : public CppUnit::TestFixture { void testFullFileListWithCursorCsvZip(); void testFullFileListWithCursorJsonBlacklist(); void testFullFileListWithCursorCsvBlacklist(); + void testFullFileListWithCursorMissingEof(); void testGetInfoUser(); void testGetInfoDrive(); void testThumbnail(); diff --git a/test/libsyncengine/jobs/network/testsnapshotitemhandler.cpp b/test/libsyncengine/jobs/network/testsnapshotitemhandler.cpp index 46d861741..65366642d 100644 --- a/test/libsyncengine/jobs/network/testsnapshotitemhandler.cpp +++ b/test/libsyncengine/jobs/network/testsnapshotitemhandler.cpp @@ -26,6 +26,8 @@ using namespace CppUnit; namespace KDC { +constexpr std::string endOfFileDelimiter = "#EOF"; + namespace snapshotitem_checker { std::string makeMessage(const CppUnit::Exception &e) { std::string msg = "Details: \n -" + e.message().details(); @@ -217,10 +219,11 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = true; bool error = true; + bool eof = true; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(!handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(!handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); } @@ -230,11 +233,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString("kDrive2") << ",dir,1000,123,124,0,1"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); @@ -250,11 +254,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"("kDrive2")") << ",dir,1000,123,124,0,1,"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); @@ -270,11 +275,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"(""kDrive2"")") << ",dir,1000,123,124,0,1"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); } @@ -284,12 +290,13 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"(kDrive 2)") << ",dir,1000,123,124,1,0,"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); @@ -305,12 +312,13 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"("kDrive )"); SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(error); } @@ -320,11 +328,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"(kDrive2)") << ",dir,1000,123,124,"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(ignore); CPPUNIT_ASSERT(!error); } @@ -334,11 +343,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"("test"test")") << ",dir,1000,123,124,0,1"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); } @@ -348,11 +358,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"("kDrive2")") << ",dir,1000,123,124,0,1"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); } @@ -362,11 +373,12 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"(test\"test)") << ",dir,1000,123,124,0,1"; SnapshotItemHandler handler(Log::instance()->getLogger()); - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(ignore); CPPUNIT_ASSERT(!error); } @@ -376,6 +388,7 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItem item; bool ignore = false; bool error = false; + bool eof = false; std::stringstream ss; ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" << "0,1," << toCsvString(R"(test\"test)") << ",dir,1000,123,124,0,1\n" @@ -384,18 +397,106 @@ void TestSnapshotItemHandler::testGetItem() { SnapshotItemHandler handler(Log::instance()->getLogger()); // First line should be ignored because of parsing issue - CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore)); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); CPPUNIT_ASSERT(ignore); CPPUNIT_ASSERT(!error); // The other ones should be correctly parsed int counter = 0; - while (handler.getItem(item, ss, error, ignore)) { + while (handler.getItem(item, ss, error, ignore, eof)) { counter++; CPPUNIT_ASSERT(!ignore); CPPUNIT_ASSERT(!error); } CPPUNIT_ASSERT_EQUAL(2, counter); // There should be 2 valid items } + + // End of line test : normal case + { + SnapshotItem item; + bool ignore = false; + bool error = false; + bool eof = false; + std::stringstream ss; + ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" + << "1,0,test,dir,1000,123,124,0,1\n" + << endOfFileDelimiter.c_str(); + SnapshotItemHandler handler(Log::instance()->getLogger()); + while (handler.getItem(item, ss, error, ignore, eof)) { + // Nothing to do, just read the whole file + } + CPPUNIT_ASSERT(!ignore); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(eof); + + const SnapshotItem expectedItem(NodeId("1"), NodeId("0"), Str2SyncName(std::string("test")), static_cast(123), + static_cast(124), NodeType::Directory, static_cast(1000), true, false, + true); + const auto [success, message] = snapshotitem_checker::compare(expectedItem, item); + CPPUNIT_ASSERT_MESSAGE(message, success); + } + + // End of line test : missing EOF delimiter + { + SnapshotItem item; + bool ignore = false; + bool error = false; + bool eof = false; + std::stringstream ss; + ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" + << "1,0,test,dir,1000,123,124,0,1\n"; + SnapshotItemHandler handler(Log::instance()->getLogger()); + while (handler.getItem(item, ss, error, ignore, eof)) { + // Nothing to do, just read the whole file + } + CPPUNIT_ASSERT(!ignore); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(!eof); + + const SnapshotItem expectedItem(NodeId("1"), NodeId("0"), Str2SyncName(std::string("test")), static_cast(123), + static_cast(124), NodeType::Directory, static_cast(1000), true, false, + true); + const auto [success, message] = snapshotitem_checker::compare(expectedItem, item); + CPPUNIT_ASSERT_MESSAGE(message, success); + } + + // End of line test : EOF delimiter not at the end + { + SnapshotItem item; + bool ignore = false; + bool error = false; + bool eof = false; + std::stringstream ss; + ss << "id,parent_id,name,type,size,created_at,last_modified_at,can_write,is_link\n" + << "1,0,test,dir,1000,123,124,0,1\n" + << endOfFileDelimiter.c_str() << "\n" + << "2,0,test2,dir,1000,123,124,0,1"; + SnapshotItemHandler handler(Log::instance()->getLogger()); + CPPUNIT_ASSERT(handler.getItem(item, ss, error, ignore, eof)); + CPPUNIT_ASSERT(!ignore); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(!eof); + { + const SnapshotItem expectedItem(NodeId("1"), NodeId("0"), Str2SyncName(std::string("test")), + static_cast(123), static_cast(124), NodeType::Directory, + static_cast(1000), true, false, true); + const auto [success, message] = snapshotitem_checker::compare(expectedItem, item); + CPPUNIT_ASSERT_MESSAGE(message, success); + } + + while (handler.getItem(item, ss, error, ignore, eof)) { + // Nothing to do, just read the whole file + } + CPPUNIT_ASSERT(!ignore); + CPPUNIT_ASSERT(!error); + CPPUNIT_ASSERT(eof); + { + const SnapshotItem expectedItem(NodeId("2"), NodeId("0"), Str2SyncName(std::string("test2")), + static_cast(123), static_cast(124), NodeType::Directory, + static_cast(1000), true, false, true); + const auto [success, message] = snapshotitem_checker::compare(expectedItem, item); + CPPUNIT_ASSERT_MESSAGE(message, !success); + } + } } } // namespace KDC