aboutsummaryrefslogtreecommitdiffstats
path: root/documentapi/src/tests/messages/error_codes_test.cpp
blob: 5a99914f3f925f29bae113e7e8bd7623e2fc0663 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// Copyright Vespa.ai. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root.

#include <vespa/vespalib/testkit/testapp.h>
#include <vespa/documentapi/messagebus/documentprotocol.h>
#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include <exception>
#include <map>

using NamedErrorCodes = std::map<std::string, uint32_t>;

// DocumentAPI C++ module uses Ye Olde Test Framework.
class ErrorCodesTest : public vespalib::TestApp {
    int Main() override;

    void error_codes_match_java_definitions();
    void stringification_is_defined_for_all_error_codes();

    NamedErrorCodes all_document_protocol_error_codes();
    std::string path_prefixed(const std::string& file_name) const;
};

TEST_APPHOOK(ErrorCodesTest);

// ERROR_CODE_KV(FOO) -> {"FOO", DocumentProtocol::FOO}
#define ERROR_CODE_KV(code_name) \
    {#code_name, DocumentProtocol::code_name}

NamedErrorCodes
ErrorCodesTest::all_document_protocol_error_codes()
{
    using documentapi::DocumentProtocol;
    return {
        ERROR_CODE_KV(ERROR_MESSAGE_IGNORED),
        ERROR_CODE_KV(ERROR_POLICY_FAILURE),
        ERROR_CODE_KV(ERROR_DOCUMENT_NOT_FOUND),
        // Error code not consistently named between languages!
        // Java: ERROR_DOCUMENT_EXISTS, C++: ERROR_EXISTS
        // Names must be consistent in test or checking will fail.
        {"ERROR_DOCUMENT_EXISTS", DocumentProtocol::ERROR_EXISTS},
        ERROR_CODE_KV(ERROR_REJECTED),
        ERROR_CODE_KV(ERROR_NOT_IMPLEMENTED),
        ERROR_CODE_KV(ERROR_ILLEGAL_PARAMETERS),
        ERROR_CODE_KV(ERROR_UNKNOWN_COMMAND),
        ERROR_CODE_KV(ERROR_NO_SPACE),
        ERROR_CODE_KV(ERROR_IGNORED),
        ERROR_CODE_KV(ERROR_INTERNAL_FAILURE),
        ERROR_CODE_KV(ERROR_TEST_AND_SET_CONDITION_FAILED),
        ERROR_CODE_KV(ERROR_PROCESSING_FAILURE),
        ERROR_CODE_KV(ERROR_TIMESTAMP_EXIST),
        ERROR_CODE_KV(ERROR_NODE_NOT_READY),
        ERROR_CODE_KV(ERROR_WRONG_DISTRIBUTION),
        ERROR_CODE_KV(ERROR_ABORTED),
        ERROR_CODE_KV(ERROR_BUSY),
        ERROR_CODE_KV(ERROR_NOT_CONNECTED),
        ERROR_CODE_KV(ERROR_DISK_FAILURE),
        ERROR_CODE_KV(ERROR_IO_FAILURE),
        ERROR_CODE_KV(ERROR_BUCKET_NOT_FOUND),
        ERROR_CODE_KV(ERROR_BUCKET_DELETED),
        ERROR_CODE_KV(ERROR_STALE_TIMESTAMP),
        ERROR_CODE_KV(ERROR_SUSPENDED)
    };
}

#undef ERROR_CODE_KV

namespace {

std::string read_file(const std::string& file_name) {
    std::ifstream ifs(file_name);
    if (!ifs.is_open()) {
        throw std::runtime_error("file '" + file_name + "' does not exist");
    }
    std::ostringstream oss;
    oss << ifs.rdbuf();
    return oss.str();
}

void write_file(const std::string& file_name,
                const std::string& content)
{
    std::ofstream ofs(file_name, std::ios_base::trunc);
    ofs << content;
}

std::string to_sorted_key_value_string(const NamedErrorCodes& codes) {
    std::ostringstream os;
    bool emit_newline = false;
    for (auto& kv : codes) {
        if (emit_newline) {
            os << '\n';
        }
        os << kv.first << ' ' << kv.second;
        emit_newline = true;
    }
    return os.str();
}

} // anon ns

std::string
ErrorCodesTest::path_prefixed(const std::string& file_name) const {
    return TEST_PATH("../../../test/crosslanguagefiles/" + file_name);
}

void
ErrorCodesTest::error_codes_match_java_definitions()
{
    NamedErrorCodes codes(all_document_protocol_error_codes());
    auto cpp_golden_file = path_prefixed("HEAD-cpp-golden-error-codes.txt");
    auto cpp_golden_data = to_sorted_key_value_string(codes);
    write_file(cpp_golden_file, cpp_golden_data);

    auto java_golden_file = path_prefixed("HEAD-java-golden-error-codes.txt");
    auto java_golden_data = read_file(java_golden_file);
    EXPECT_EQUAL(cpp_golden_data, java_golden_data);
}

void
ErrorCodesTest::stringification_is_defined_for_all_error_codes()
{
    using documentapi::DocumentProtocol;
    NamedErrorCodes codes(all_document_protocol_error_codes());
    for (auto& kv : codes) {
        // Ugh, special casing due to divergence between Java and C++ naming.
        // Can we fix this without breaking anything in exciting ways?
        if (kv.second != DocumentProtocol::ERROR_EXISTS) {
            EXPECT_EQUAL(kv.first, "ERROR_" +
                    DocumentProtocol::getErrorName(kv.second));
        } else {
            EXPECT_EQUAL("EXISTS", DocumentProtocol::getErrorName(kv.second));
        }
    }
}

int
ErrorCodesTest::Main()
{
    TEST_INIT("error_codes_test");
    error_codes_match_java_definitions();
    TEST_FLUSH();
    stringification_is_defined_for_all_error_codes();
    TEST_FLUSH();
    TEST_DONE();
}