Skip to content

Commit a20ccae

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 b486e4b commit a20ccae

12 files changed

+847
-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: 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-2.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+
15+
BOOST_AUTO_TEST_CASE(connect)
16+
{
17+
ResumeWriter();
18+
19+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
20+
21+
Accept();
22+
auto resp = GetSplitDecodedRequestBody();
23+
SendResponse();
24+
25+
// Just some basic sanity tests. It's not important to check if everything is entirely
26+
// correct here.
27+
BOOST_REQUIRE_GT(resp->GetLength(), 1);
28+
Dictionary::Ptr cr = resp->Get(1);
29+
BOOST_CHECK(cr->Contains("@timestamp"));
30+
BOOST_CHECK_EQUAL(cr->Get("check_command"), "dummy");
31+
BOOST_CHECK_EQUAL(cr->Get("host"), "h1");
32+
PauseWriter();
33+
}
34+
35+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
36+
{
37+
ResumeWriter();
38+
39+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [](const CheckResult::Ptr& cr) {
40+
cr->SetOutput(GetRandomString("####", 1024UL * 1024));
41+
});
42+
43+
// Accept the connection, but don't read from it to leave the client hanging.
44+
Accept();
45+
GetDataUntil("####");
46+
47+
// Now try to pause.
48+
PauseWriter();
49+
50+
REQUIRE_LOG_MESSAGE("Operation cancelled\\.", 10s);
51+
REQUIRE_LOG_MESSAGE("'ElasticsearchWriter' paused\\.", 10s);
52+
}
53+
54+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-gelfwriter.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-2.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+
15+
BOOST_AUTO_TEST_CASE(connect)
16+
{
17+
ResumeWriter();
18+
19+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
20+
21+
Accept();
22+
Dictionary::Ptr resp = JsonDecode(GetDataUntil('\0'));
23+
24+
// Just some basic sanity tests. It's not important to check if everything is entirely
25+
// correct here.
26+
BOOST_CHECK_CLOSE(resp->Get("timestamp").Get<double>(), Utility::GetTime(), 0.5);
27+
BOOST_CHECK_EQUAL(resp->Get("_check_command"), "dummy");
28+
BOOST_CHECK_EQUAL(resp->Get("_hostname"), "h1");
29+
PauseWriter();
30+
}
31+
32+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
33+
{
34+
ResumeWriter();
35+
36+
// Make GelfWriter fill up the connection's buffer with a huge check-result.
37+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [](const CheckResult::Ptr& cr) {
38+
cr->SetOutput(GetRandomString("####", 1024UL * 1024));
39+
});
40+
41+
// Accept the connection, but only read far enough so we know the writer is now stuck.
42+
Accept();
43+
GetDataUntil("####");
44+
45+
// Now try to pause.
46+
PauseWriter();
47+
48+
REQUIRE_LOG_MESSAGE("Operation cancelled\\.", 1s);
49+
REQUIRE_LOG_MESSAGE("'GelfWriter' paused\\.", 1s);
50+
}
51+
52+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-graphitewriter.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-2.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(
13+
perfdata_graphitewriter,
14+
PerfdataWriterFixture<GraphiteWriter>,
15+
*boost::unit_test::label("perfdata")
16+
)
17+
18+
BOOST_AUTO_TEST_CASE(connect)
19+
{
20+
ResumeWriter();
21+
22+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
23+
24+
Accept();
25+
auto msg = GetDataUntil('\n');
26+
27+
// Just some basic sanity tests. It's not important to check if everything is entirely correct here.
28+
std::string_view cmpStr{"icinga2.h1.host.dummy.perfdata.thing.value 42"};
29+
BOOST_REQUIRE_EQUAL(msg.substr(0, cmpStr.length()), cmpStr);
30+
PauseWriter();
31+
}
32+
33+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
34+
{
35+
ResumeWriter();
36+
37+
// Make GraphiteWriter send a huge message that fills up the connection's buffer.
38+
ReceiveCheckResults(1, ServiceState::ServiceCritical, [&](const CheckResult::Ptr& cr) {
39+
cr->GetPerformanceData()->Add(new PerfdataValue{GetRandomString("aaaa", 24UL * 1024 * 1024), 1});
40+
});
41+
42+
// Accept the connection, but don't read from it to leave the client hanging.
43+
Accept();
44+
GetDataUntil("aaaa");
45+
46+
// Now try to pause.
47+
PauseWriter();
48+
49+
REQUIRE_LOG_MESSAGE("Operation Cancelled\\.", 10s);
50+
REQUIRE_LOG_MESSAGE("'GraphiteWriter' paused\\.", 10s);
51+
}
52+
53+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-influxdbwriter.cpp

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
// SPDX-FileCopyrightText: 2026 Icinga GmbH <https://icinga.com>
2+
// SPDX-License-Identifier: GPL-2.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+
14+
BOOST_AUTO_TEST_CASE(connect)
15+
{
16+
ResumeWriter();
17+
18+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
19+
20+
Accept();
21+
auto req = GetSplitRequestBody(',');
22+
SendResponse(boost::beast::http::status::no_content);
23+
24+
// Just some basic sanity tests. It's not important to check if everything is entirely
25+
// correct here.
26+
BOOST_REQUIRE_EQUAL(req.size(), 3);
27+
BOOST_CHECK_EQUAL(req[0], "dummy");
28+
BOOST_CHECK_EQUAL(req[1], "hostname=h1");
29+
std::string_view perfData = "metric=thing value=42";
30+
BOOST_CHECK_EQUAL(req[2].substr(0, perfData.length()), perfData);
31+
PauseWriter();
32+
}
33+
34+
BOOST_AUTO_TEST_CASE(pause_with_pending_work)
35+
{
36+
ResumeWriter();
37+
38+
// Make Influxdb2Writer fill up the connection's buffer with a huge check-result.
39+
ReceiveCheckResults(1, ServiceState::ServiceCritical);
40+
41+
// Accept the connection, but only read far enough so we know the writer is now stuck.
42+
Accept();
43+
44+
// Now try to pause.
45+
PauseWriter();
46+
47+
REQUIRE_LOG_MESSAGE("Operation cancelled\\.", 1s);
48+
REQUIRE_LOG_MESSAGE("'Influxdb2Writer' paused\\.", 1s);
49+
}
50+
51+
BOOST_AUTO_TEST_SUITE_END()

test/perfdata-opentsdbwriter.cpp

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

0 commit comments

Comments
 (0)