1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2011-2020 Couchbase, Inc.
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include "config.h"
19 #include <gtest/gtest.h>
20 #include <libcouchbase/couchbase.h>
21 #include "jsparse/parser.h"
22 #include "contrib/lcb-jsoncpp/lcb-jsoncpp.h"
23 #include "t_jsparse.h"
24 
25 class JsonParseTest : public ::testing::Test
26 {
27 };
28 
29 using namespace lcb::jsparse;
30 
iov2s(const lcb_IOV &iov)31 static std::string iov2s(const lcb_IOV &iov)
32 {
33     return std::string(reinterpret_cast<const char *>(iov.iov_base), iov.iov_len);
34 }
35 
36 struct Context : Parser::Actions {
37     lcb_STATUS rc;
38     bool received_done;
39     std::string meta;
40     std::vector<std::string> rows;
ContextContext41     Context()
42     {
43         reset();
44     }
resetContext45     void reset()
46     {
47         rc = LCB_SUCCESS;
48         received_done = false;
49         meta.clear();
50         rows.clear();
51     }
JSPARSE_on_rowContext52     void JSPARSE_on_row(const Row &row)
53     {
54         rows.push_back(iov2s(row.row));
55     }
JSPARSE_on_completeContext56     void JSPARSE_on_complete(const std::string &s)
57     {
58         meta.assign(s);
59         received_done = true;
60     }
JSPARSE_on_errorContext61     void JSPARSE_on_error(const std::string &)
62     {
63         rc = LCB_ERR_PROTOCOL_ERROR;
64         received_done = true;
65     }
66 };
67 
validateJsonRows(const char *txt, size_t ntxt, Parser::Mode mode)68 static bool validateJsonRows(const char *txt, size_t ntxt, Parser::Mode mode)
69 {
70     Context cx;
71     Parser parser(mode, &cx);
72 
73     for (size_t ii = 0; ii < ntxt; ii++) {
74         parser.feed(txt + ii, 1);
75     }
76     EXPECT_EQ(LCB_SUCCESS, cx.rc);
77 
78     lcb_IOV out;
79     parser.get_postmortem(out);
80     EXPECT_EQ(cx.meta, iov2s(out));
81     Json::Value root;
82     EXPECT_TRUE(Json::Reader().parse(cx.meta, root));
83     return true;
84 }
85 
validateBadParse(const char *txt, size_t ntxt, Parser::Mode mode)86 static bool validateBadParse(const char *txt, size_t ntxt, Parser::Mode mode)
87 {
88     Context cx;
89     Parser p(mode, &cx);
90     p.feed(txt, ntxt);
91     EXPECT_EQ(LCB_ERR_PROTOCOL_ERROR, cx.rc);
92     return true;
93 }
94 
TEST_F(JsonParseTest, testFTS)95 TEST_F(JsonParseTest, testFTS)
96 {
97     ASSERT_TRUE(validateJsonRows(JSON_fts_good, sizeof(JSON_fts_good), Parser::MODE_FTS));
98     ASSERT_TRUE(validateBadParse(JSON_fts_bad, sizeof(JSON_fts_bad), Parser::MODE_FTS));
99     ASSERT_TRUE(validateBadParse(JSON_fts_bad2, sizeof(JSON_fts_bad2), Parser::MODE_FTS));
100 }
101 
TEST_F(JsonParseTest, testN1QL)102 TEST_F(JsonParseTest, testN1QL)
103 {
104     ASSERT_TRUE(validateJsonRows(JSON_n1ql_nonempty, sizeof(JSON_n1ql_nonempty), Parser::MODE_N1QL));
105     ASSERT_TRUE(validateJsonRows(JSON_n1ql_empty, sizeof(JSON_n1ql_empty), Parser::MODE_N1QL));
106     ASSERT_TRUE(validateBadParse(JSON_n1ql_bad, sizeof(JSON_n1ql_bad), Parser::MODE_N1QL));
107 }
108 
TEST_F(JsonParseTest, testInvalidJSON)109 TEST_F(JsonParseTest, testInvalidJSON)
110 {
111     std::string input = "503 Service Unavailable";
112     Json::Value meta;
113     ASSERT_TRUE(Json::Reader().parse(input.c_str(), input.c_str() + input.size(), meta));
114     ASSERT_FALSE(Json::Reader(Json::Features::strictMode()).parse(input.c_str(), input.c_str() + input.size(), meta));
115 }