Skip to content

Commit 2eff710

Browse files
Add unit-tests for PerfdataWriterConnection
There's a set of two tests for each perfdatawriter, just to make sure they can connect and send data that looks reasonably correct, and to make sure pausing actually works while the connection is stuck. Then there's a more in-depth suite of tests for PerfdataWriterConnection itself, to verify that connection handling works well in all types of scenarios.
1 parent 36c8950 commit 2eff710

12 files changed

+914
-2
lines changed

test/CMakeLists.txt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,18 @@ if(ICINGA2_WITH_NOTIFICATION)
140140
)
141141
endif()
142142

143+
if(ICINGA2_WITH_PERFDATA)
144+
list(APPEND base_test_SOURCES
145+
perfdata-elasticsearchwriter.cpp
146+
perfdata-gelfwriter.cpp
147+
perfdata-graphitewriter.cpp
148+
perfdata-influxdbwriter.cpp
149+
perfdata-opentsdbwriter.cpp
150+
perfdata-perfdatawriterconnection.cpp
151+
$<TARGET_OBJECTS:perfdata>
152+
)
153+
endif()
154+
143155
if(ICINGA2_UNITY_BUILD)
144156
mkunity_target(base test base_test_SOURCES)
145157
endif()
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,62 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include <BoostTestTargetConfig.h>
5+
#include "perfdata/elasticsearchwriter.hpp"
6+
#include "test/base-testloggerfixture.hpp"
7+
#include "test/perfdata-perfdatawriterfixture.hpp"
8+
#include "test/utils.hpp"
9+
10+
using namespace icinga;
11+
12+
BOOST_FIXTURE_TEST_SUITE(perfdata_elasticsearchwriter, PerfdataWriterFixture<ElasticsearchWriter>,
13+
*boost::unit_test::label("perfdata")
14+
*boost::unit_test::label("network")
15+
)
16+
17+
BOOST_AUTO_TEST_CASE(connect)
18+
{
19+
ResumeWriter();
20+
21+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
22+
23+
Accept();
24+
auto resp = GetSplitDecodedRequestBody();
25+
SendResponse();
26+
27+
// ElasticsearchWriter wants to send the same message twice, once for the check result
28+
// and once for the "state change".
29+
resp = GetSplitDecodedRequestBody();
30+
SendResponse();
31+
32+
// Just some basic sanity tests. It's not important to check if everything is entirely
33+
// correct here.
34+
BOOST_REQUIRE_GT(resp->GetLength(), 1);
35+
Dictionary::Ptr cr = resp->Get(1);
36+
BOOST_CHECK(cr->Contains("@timestamp"));
37+
BOOST_CHECK_EQUAL(cr->Get("check_command"), "dummy");
38+
BOOST_CHECK_EQUAL(cr->Get("host"), "h1");
39+
40+
PauseWriter();
41+
}
42+
43+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
44+
{
45+
ResumeWriter();
46+
47+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [](const CheckResult::Ptr& cr) {
48+
cr->SetOutput(GetRandomString("####", 1024UL * 1024));
49+
});
50+
51+
// Accept the connection, but don't read from it to leave the client hanging.
52+
Accept();
53+
GetDataUntil("####");
54+
55+
// Now try to pause.
56+
PauseWriter();
57+
58+
REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s);
59+
REQUIRE_LOG_MESSAGE("'ElasticsearchWriter' paused\\.", 10s);
60+
}
61+
62+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-gelfwriter.cpp

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include <BoostTestTargetConfig.h>
5+
#include "perfdata/gelfwriter.hpp"
6+
#include "test/base-testloggerfixture.hpp"
7+
#include "test/perfdata-perfdatawriterfixture.hpp"
8+
#include "test/utils.hpp"
9+
10+
using namespace icinga;
11+
12+
BOOST_FIXTURE_TEST_SUITE(perfdata_gelfwriter, PerfdataWriterFixture<GelfWriter>,
13+
*boost::unit_test::label("perfdata")
14+
*boost::unit_test::label("network")
15+
)
16+
17+
BOOST_AUTO_TEST_CASE(connect)
18+
{
19+
ResumeWriter();
20+
21+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
22+
23+
Accept();
24+
Dictionary::Ptr resp = JsonDecode(GetDataUntil('\0'));
25+
26+
// Just some basic sanity tests. It's not important to check if everything is entirely
27+
// correct here.
28+
BOOST_CHECK_CLOSE(resp->Get("timestamp").Get<double>(), Utility::GetTime(), 0.5);
29+
BOOST_CHECK_EQUAL(resp->Get("_check_command"), "dummy");
30+
BOOST_CHECK_EQUAL(resp->Get("_hostname"), "h1");
31+
PauseWriter();
32+
}
33+
34+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
35+
{
36+
ResumeWriter();
37+
38+
// Make GelfWriter fill up the connection's buffer with a huge check-result.
39+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [](const CheckResult::Ptr& cr) {
40+
cr->SetOutput(GetRandomString("####", 1024UL * 1024));
41+
});
42+
43+
// Accept the connection, but only read far enough so we know the writer is now stuck.
44+
Accept();
45+
GetDataUntil("####");
46+
47+
// Now try to pause.
48+
PauseWriter();
49+
50+
REQUIRE_LOG_MESSAGE("Connection stopped\\.", 1s);
51+
REQUIRE_LOG_MESSAGE("'GelfWriter' paused\\.", 1s);
52+
}
53+
54+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-graphitewriter.cpp

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include <BoostTestTargetConfig.h>
5+
#include "perfdata/graphitewriter.hpp"
6+
#include "test/base-testloggerfixture.hpp"
7+
#include "test/perfdata-perfdatawriterfixture.hpp"
8+
#include "test/utils.hpp"
9+
10+
using namespace icinga;
11+
12+
BOOST_FIXTURE_TEST_SUITE(perfdata_graphitewriter, PerfdataWriterFixture<GraphiteWriter>,
13+
*boost::unit_test::label("perfdata")
14+
*boost::unit_test::label("network")
15+
)
16+
17+
BOOST_AUTO_TEST_CASE(connect)
18+
{
19+
ResumeWriter();
20+
21+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
22+
23+
Accept();
24+
auto msg = GetDataUntil('\n');
25+
26+
// Just some basic sanity tests. It's not important to check if everything is entirely correct here.
27+
std::string_view cmpStr{"icinga2.h1.host.dummy.perfdata.thing.value 42"};
28+
BOOST_REQUIRE_EQUAL(msg.substr(0, cmpStr.length()), cmpStr);
29+
PauseWriter();
30+
}
31+
32+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
33+
{
34+
ResumeWriter();
35+
36+
// Make GraphiteWriter send a huge message that fills up the connection's buffer.
37+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [&](const CheckResult::Ptr& cr) {
38+
cr->GetPerformanceData()->Add(new PerfdataValue{GetRandomString("aaaa", 24UL * 1024 * 1024), 1});
39+
});
40+
41+
// Accept the connection, but don't read from it to leave the client hanging.
42+
Accept();
43+
GetDataUntil("aaaa");
44+
45+
// Now try to pause.
46+
PauseWriter();
47+
48+
REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s);
49+
REQUIRE_LOG_MESSAGE("'GraphiteWriter' paused\\.", 10s);
50+
}
51+
52+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-influxdbwriter.cpp

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include <BoostTestTargetConfig.h>
5+
#include "perfdata/influxdb2writer.hpp"
6+
#include "test/base-testloggerfixture.hpp"
7+
#include "test/perfdata-perfdatawriterfixture.hpp"
8+
9+
using namespace icinga;
10+
11+
BOOST_FIXTURE_TEST_SUITE(perfdata_influxdbwriter, PerfdataWriterFixture<Influxdb2Writer>,
12+
*boost::unit_test::label("perfdata")
13+
*boost::unit_test::label("network")
14+
)
15+
16+
BOOST_AUTO_TEST_CASE(connect)
17+
{
18+
ResumeWriter();
19+
20+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
21+
22+
Accept();
23+
auto req = GetSplitRequestBody(',');
24+
SendResponse(boost::beast::http::status::no_content);
25+
26+
// Just some basic sanity tests. It's not important to check if everything is entirely
27+
// correct here.
28+
BOOST_REQUIRE_EQUAL(req.size(), 3);
29+
BOOST_CHECK_EQUAL(req[0], "dummy");
30+
BOOST_CHECK_EQUAL(req[1], "hostname=h1");
31+
std::string_view perfData = "metric=thing value=42";
32+
BOOST_CHECK_EQUAL(req[2].substr(0, perfData.length()), perfData);
33+
PauseWriter();
34+
}
35+
36+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
37+
{
38+
ResumeWriter();
39+
40+
// Make Influxdb2Writer fill up the connection's buffer with a huge check-result.
41+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
42+
43+
// Accept the connection, but only read far enough so we know the writer is now stuck.
44+
Accept();
45+
46+
// Now try to pause.
47+
PauseWriter();
48+
49+
REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s);
50+
REQUIRE_LOG_MESSAGE("'Influxdb2Writer' paused\\.", 1s);
51+
}
52+
53+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-opentsdbwriter.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-3.0-or-later
3+
4+
#include <BoostTestTargetConfig.h>
5+
#include "perfdata/opentsdbwriter.hpp"
6+
#include "test/base-testloggerfixture.hpp"
7+
#include "test/perfdata-perfdatawriterfixture.hpp"
8+
#include "test/utils.hpp"
9+
10+
using namespace icinga;
11+
12+
BOOST_FIXTURE_TEST_SUITE(perfdata_opentsdbwriter, PerfdataWriterFixture<OpenTsdbWriter>,
13+
*boost::unit_test::label("perfdata")
14+
*boost::unit_test::label("network")
15+
)
16+
17+
BOOST_AUTO_TEST_CASE(connect)
18+
{
19+
ResumeWriter();
20+
21+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
22+
23+
Accept();
24+
auto msg = GetDataUntil('\n');
25+
std::vector<std::string> splitMsg;
26+
boost::split(splitMsg, msg, boost::is_any_of(" "));
27+
28+
// Just some basic sanity tests. It's not important to check if everything is entirely correct here.
29+
BOOST_REQUIRE_EQUAL(splitMsg.size(), 5);
30+
BOOST_REQUIRE_EQUAL(splitMsg[0], "put");
31+
BOOST_REQUIRE_EQUAL(splitMsg[1], "icinga.host.state");
32+
BOOST_REQUIRE_CLOSE(boost::lexical_cast<double>(splitMsg[2]), Utility::GetTime(), 1);
33+
BOOST_REQUIRE_EQUAL(splitMsg[3], "1");
34+
BOOST_REQUIRE_EQUAL(splitMsg[4], "host=h1");
35+
PauseWriter();
36+
}
37+
38+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
39+
{
40+
ResumeWriter();
41+
42+
// Make OpenTsdbWriter send a huge message that fills up the connection's buffer.
43+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [&](const CheckResult::Ptr& cr) {
44+
cr->GetPerformanceData()->Add(new PerfdataValue{GetRandomString("aaaaa", 24ul * 1024 * 1024), 1});
45+
});
46+
47+
// Accept the connection, and read until OpenTsdbWriter has started sending the large part
48+
// of the PerfdataValue sent above.
49+
Accept();
50+
GetDataUntil("aaaaa");
51+
52+
// Now stop reading and try to pause OpenTsdbWriter.
53+
PauseWriter();
54+
55+
REQUIRE_LOG_MESSAGE("Connection stopped\\.", 10s);
56+
REQUIRE_LOG_MESSAGE("'OpenTsdbWriter' paused\\.", 10s);
57+
}
58+
59+
BOOST_AUTO_TEST_SUITE_END()

0 commit comments

Comments
 (0)