use rivet_error::*;
use serde::{Deserialize, Serialize};
fn init_test() {
unsafe {
std::env::set_var("RIVET_ERROR_OUTPUT_DIR", "/tmp/rivet_error_test");
}
}
#[derive(RivetError)]
#[error("test", "simple_error", "This is a simple test error")]
struct TestError;
#[derive(RivetError, Serialize, Deserialize)]
#[error(
"test",
"meta_error",
"Default message",
"Error with value {value} and name {name}"
)]
struct TestMetaError {
value: i64,
name: String,
}
#[test]
fn test_simple_error_build() {
init_test();
let error = TestError.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "simple_error");
assert_eq!(rivet_error.message(), "This is a simple test error");
assert!(rivet_error.meta.is_none());
}
#[test]
fn test_meta_error_build() {
init_test();
let error = TestMetaError {
value: 42,
name: "test_name".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "meta_error");
assert_eq!(
rivet_error.message(),
"Error with value 42 and name test_name"
);
assert!(rivet_error.meta.is_some());
}
#[test]
fn test_internal_error_extraction() {
let regular_error = anyhow::anyhow!("Some random error");
let rivet_error = RivetError::extract(®ular_error);
assert_eq!(rivet_error.group(), "core");
assert_eq!(rivet_error.code(), "internal_error");
assert!(rivet_error.message().contains("An internal error occurred"));
assert!(rivet_error.meta.is_some());
}
#[test]
fn test_internal_error_preserves_actor_specifier() {
let regular_error =
anyhow::anyhow!("Some random error").context(ActorSpecifier::new("actor-2", 8));
let rivet_error = RivetError::extract(®ular_error);
assert_eq!(rivet_error.group(), "core");
assert_eq!(
rivet_error.actor(),
Some(&ActorSpecifier::new("actor-2", 8))
);
}
#[test]
fn test_actor_specifier_extracted_from_middle_of_chain() {
let error = anyhow::anyhow!("Some random error")
.context(ActorSpecifier::new("actor-3", 9))
.context("dispatching action");
let rivet_error = RivetError::extract(&error);
assert_eq!(
rivet_error.actor(),
Some(&ActorSpecifier::new("actor-3", 9))
);
}
#[test]
fn test_error_serialization() {
init_test();
let error = TestError.build();
let rivet_error = RivetError::extract(&error);
let json = serde_json::to_string(&rivet_error).unwrap();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["group"], "test");
assert_eq!(value["code"], "simple_error");
assert_eq!(value["message"], "This is a simple test error");
assert!(value.get("meta").is_none());
}
#[test]
fn test_actor_specifier_serialization() {
init_test();
let error = TestError
.build()
.context(ActorSpecifier::new("actor-1", 7).with_key("user:1"));
let rivet_error = RivetError::extract(&error);
assert_eq!(
rivet_error.actor(),
Some(&ActorSpecifier::new("actor-1", 7).with_key("user:1"))
);
let value: serde_json::Value =
serde_json::from_str(&serde_json::to_string(&rivet_error).unwrap()).unwrap();
assert_eq!(value["actor"]["actorId"], "actor-1");
assert_eq!(value["actor"]["generation"], 7);
assert_eq!(value["actor"]["key"], "user:1");
}
#[test]
fn test_meta_error_serialization() {
init_test();
let error = TestMetaError {
value: 100,
name: "serialization_test".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
let json = serde_json::to_string(&rivet_error).unwrap();
let value: serde_json::Value = serde_json::from_str(&json).unwrap();
assert_eq!(value["group"], "test");
assert_eq!(value["code"], "meta_error");
assert_eq!(
value["message"],
"Error with value 100 and name serialization_test"
);
let meta_value = value["meta"].as_object().unwrap();
assert_eq!(meta_value["value"], 100);
assert_eq!(meta_value["name"], "serialization_test");
}
#[test]
fn test_error_chaining() {
init_test();
let _base_error = anyhow::anyhow!("Base error");
let wrapped_error = TestError.build();
let extracted = RivetError::extract(&wrapped_error);
assert_eq!(extracted.code(), "simple_error");
}
#[derive(RivetError, Serialize, Deserialize)]
#[error(
"test",
"namespace_invalid_name",
"
Invalid namespace name.
Namespace names must be valid DNS subdomains.
"
)]
struct NamespaceInvalidName {
name: String,
reason: String,
}
#[test]
fn test_struct_without_formatted_description() {
init_test();
let error = NamespaceInvalidName {
name: "invalid name".to_string(),
reason: "contains spaces".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "namespace_invalid_name");
assert!(rivet_error.message().contains("Invalid namespace name"));
assert!(rivet_error.meta.is_some());
let meta_value: serde_json::Value =
serde_json::from_str(rivet_error.meta.as_ref().unwrap().get()).unwrap();
assert_eq!(meta_value["name"], "invalid name");
assert_eq!(meta_value["reason"], "contains spaces");
}
#[derive(RivetError, Serialize, Deserialize)]
#[error(
"test",
"api_rate_limited",
"
Rate limit exceeded.
The API rate limit has been exceeded for this endpoint.
Please wait before making additional requests.
",
"Rate limit exceeded. Limit: {limit}, resets at: {reset_at}"
)]
struct ApiRateLimited {
limit: u32,
reset_at: i64,
}
#[test]
fn test_multiline_descriptions() {
init_test();
let error = ApiRateLimited {
limit: 100,
reset_at: 1234567890,
}
.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "api_rate_limited");
assert_eq!(
rivet_error.message(),
"Rate limit exceeded. Limit: 100, resets at: 1234567890"
);
}
#[test]
fn test_metadata_for_simple_error() {
init_test();
let error = TestError.build();
let rivet_error = RivetError::extract(&error);
assert!(rivet_error.metadata().is_none());
}
#[test]
fn test_metadata_for_error_with_fields() {
init_test();
let error = TestMetaError {
value: 42,
name: "test_name".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
let metadata = rivet_error.metadata().expect("Should have metadata");
assert_eq!(metadata["value"], 42);
assert_eq!(metadata["name"], "test_name");
}
#[test]
fn test_metadata_for_namespace_error() {
init_test();
let error = NamespaceInvalidName {
name: "invalid-name".to_string(),
reason: "contains dash".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
let metadata = rivet_error.metadata().expect("Should have metadata");
assert_eq!(metadata["name"], "invalid-name");
assert_eq!(metadata["reason"], "contains dash");
}
#[test]
fn test_metadata_for_rate_limited_error() {
init_test();
let error = ApiRateLimited {
limit: 1000,
reset_at: 987654321,
}
.build();
let rivet_error = RivetError::extract(&error);
let metadata = rivet_error.metadata().expect("Should have metadata");
assert_eq!(metadata["limit"], 1000);
assert_eq!(metadata["reset_at"], 987654321);
}
#[test]
fn test_metadata_for_internal_error() {
let regular_error = anyhow::anyhow!("Some internal error");
let rivet_error = RivetError::extract(®ular_error);
let metadata = rivet_error.metadata().expect("Should have metadata");
assert!(metadata["error"].is_string());
assert!(
metadata["error"]
.as_str()
.unwrap()
.contains("Some internal error")
);
}
#[derive(RivetError, Serialize, Deserialize, Clone)]
#[error("test")]
enum TestEnumError {
#[error("not_found", "The resource does not exist.")]
NotFound,
#[error(
"input_too_large",
"Input too large.",
"Input too large (max {max_size})."
)]
InputTooLarge { max_size: usize },
#[error(
"key_too_large",
"Key too large.",
"Key '{key_preview}' too large (max {max_size} bytes)."
)]
KeyTooLarge {
max_size: usize,
key_preview: String,
},
}
#[test]
fn test_enum_variant_without_metadata() {
init_test();
let error = TestEnumError::NotFound.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "not_found");
assert_eq!(rivet_error.message(), "The resource does not exist.");
assert!(rivet_error.metadata().is_none());
}
#[test]
fn test_enum_variant_input_too_large_metadata() {
init_test();
let error = TestEnumError::InputTooLarge { max_size: 1024 }.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "input_too_large");
assert_eq!(rivet_error.message(), "Input too large (max 1024).");
let metadata = rivet_error.metadata().expect("Should have metadata");
assert_eq!(metadata["max_size"], 1024);
}
#[test]
fn test_enum_variant_key_too_large_metadata() {
init_test();
let error = TestEnumError::KeyTooLarge {
max_size: 256,
key_preview: "very_long_key_name...".to_string(),
}
.build();
let rivet_error = RivetError::extract(&error);
assert_eq!(rivet_error.group(), "test");
assert_eq!(rivet_error.code(), "key_too_large");
assert_eq!(
rivet_error.message(),
"Key 'very_long_key_name...' too large (max 256 bytes)."
);
let metadata = rivet_error.metadata().expect("Should have metadata");
assert_eq!(metadata["max_size"], 256);
assert_eq!(metadata["key_preview"], "very_long_key_name...");
}