#include "configuration.h"
#include <chrono>
#include <filesystem>
#include <fstream>
#include <string>
#include <variant>
#include <vector>
#include <gtest/gtest.h>
namespace
{
class TemporaryConfig
{
public:
explicit TemporaryConfig(std::string content)
: path(std::filesystem::temp_directory_path() /
"status_tracker_test_config.yaml")
{
std::ofstream file(path);
file << content;
}
~TemporaryConfig()
{
std::filesystem::remove(path);
}
const std::filesystem::path path;
};
TEST(Configuration, LoadsGroupedProtocolSettings)
{
TemporaryConfig file(
"worker_count: 4\n"
"database_path: tracker.sqlite\n"
"unix_socket: /run/probius.sock\n"
"socket_permission: 0o660\n"
"groups:\n"
" - name: Public services\n"
" services:\n"
" blog:\n"
" name: Blog\n"
" description: Personal website\n"
" url: https://blog.example/\n"
" timeout_second: 3\n"
" interval:\n"
" value: 1\n"
" unit: minute\n"
" endpoint:\n"
" protocol: HTTP\n"
" ssh:\n"
" name: SSH\n"
" description: SSH server\n"
" interval:\n"
" value: 2\n"
" unit: hour\n"
" endpoint:\n"
" protocol: tcp\n"
" host: server.example\n"
" port: 22\n"
" timeout_second: 7\n"
" dns:\n"
" name: DNS\n"
" description: DNS server\n"
" interval:\n"
" value: 30\n"
" unit: second\n"
" endpoint:\n"
" protocol: UDP\n"
" host: dns.example\n"
" port: 53\n"
" payload: ping\n"
" gateway:\n"
" name: Gateway\n"
" description: Network gateway\n"
" interval:\n"
" value: 1\n"
" unit: day\n"
" endpoint:\n"
" protocol: icmp\n"
" host: 192.0.2.1\n");
auto configuration = Configuration::fromYaml(file.path);
ASSERT_TRUE(configuration)
<< (configuration ? "" : configuration.error().msg());
EXPECT_EQ(configuration->worker_count, 4);
EXPECT_EQ(configuration->database_path, "tracker.sqlite");
EXPECT_EQ(configuration->unix_socket, "/run/probius.sock");
ASSERT_TRUE(configuration->socket_permission);
EXPECT_EQ(*configuration->socket_permission, 0660);
ASSERT_EQ(configuration->groups.size(), 1);
ASSERT_EQ(configuration->groups.front().services.size(), 4);
const auto& services = configuration->groups.front().services;
EXPECT_EQ(services[0].timeout, std::chrono::seconds(3));
EXPECT_EQ(services[0].interval, std::chrono::minutes(1));
ASSERT_TRUE(std::holds_alternative<HttpEndpoint>(services[0].endpoint));
EXPECT_TRUE(std::get<HttpEndpoint>(services[0].endpoint).url.empty());
ASSERT_TRUE(services[0].url);
EXPECT_EQ(*services[0].url, "https://blog.example/");
EXPECT_EQ(services[1].timeout, std::chrono::seconds(7));
ASSERT_TRUE(std::holds_alternative<TcpEndpoint>(services[1].endpoint));
EXPECT_EQ(std::get<TcpEndpoint>(services[1].endpoint).port, 22);
ASSERT_TRUE(std::holds_alternative<UdpEndpoint>(services[2].endpoint));
EXPECT_EQ(std::get<UdpEndpoint>(services[2].endpoint).payload, "ping");
EXPECT_EQ(services[3].interval, std::chrono::hours(24));
ASSERT_TRUE(std::holds_alternative<IcmpEndpoint>(services[3].endpoint));
}
TEST(Configuration, RejectsInvalidValues)
{
const std::vector<std::string> contents = {
"worker_count: 0\n"
"database_path: db\n"
"groups: {}\n",
"worker_count: 1\n"
"database_path: db\n"
"groups:\n"
" - name: g\n"
" services:\n"
" x:\n"
" name: x\n"
" description: x\n"
" interval: {value: 1, unit: fortnight}\n"
" endpoint: {protocol: TCP, host: x, port: 1}\n",
"worker_count: 1\n"
"database_path: db\n"
"groups:\n"
" - name: g\n"
" services:\n"
" x:\n"
" name: x\n"
" description: x\n"
" interval: {value: 1, unit: minute}\n"
" endpoint: {protocol: TCP, host: x, port: 0}\n",
"worker_count: 1\n"
"database_path: db\n"
"unix_socket: /run/probius.sock\n"
"socket_permission: 0o668\n"
"groups: []\n",
"worker_count: 1\n"
"database_path: db\n"
"unix_socket: /run/probius.sock\n"
"socket_permission: 0o1000\n"
"groups: []\n"
};
for(const auto& content : contents)
{
TemporaryConfig file(content);
EXPECT_FALSE(Configuration::fromYaml(file.path));
}
}
TEST(Configuration, RejectsDuplicateServiceIDs)
{
TemporaryConfig file(
"worker_count: 1\n"
"database_path: db.sqlite\n"
"groups:\n"
" - name: first\n"
" services:\n"
" duplicate:\n"
" name: First\n"
" description: First service\n"
" interval: {value: 1, unit: minute}\n"
" endpoint: {protocol: ICMP, host: 192.0.2.1}\n"
" - name: second\n"
" services:\n"
" duplicate:\n"
" name: Second\n"
" description: Second service\n"
" interval: {value: 1, unit: minute}\n"
" endpoint: {protocol: ICMP, host: 192.0.2.2}\n");
EXPECT_FALSE(Configuration::fromYaml(file.path));
}
TEST(Configuration, RejectsMalformedYaml)
{
TemporaryConfig file("worker_count: [\n");
EXPECT_FALSE(Configuration::fromYaml(file.path));
}
}