Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 106 additions & 4 deletions include/kcenon/messaging/error/messaging_error_category.h
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@
* @code
* #include <kcenon/messaging/error/messaging_error_category.h>
*
* // Create a typed error code
* auto ec = kcenon::messaging::make_messaging_error_code(
* kcenon::messaging::error::queue_full);
* // Preferred: use codes enum with make_typed_error_code
* auto ec = make_typed_error_code(messaging_error_category::queue_full);
*
* // Use with Result<T>
* return Result<void>::err(ec);
* return Result<int>::err(ec);
*
* // Alternative: use make_messaging_error_code with int constants
* auto ec2 = make_messaging_error_code(error::queue_full);
*
* // Check category
* if (ec.category() == messaging_error_category::instance()) {
Expand Down Expand Up @@ -55,6 +57,86 @@ namespace kcenon::messaging {
*/
class messaging_error_category : public common::error_category {
public:
/**
* @brief Typed error code enumeration for messaging_system
*
* Mirrors the constexpr int values from error_codes.h, enabling
* type-safe error code construction via make_typed_error_code().
* Follows the same pattern as common_error_category::codes.
*
* @code
* auto ec = make_typed_error_code(messaging_error_category::queue_full);
* return Result<int>::err(ec);
* @endcode
*/
enum codes : int {
// Message errors (-700 to -719)
invalid_message = error::invalid_message,
message_too_large = error::message_too_large,
message_expired = error::message_expired,
invalid_payload = error::invalid_payload,
message_serialization_failed = error::message_serialization_failed,
message_deserialization_failed = error::message_deserialization_failed,

// Task errors (-706 to -715)
task_not_found = error::task_not_found,
task_already_running = error::task_already_running,
task_cancelled = error::task_cancelled,
task_timeout = error::task_timeout,
task_failed = error::task_failed,
task_handler_not_found = error::task_handler_not_found,
task_spawner_not_configured = error::task_spawner_not_configured,
task_invalid_argument = error::task_invalid_argument,
task_operation_failed = error::task_operation_failed,
schedule_already_exists = error::schedule_already_exists,

// Routing errors (-720 to -739)
routing_failed = error::routing_failed,
unknown_topic = error::unknown_topic,
no_subscribers = error::no_subscribers,
invalid_topic_pattern = error::invalid_topic_pattern,
route_not_found = error::route_not_found,

// Queue errors (-740 to -759)
queue_full = error::queue_full,
queue_empty = error::queue_empty,
queue_stopped = error::queue_stopped,
enqueue_failed = error::enqueue_failed,
dequeue_failed = error::dequeue_failed,
queue_timeout = error::queue_timeout,
dlq_full = error::dlq_full,
dlq_empty = error::dlq_empty,
dlq_message_not_found = error::dlq_message_not_found,
dlq_replay_failed = error::dlq_replay_failed,
dlq_not_configured = error::dlq_not_configured,

// Subscription errors (-760 to -779)
subscription_failed = error::subscription_failed,
subscription_not_found = error::subscription_not_found,
duplicate_subscription = error::duplicate_subscription,
unsubscribe_failed = error::unsubscribe_failed,
invalid_subscription = error::invalid_subscription,

// Publishing errors (-780 to -799)
publication_failed = error::publication_failed,
no_route_found = error::no_route_found,
message_rejected = error::message_rejected,
broker_unavailable = error::broker_unavailable,
broker_not_started = error::broker_not_started,
already_running = error::already_running,
not_running = error::not_running,
backend_not_ready = error::backend_not_ready,
request_timeout = error::request_timeout,
not_supported = error::not_supported,

// Transport errors (-790 to -794)
connection_failed = error::connection_failed,
send_timeout = error::send_timeout,
receive_timeout = error::receive_timeout,
authentication_failed = error::authentication_failed,
not_connected = error::not_connected,
};

/**
* @brief Returns the singleton instance
*
Expand Down Expand Up @@ -111,4 +193,24 @@ inline common::typed_error_code make_messaging_error_code(int code) noexcept {
return common::typed_error_code(code, messaging_error_category::instance());
}

/**
* @brief Create a typed_error_code from messaging_error_category::codes enum
*
* ADL-discoverable overload that follows the common_system pattern.
* Enables type-safe error code construction without specifying the category.
*
* @param code Messaging error code enum value
* @return typed_error_code with messaging_error_category
*
* @code
* auto ec = make_typed_error_code(messaging_error_category::queue_full);
* return Result<int>::err(ec);
* @endcode
*/
inline common::typed_error_code make_typed_error_code(
messaging_error_category::codes code) noexcept {
return common::typed_error_code(
static_cast<int>(code), messaging_error_category::instance());
}

} // namespace kcenon::messaging
107 changes: 107 additions & 0 deletions test/unit/core/test_messaging_error_category.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -145,3 +145,110 @@ TEST(MessagingErrorCategoryTest, ResultIntegration) {

EXPECT_FALSE(result.is_ok());
}

// =============================================================================
// codes enum Tests
// =============================================================================

TEST(MessagingErrorCategoryTest, CodesEnumValuesMatchConstants) {
// Verify enum values are identical to constexpr int values
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::invalid_message),
msg::error::invalid_message);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::queue_full),
msg::error::queue_full);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::routing_failed),
msg::error::routing_failed);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::subscription_failed),
msg::error::subscription_failed);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::publication_failed),
msg::error::publication_failed);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::connection_failed),
msg::error::connection_failed);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::task_not_found),
msg::error::task_not_found);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::dlq_full),
msg::error::dlq_full);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::not_supported),
msg::error::not_supported);
EXPECT_EQ(static_cast<int>(msg::messaging_error_category::not_connected),
msg::error::not_connected);
}

