| /** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include <array> |
| #include <memory> |
| #include <string> |
| |
| #include "core/Core.h" |
| #include "FileSystemRepository.h" |
| #include "VolatileContentRepository.h" |
| #include "DatabaseContentRepository.h" |
| #include "core/BufferedContentSession.h" |
| #include "minifi-cpp/FlowFileRecord.h" |
| #include "unit/TestBase.h" |
| #include "unit/Catch.h" |
| #include "utils/ConfigurationUtils.h" |
| |
| template<typename ContentRepositoryClass> |
| class ContentSessionController : public TestController { |
| public: |
| ContentSessionController() |
| : contentRepository(std::make_shared<ContentRepositoryClass>()) { |
| auto contentRepoPath = createTempDirectory(); |
| auto config = std::make_shared<minifi::ConfigureImpl>(); |
| config->set(minifi::Configure::nifi_dbcontent_repository_directory_default, contentRepoPath.string()); |
| contentRepository->initialize(config); |
| } |
| |
| ~ContentSessionController() { |
| log.reset(); |
| } |
| |
| ContentSessionController(ContentSessionController&&) = delete; |
| ContentSessionController(const ContentSessionController&) = delete; |
| ContentSessionController& operator=(ContentSessionController&&) = delete; |
| ContentSessionController& operator=(const ContentSessionController&) = delete; |
| |
| std::shared_ptr<core::ContentRepository> contentRepository; |
| }; |
| |
| std::shared_ptr<minifi::io::OutputStream> operator<<(std::shared_ptr<minifi::io::OutputStream> stream, const std::string& str) { |
| REQUIRE(stream->write(reinterpret_cast<const uint8_t*>(str.data()), str.length()) == str.length()); |
| return stream; |
| } |
| |
| std::shared_ptr<minifi::io::InputStream> operator>>(std::shared_ptr<minifi::io::InputStream> stream, std::string& str) { |
| str = ""; |
| std::array<std::byte, utils::configuration::DEFAULT_BUFFER_SIZE> buffer{}; |
| while (true) { |
| const auto ret = stream->read(buffer); |
| REQUIRE_FALSE(minifi::io::isError(ret)); |
| if (ret == 0) { break; } |
| str += std::string{reinterpret_cast<char*>(buffer.data()), ret}; |
| } |
| return stream; |
| } |
| |
| void NO_CREATE(const std::shared_ptr<minifi::ResourceClaim>&) { |
| REQUIRE(false); |
| } |
| |
| // TODO(adebreceni): |
| // seems like the current version of Catch2 does not support templated tests |
| // we should update instead of creating make-shift macros |
| template<typename ContentRepositoryClass> |
| void test_template() { |
| ContentSessionController<ContentRepositoryClass> controller; |
| std::shared_ptr<core::ContentRepository> contentRepository = controller.contentRepository; |
| |
| std::shared_ptr<minifi::ResourceClaim> oldClaim; |
| { |
| auto session = contentRepository->createSession(); |
| oldClaim = session->create(); |
| session->write(oldClaim) << "data"; |
| session->commit(); |
| } |
| |
| auto session = contentRepository->createSession(); |
| const bool is_buffered_session = std::dynamic_pointer_cast<core::BufferedContentSession>(session) != nullptr; |
| |
| REQUIRE_THROWS(session->write(oldClaim)); |
| |
| REQUIRE_NOTHROW(session->read(oldClaim)); |
| |
| session->append(oldClaim, 4, NO_CREATE) << "-addendum"; |
| |
| std::shared_ptr<minifi::ResourceClaim> copied_claim; |
| { |
| auto other_session = contentRepository->createSession(); |
| other_session->append(oldClaim, 4, [&] (auto new_claim) { |
| copied_claim = std::move(new_claim); |
| }) << "-some extra content"; |
| other_session->commit(); |
| } |
| REQUIRE(copied_claim); |
| |
| { |
| std::string read_content; |
| read_content.resize(contentRepository->size(*copied_claim)); |
| contentRepository->read(*copied_claim)->read(as_writable_bytes(std::span(read_content))); |
| REQUIRE(read_content == "data-some extra content"); |
| } |
| |
| // TODO(adebreceni): MINIFICPP-1954 |
| if (is_buffered_session) { |
| REQUIRE_THROWS(session->read(oldClaim)); |
| } |
| |
| auto claim1 = session->create(); |
| session->write(claim1) << "hello content!"; |
| { |
| std::string content; |
| session->read(claim1) >> content; |
| REQUIRE(content == "hello content!"); |
| } |
| |
| auto claim2 = session->create(); |
| session->append(claim2, 0, NO_CREATE) << "beginning"; |
| session->append(claim2, 9, NO_CREATE) << "-end"; |
| |
| auto claim3 = session->create(); |
| session->write(claim3) << "first"; |
| session->append(claim3, 5, NO_CREATE) << "-last"; |
| |
| auto claim4 = session->create(); |
| session->write(claim4) << "beginning"; |
| session->write(claim4) << "overwritten"; |
| |
| SECTION("Commit") { |
| session->commit(); |
| |
| std::string content; |
| contentRepository->read(*oldClaim) >> content; |
| REQUIRE(content == "data-addendum"); |
| |
| contentRepository->read(*claim1) >> content; |
| REQUIRE(content == "hello content!"); |
| |
| contentRepository->read(*claim2) >> content; |
| REQUIRE(content == "beginning-end"); |
| |
| contentRepository->read(*claim3) >> content; |
| REQUIRE(content == "first-last"); |
| |
| contentRepository->read(*claim4) >> content; |
| REQUIRE(content == "overwritten"); |
| } |
| |
| SECTION("Rollback") { |
| session->rollback(); |
| |
| if (is_buffered_session) { |
| std::string content; |
| contentRepository->read(*oldClaim) >> content; |
| REQUIRE(content == "data"); |
| REQUIRE(!contentRepository->exists(*claim1)); |
| REQUIRE(!contentRepository->exists(*claim2)); |
| REQUIRE(!contentRepository->exists(*claim3)); |
| REQUIRE(!contentRepository->exists(*claim4)); |
| } |
| } |
| } |
| |
| TEST_CASE("ContentSession behavior") { |
| SECTION("FileSystemRepository") { |
| test_template<core::repository::FileSystemRepository>(); |
| } |
| SECTION("VolatileContentRepository") { |
| test_template<core::repository::VolatileContentRepository>(); |
| } |
| SECTION("DatabaseContentRepository") { |
| test_template<core::repository::DatabaseContentRepository>(); |
| } |
| } |