diff options
Diffstat (limited to 'documentapi/src/tests/replymerger/replymerger_test.cpp')
-rw-r--r-- | documentapi/src/tests/replymerger/replymerger_test.cpp | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/documentapi/src/tests/replymerger/replymerger_test.cpp b/documentapi/src/tests/replymerger/replymerger_test.cpp new file mode 100644 index 00000000000..2e1551e8dca --- /dev/null +++ b/documentapi/src/tests/replymerger/replymerger_test.cpp @@ -0,0 +1,304 @@ +// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. +#include <vespa/log/log.h> +LOG_SETUP("replymerger_test"); + +#include <vespa/fastos/fastos.h> +#include <iostream> +#include <vespa/vespalib/testkit/testapp.h> +#include <vespa/documentapi/messagebus/replymerger.h> +#include <vespa/documentapi/messagebus/documentprotocol.h> +#include <vespa/documentapi/messagebus/messages/removedocumentreply.h> +#include <vespa/documentapi/messagebus/messages/updatedocumentreply.h> +#include <vespa/documentapi/messagebus/messages/getdocumentreply.h> +#include <vespa/messagebus/emptyreply.h> + +using namespace documentapi; + +class Test : public vespalib::TestApp +{ + static void assertReplyErrorsMatch(const mbus::Reply& r, + const std::vector<mbus::Error>& errors); +public: + int Main(); + + void mergingGenericRepliesWithNoErrorsPicksFirstReply(); + void mergingSingleReplyWithOneErrorReturnsEmptyReplyWithError(); + void mergingSingleReplyWithMultipleErrorsReturnsEmptyReplyWithAllErrors(); + void mergingMultipleRepliesWithMultipleErrorsReturnsEmptyReplyWithAllErrors(); + void returnIgnoredReplyWhenAllRepliesHaveOnlyIgnoredErrors(); + void successfulReplyTakesPrecedenceOverIgnoredReplyWhenNoErrors(); + void nonIgnoredErrorTakesPrecedence(); + void returnRemoveDocumentReplyWhereDocWasFound(); + void returnFirstRemoveDocumentReplyIfNoDocsWereFound(); + void returnUpdateDocumentReplyWhereDocWasFound(); + void returnGetDocumentReplyWhereDocWasFound(); + void mergingZeroRepliesReturnsDefaultEmptyReply(); +}; + +TEST_APPHOOK(Test); + +void +Test::mergingGenericRepliesWithNoErrorsPicksFirstReply() +{ + mbus::EmptyReply r1; + mbus::EmptyReply r2; + mbus::EmptyReply r3; + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + merger.merge(2, r3); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + EXPECT_EQUAL(0u, ret.getSuccessfulReplyIndex()); +} + +void +Test::mergingSingleReplyWithOneErrorReturnsEmptyReplyWithError() +{ + mbus::EmptyReply r1; + std::vector<mbus::Error> errors = { mbus::Error(1234, "oh no!") }; + r1.addError(errors[0]); + ReplyMerger merger; + merger.merge(0, r1); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + assertReplyErrorsMatch(*gen, errors); +} + +void +Test::mergingSingleReplyWithMultipleErrorsReturnsEmptyReplyWithAllErrors() +{ + mbus::EmptyReply r1; + std::vector<mbus::Error> errors = { + mbus::Error(1234, "oh no!"), + mbus::Error(4567, "oh dear!") + }; + r1.addError(errors[0]); + r1.addError(errors[1]); + ReplyMerger merger; + merger.merge(0, r1); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + assertReplyErrorsMatch(*gen, errors); +} + +void +Test::mergingMultipleRepliesWithMultipleErrorsReturnsEmptyReplyWithAllErrors() +{ + mbus::EmptyReply r1; + mbus::EmptyReply r2; + std::vector<mbus::Error> errors = { + mbus::Error(1234, "oh no!"), + mbus::Error(4567, "oh dear!"), + mbus::Error(678, "omg!") + }; + r1.addError(errors[0]); + r1.addError(errors[1]); + r2.addError(errors[2]); + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + assertReplyErrorsMatch(*gen, errors); +} + +void +Test::returnIgnoredReplyWhenAllRepliesHaveOnlyIgnoredErrors() +{ + mbus::EmptyReply r1; + mbus::EmptyReply r2; + std::vector<mbus::Error> errors = { + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "oh no!"), + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "oh dear!"), + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "omg!") + }; + r1.addError(errors[0]); + r1.addError(errors[1]); + r2.addError(errors[2]); + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + // Only first ignore error from each reply. + assertReplyErrorsMatch(*gen, { errors[0], errors[2] }); +} + +void +Test::successfulReplyTakesPrecedenceOverIgnoredReplyWhenNoErrors() +{ + mbus::EmptyReply r1; + mbus::EmptyReply r2; + std::vector<mbus::Error> errors = { + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "oh no!"), + }; + r1.addError(errors[0]); + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + EXPECT_EQUAL(1u, ret.getSuccessfulReplyIndex()); +} + +void +Test::nonIgnoredErrorTakesPrecedence() +{ + mbus::EmptyReply r1; + mbus::EmptyReply r2; + std::vector<mbus::Error> errors = { + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "oh no!"), + mbus::Error(DocumentProtocol::ERROR_ABORTED, "kablammo!"), + mbus::Error(DocumentProtocol::ERROR_MESSAGE_IGNORED, "omg!") + }; + r1.addError(errors[0]); + r1.addError(errors[1]); + r2.addError(errors[2]); + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + // All errors from replies with errors are included, not those that + // are fully ignored. + assertReplyErrorsMatch(*gen, { errors[0], errors[1] }); +} + +void +Test::returnRemoveDocumentReplyWhereDocWasFound() +{ + RemoveDocumentReply r1; + RemoveDocumentReply r2; + RemoveDocumentReply r3; + r1.setWasFound(false); + r2.setWasFound(true); + r3.setWasFound(false); + + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + merger.merge(2, r3); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + ASSERT_EQUAL(1u, ret.getSuccessfulReplyIndex()); +} + +void +Test::returnFirstRemoveDocumentReplyIfNoDocsWereFound() +{ + RemoveDocumentReply r1; + RemoveDocumentReply r2; + r1.setWasFound(false); + r2.setWasFound(false); + + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + ASSERT_EQUAL(0u, ret.getSuccessfulReplyIndex()); +} + +void +Test::returnUpdateDocumentReplyWhereDocWasFound() +{ + UpdateDocumentReply r1; + UpdateDocumentReply r2; + UpdateDocumentReply r3; + r1.setWasFound(false); + r2.setWasFound(true); // return first reply + r3.setWasFound(true); + + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + merger.merge(2, r3); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + ASSERT_EQUAL(1u, ret.getSuccessfulReplyIndex()); +} + +void +Test::returnGetDocumentReplyWhereDocWasFound() +{ + GetDocumentReply r1; + GetDocumentReply r2; + GetDocumentReply r3; + r2.setLastModified(12345ULL); + + ReplyMerger merger; + merger.merge(0, r1); + merger.merge(1, r2); + merger.merge(2, r3); + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_TRUE(ret.isSuccessful()); + ASSERT_FALSE(ret.hasGeneratedReply()); + ASSERT_EQUAL(1u, ret.getSuccessfulReplyIndex()); +} + +void +Test::assertReplyErrorsMatch(const mbus::Reply& r, + const std::vector<mbus::Error>& errors) +{ + ASSERT_EQUAL(r.getNumErrors(), errors.size()); + for (size_t i = 0; i < errors.size(); ++i) { + ASSERT_EQUAL(errors[i].getCode(), r.getError(i).getCode()); + ASSERT_EQUAL(errors[i].getMessage(), r.getError(i).getMessage()); + } +} + +void +Test::mergingZeroRepliesReturnsDefaultEmptyReply() +{ + ReplyMerger merger; + ReplyMerger::Result ret(merger.mergedReply()); + ASSERT_FALSE(ret.isSuccessful()); + ASSERT_TRUE(ret.hasGeneratedReply()); + std::unique_ptr<mbus::Reply> gen(ret.releaseGeneratedReply()); + ASSERT_TRUE(dynamic_cast<mbus::EmptyReply*>(gen.get()) != 0); + assertReplyErrorsMatch(*gen, {}); +} + +#ifdef RUN_TEST +# error Someone defined RUN_TEST already! Oh no! +#endif +#define RUN_TEST(f) \ + std::cerr << "running test case '" #f "'\n"; \ + f(); TEST_FLUSH(); + +int +Test::Main() +{ + TEST_INIT("replymerger_test"); + + RUN_TEST(mergingGenericRepliesWithNoErrorsPicksFirstReply); + RUN_TEST(mergingSingleReplyWithOneErrorReturnsEmptyReplyWithError); + RUN_TEST(mergingSingleReplyWithMultipleErrorsReturnsEmptyReplyWithAllErrors); + RUN_TEST(mergingMultipleRepliesWithMultipleErrorsReturnsEmptyReplyWithAllErrors); + RUN_TEST(returnIgnoredReplyWhenAllRepliesHaveOnlyIgnoredErrors); + RUN_TEST(successfulReplyTakesPrecedenceOverIgnoredReplyWhenNoErrors); + RUN_TEST(nonIgnoredErrorTakesPrecedence); + RUN_TEST(returnRemoveDocumentReplyWhereDocWasFound); + RUN_TEST(returnFirstRemoveDocumentReplyIfNoDocsWereFound); + RUN_TEST(returnUpdateDocumentReplyWhereDocWasFound); + RUN_TEST(returnGetDocumentReplyWhereDocWasFound); + RUN_TEST(mergingZeroRepliesReturnsDefaultEmptyReply); + + TEST_DONE(); +} |