TEST(MessagingErrorCategoryTest, MakeTypedErrorCodeFromEnum) {
auto ec = msg::make_typed_error_code(
msg::messaging_error_category::queue_full);

EXPECT_EQ(ec.value(), msg::error::queue_full);
EXPECT_EQ(ec.category().name(), "messaging");
EXPECT_EQ(ec.message(), "Message queue full");
EXPECT_TRUE(static_cast<bool>(ec));
}

TEST(MessagingErrorCategoryTest, EnumCodesAllGroups) {
// Test one code from each error group via the enum
auto msg_ec = msg::make_typed_error_code(
msg::messaging_error_category::invalid_message);
EXPECT_EQ(msg_ec.value(), -700);

auto task_ec = msg::make_typed_error_code(
msg::messaging_error_category::task_timeout);
EXPECT_EQ(task_ec.value(), -709);

auto route_ec = msg::make_typed_error_code(
msg::messaging_error_category::unknown_topic);
EXPECT_EQ(route_ec.value(), -721);

auto queue_ec = msg::make_typed_error_code(
msg::messaging_error_category::queue_stopped);
EXPECT_EQ(queue_ec.value(), -742);

auto sub_ec = msg::make_typed_error_code(
msg::messaging_error_category::duplicate_subscription);
EXPECT_EQ(sub_ec.value(), -762);

auto pub_ec = msg::make_typed_error_code(
msg::messaging_error_category::broker_unavailable);
EXPECT_EQ(pub_ec.value(), -783);

auto transport_ec = msg::make_typed_error_code(
msg::messaging_error_category::not_connected);
EXPECT_EQ(transport_ec.value(), -794);
}

TEST(MessagingErrorCategoryTest, EnumResultIntegration) {
// Direct construction via Result<T> constructor
auto ec = msg::make_typed_error_code(
msg::messaging_error_category::broker_not_started);
cmn::Result<std::string> result{ec};

EXPECT_FALSE(result.is_ok());

// Factory method
auto result2 = cmn::Result<int>::err(
msg::make_typed_error_code(
msg::messaging_error_category::queue_full));

EXPECT_FALSE(result2.is_ok());
}

TEST(MessagingErrorCategoryTest, EnumAndIntProduceSameErrorCode) {
// Verify enum-based and int-based construction produce equal results
auto from_enum = msg::make_typed_error_code(
msg::messaging_error_category::queue_full);
auto from_int = msg::make_messaging_error_code(msg::error::queue_full);

EXPECT_EQ(from_enum, from_int);
EXPECT_EQ(from_enum.value(), from_int.value());
EXPECT_EQ(from_enum.category(), from_int.category());
EXPECT_EQ(from_enum.message(), from_int.message());
}

TEST(MessagingErrorCategoryTest, EnumCrossCategoryInequality) {
auto messaging_ec = msg::make_typed_error_code(
msg::messaging_error_category::invalid_message);
auto common_ec = cmn::make_typed_error_code(
cmn::common_error_category::internal_error);

// Same underlying int value could exist but different categories
EXPECT_NE(messaging_ec, common_ec);
}
Loading