ores.cpp.shell-command.operation_tests
Table of Contents
The tests for one operation model's shell command unit.
Three cases per declared command, and they are the same three whatever the
operation is because every command is built the same way: a signed-out session
is refused, a wrong number of positionals is refused and names the count it
expected, and a valid token vector gets as far as the transport. A test that
reached the server would need a connection; Not connected to NATS is the
proof that everything ahead of it, the guards and the request the tokens built,
was satisfied.
The command count is asserted from the projection, so a message the model adds appears here without an edit to the test.
1. Template
The full template source. Edit here and re-tangle with
compass build --direct tangle_codegen_templates to regenerate
library/templates/cpp_shell_operation_tests.cpp.mustache.
{{! GENERATED FILE — tangled from projects/ores.codegen/library/templates/ores.cpp.shell-command.operation_tests.org. Edit the org source. }}
{{{cpp_license}}}
{{#operation}}
#include "ores.logging/make_logger.hpp"
#include "ores.nats/service/nats_client.hpp"
#include "ores.shell/app/command_feedback.hpp"
#include "ores.shell/app/commands/{{component}}/{{entity_singular}}_operations_commands.hpp"
#include <catch2/catch_test_macros.hpp>
#include <cli/cli.h>
#include <algorithm>
#include <sstream>
#include <string>
#include <string_view>
#include <vector>
using ores::nats::service::nats_client;
using ores::shell::app::command_feedback;
using ores::shell::app::commands::{{entity_singular}}_operations_commands;
using namespace ores::logging;
namespace {
const std::string_view test_suite("ores.shell.{{component}}.tests");
const std::string tags("[commands]");
// One token per positional argument the command takes, in declaration order.
std::vector<std::string> tokens(const std::size_t count) {
return std::vector<std::string>(count, std::string{"sample"});
}
}
TEST_CASE("{{entity_singular}}_operations_registers_every_declared_command", tags) {
auto lg(make_logger(test_suite));
cli::Menu root_menu("root");
nats_client session;
{{entity_singular}}_operations_commands::register_commands(root_menu, session);
// The menu's completion list is the only public view of its children, so a
// command that is missing from it was never registered.
const auto completions = root_menu.GetCompletions("{{entity_singular}} ");
for (const auto& verb : {
{{#shell_commands}}
std::string{"{{entity_singular}} {{command}}"},
{{/shell_commands}}
})
CHECK(std::find(completions.begin(), completions.end(), verb) !=
completions.end());
BOOST_LOG_SEV(lg, debug) << "Registered {{shell_command_count}} command(s).";
}
{{#shell_commands}}
{{^public}}
TEST_CASE("{{entity_singular}}_operations_process_{{identifier}}_requires_a_session", tags) {
auto lg(make_logger(test_suite));
nats_client session;
std::ostringstream out;
command_feedback::reset();
{{entity_singular}}_operations_commands::process_{{identifier}}(
out, session, tokens({{positional_count}}));
BOOST_LOG_SEV(lg, debug) << "Output for a signed-out session: " << out.str();
CHECK(out.str().find("You must be logged in") != std::string::npos);
CHECK(command_feedback::failed());
}
{{/public}}
{{#positional_count}}
TEST_CASE("{{entity_singular}}_operations_process_{{identifier}}_reports_the_expected_count",
tags) {
auto lg(make_logger(test_suite));
nats_client session;
nats_client::login_info info;
info.username = "tester";
info.jwt = "token";
session.set_auth(std::move(info));
std::ostringstream out;
command_feedback::reset();
{{entity_singular}}_operations_commands::process_{{identifier}}(out, session, {});
BOOST_LOG_SEV(lg, debug) << "Output for an empty argument list: " << out.str();
CHECK(out.str().find("Expected {{positional_count}} arguments, got 0.") !=
std::string::npos);
CHECK(command_feedback::failed());
}
{{/positional_count}}
TEST_CASE("{{entity_singular}}_operations_process_{{identifier}}_reaches_the_transport", tags) {
auto lg(make_logger(test_suite));
nats_client session;
nats_client::login_info info;
info.username = "tester";
info.jwt = "token";
session.set_auth(std::move(info));
std::ostringstream out;
command_feedback::reset();
{{entity_singular}}_operations_commands::process_{{identifier}}(
out, session, tokens({{positional_count}}));
BOOST_LOG_SEV(lg, debug) << "Output for a valid token vector: " << out.str();
// Whether the command carries a token or not, everything ahead of the
// transport is satisfied, which is what the absence of a connection proves.
CHECK(out.str().find("Not connected to NATS") != std::string::npos);
CHECK(command_feedback::failed());
}
{{/shell_commands}}
{{/operation}}
2. See also
- Parent facet: ores.cpp.shell-command.
- Sibling archetype: ores.cpp.shell-command.command_header.
- Sibling archetype: ores.cpp.shell-command.command_implementation.
- Template variable reference — the variables this template reads.