// ---------------------------------------------------------------------- // File: response-formatter.cc // Author: Georgios Bitzes - CERN // ---------------------------------------------------------------------- /************************************************************************ * quarkdb - a redis-like highly available key-value store * * Copyright (C) 2016 CERN/Switzerland * * * * This program is free software: you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation, either version 3 of the License, or * * (at your option) any later version. * * * * This program is distributed in the hope that it will be useful, * * but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * * GNU General Public License for more details. * * * * You should have received a copy of the GNU General Public License * * along with this program. If not, see .* ************************************************************************/ #include "Formatter.hh" #include "redis/ArrayResponseBuilder.hh" #include "qclient/ResponseBuilder.hh" #include "qclient/QClient.hh" #include using namespace quarkdb; using namespace qclient; TEST(Response, T1) { ASSERT_EQ(Formatter::err("test").val, "-ERR test\r\n"); ASSERT_EQ(Formatter::ok().val, "+OK\r\n"); ASSERT_EQ(Formatter::pong().val, "+PONG\r\n"); ASSERT_EQ(Formatter::null().val, "$-1\r\n"); ASSERT_EQ(Formatter::status("test").val, "+test\r\n"); ASSERT_EQ(Formatter::noauth("asdf").val, "-NOAUTH asdf\r\n"); ASSERT_EQ( Formatter::multiply(Formatter::noauth("you shall not pass"), 3).val, "-NOAUTH you shall not pass\r\n-NOAUTH you shall not pass\r\n-NOAUTH you shall not pass\r\n" ); } TEST(ArrayResponseBuilder, BasicSanity) { ArrayResponseBuilder builder(3, false); ASSERT_THROW(builder.buildResponse(), FatalException); builder.push_back(Formatter::ok()); builder.push_back(Formatter::integer(999)); builder.push_back(Formatter::string("whee")); ASSERT_THROW(builder.push_back(Formatter::integer(123)), FatalException); RedisEncodedResponse resp = builder.buildResponse(); ASSERT_EQ(resp.val, "*3\r\n+OK\r\n:999\r\n$4\r\nwhee\r\n"); } TEST(Formatter, subscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::subscribe(false, "channel-name", 3).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"subscribe\"\n" "2) \"channel-name\"\n" "3) (integer) 3\n"); } TEST(Formatter, PushSubscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::subscribe(true, "channel-name", 3).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(ans->type, REDIS_REPLY_PUSH); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pubsub\"\n" "2) \"subscribe\"\n" "3) \"channel-name\"\n" "4) (integer) 3\n"); } TEST(Formatter, psubscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::psubscribe(false, "channel-*", 4).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"psubscribe\"\n" "2) \"channel-*\"\n" "3) (integer) 4\n"); } TEST(Formatter, PushPsubscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::psubscribe(true, "channel-*", 4).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(ans->type, REDIS_REPLY_PUSH); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pubsub\"\n" "2) \"psubscribe\"\n" "3) \"channel-*\"\n" "4) (integer) 4\n"); } TEST(Formatter, unsubscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::unsubscribe(false, "channel-name", 5).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"unsubscribe\"\n" "2) \"channel-name\"\n" "3) (integer) 5\n"); } TEST(Formatter, PushUnsubscribe) { qclient::ResponseBuilder builder; builder.feed(Formatter::unsubscribe(true, "channel-name", 5).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(ans->type, REDIS_REPLY_PUSH); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pubsub\"\n" "2) \"unsubscribe\"\n" "3) \"channel-name\"\n" "4) (integer) 5\n"); } TEST(Formatter, message) { qclient::ResponseBuilder builder; builder.feed(Formatter::message(false, "channel", "payload").val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"message\"\n" "2) \"channel\"\n" "3) \"payload\"\n"); } TEST(Formatter, PushMessage) { qclient::ResponseBuilder builder; builder.feed(Formatter::message(true, "channel", "payload").val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(ans->type, REDIS_REPLY_PUSH); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pubsub\"\n" "2) \"message\"\n" "3) \"channel\"\n" "4) \"payload\"\n"); } TEST(Formatter, pmessage) { qclient::ResponseBuilder builder; builder.feed(Formatter::pmessage(false, "pattern", "channel", "payload").val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pmessage\"\n" "2) \"pattern\"\n" "3) \"channel\"\n" "4) \"payload\"\n"); } TEST(Formatter, PushPMessage) { qclient::ResponseBuilder builder; builder.feed(Formatter::pmessage(true, "pattern", "channel", "payload").val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(ans->type, REDIS_REPLY_PUSH); ASSERT_EQ(qclient::describeRedisReply(ans), "1) \"pubsub\"\n" "2) \"pmessage\"\n" "3) \"pattern\"\n" "4) \"channel\"\n" "5) \"payload\"\n"); } TEST(Formatter, VersionedVector) { qclient::ResponseBuilder builder; builder.feed(Formatter::versionedVector(999, {"one", "two", "three", "four" } ).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) (integer) 999\n" "2) 1) \"one\"\n" " 2) \"two\"\n" " 3) \"three\"\n" " 4) \"four\"\n"); } TEST(Formatter, EmptyVersionedVector) { qclient::ResponseBuilder builder; builder.feed(Formatter::versionedVector(888, {} ).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) (integer) 888\n" "2) (empty list or set)\n"); } TEST(Formatter, VectorOfVectors) { std::vector headers; std::vector> data; headers.emplace_back("SECTION 1"); ASSERT_THROW(Formatter::vectorsWithHeaders(headers, data), FatalException); std::vector vec = { "one", "two", "three" }; data.emplace_back(vec); headers.emplace_back("SECTION 2"); vec = { "four", "five", "six" }; data.emplace_back(vec); redisReplyPtr ans = qclient::ResponseBuilder::parseRedisEncodedString(Formatter::vectorsWithHeaders(headers, data).val); ASSERT_EQ(qclient::describeRedisReply(ans), "1) 1) SECTION 1\n" " 2) 1) one\n" " 2) two\n" " 3) three\n" "2) 1) SECTION 2\n" " 2) 1) four\n" " 2) five\n" " 3) six\n"); } TEST(Formatter, NodeHealth) { std::vector indicators; indicators.emplace_back(HealthStatus::kRed, "CHICKEN-INVASION", "Imminent"); indicators.emplace_back(HealthStatus::kGreen, "BEARS", "Sleeping"); NodeHealth nodeHealth("1.33.7", "example.com:7777", indicators); qclient::ResponseBuilder builder; builder.feed(Formatter::nodeHealth(nodeHealth).val); redisReplyPtr ans; ASSERT_EQ(builder.pull(ans), qclient::ResponseBuilder::Status::kOk); ASSERT_EQ(qclient::describeRedisReply(ans), "1) NODE-HEALTH RED\n" "2) NODE example.com:7777\n" "3) VERSION 1.33.7\n" "4) ----------\n" "5) RED >> CHICKEN-INVASION Imminent\n" "6) GREEN >> BEARS Sleeping\n" ); } TEST(Formatter, VHashRevision) { std::vector contents = { "key1", "value1", "key2", "value2" }; std::vector> batch = { {contents[0], contents[1]}, {contents[2], contents[3]} }; ASSERT_EQ(qclient::ResponseBuilder::parseAndDescribeRedisEncodedString(Formatter::vhashRevision(5, batch).val), "1) (integer) 5\n" "2) 1) \"key1\"\n" " 2) \"value1\"\n" " 3) \"key2\"\n" " 4) \"value2\"\n" ); }