use std::sync::Arc;
use crate::asn1::Frame;
use crate::der::Sequence;
#[cfg(feature = "derive")]
use crate::Beamable;
#[cfg(feature = "derive")]
#[derive(Beamable, Clone, Debug, PartialEq, Sequence)]
pub struct TestMessage {
pub content: String,
}
#[cfg(not(feature = "derive"))]
#[derive(Clone, Debug, PartialEq, Sequence)]
pub struct TestMessage {
pub content: String,
}
#[cfg(not(feature = "derive"))]
impl crate::Message for TestMessage {
const MUST_BE_NON_REPUDIABLE: bool = false;
const MUST_BE_CONFIDENTIAL: bool = false;
const MUST_BE_COMPRESSED: bool = false;
const MUST_BE_PRIORITIZED: bool = false;
const MIN_VERSION: crate::Version = crate::Version::V0;
}
#[cfg(feature = "derive")]
#[derive(Beamable, Clone, Debug, PartialEq, Sequence)]
#[beam(confidential)]
pub struct ConfidentialNote {
pub content: String,
}
#[cfg(not(feature = "derive"))]
#[derive(Clone, Debug, PartialEq, Sequence)]
pub struct ConfidentialNote {
pub content: String,
}
#[cfg(not(feature = "derive"))]
impl crate::Message for ConfidentialNote {
const MUST_BE_CONFIDENTIAL: bool = true;
const MUST_BE_NON_REPUDIABLE: bool = false;
const MUST_BE_COMPRESSED: bool = false;
const MUST_BE_PRIORITIZED: bool = false;
const MIN_VERSION: crate::Version = crate::Version::V0;
}
#[cfg(feature = "derive")]
#[derive(Beamable, Clone, Debug, PartialEq, Sequence)]
#[beam(profile = 1)]
pub struct ConfidentialNonrepudiableNote {
pub content: String,
}
#[cfg(feature = "derive")]
#[derive(Beamable, Clone, Debug, PartialEq, Sequence)]
#[beam(message_integrity, frame_integrity)]
pub struct IntegralNote {
pub content: String,
}
#[cfg(not(feature = "derive"))]
#[derive(Clone, Debug, PartialEq, Sequence)]
pub struct ConfidentialNonrepudiableNote {
pub content: String,
}
#[cfg(not(feature = "derive"))]
impl crate::Message for ConfidentialNonrepudiableNote {
const MUST_BE_CONFIDENTIAL: bool = true;
const MUST_BE_NON_REPUDIABLE: bool = true;
const MUST_BE_COMPRESSED: bool = false;
const MUST_BE_PRIORITIZED: bool = false;
const MIN_VERSION: crate::Version = crate::Version::V0;
}
#[cfg(not(feature = "derive"))]
impl crate::Message for IntegralNote {
const MUST_BE_NON_REPUDIABLE: bool = false;
const MUST_BE_CONFIDENTIAL: bool = false;
const MUST_BE_COMPRESSED: bool = false;
const MUST_BE_PRIORITIZED: bool = false;
const MUST_HAVE_MESSAGE_INTEGRITY: bool = true;
const MUST_HAVE_FRAME_INTEGRITY: bool = true;
const MIN_VERSION: crate::Version = crate::Version::V0;
}
pub fn create_test_message(content: Option<&str>) -> TestMessage {
TestMessage {
content: content.map(|c| c.into()).unwrap_or_else(|| "Hello TightBeam!".to_string()),
}
}
pub fn create_v0_tightbeam(content: Option<&str>, id: Option<&str>) -> Frame {
let message = create_test_message(content);
#[cfg(feature = "std")]
let order = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.expect("Time went backwards")
.as_secs();
#[cfg(not(feature = "std"))]
let order: u64 = 1_700_000_000;
#[cfg(all(feature = "std", feature = "digest", feature = "random"))]
let id = id.unwrap_or({
use crate::crypto::hash::{Digest, Sha3_256};
use crate::random::generate_random_bytes;
let mut bytes: [u8; 32] = [0; 32];
generate_random_bytes(&mut bytes, None).expect("Failed to generate random bytes");
let hash = Sha3_256::digest(bytes);
Box::leak(format!("{hash:x}").into_boxed_str())
});
#[cfg(not(all(feature = "std", feature = "digest", feature = "random")))]
let id = id.unwrap_or("test-message-id");
#[cfg(feature = "derive")]
let result = compose! {
V0: id: id,
order: order,
message: message
};
#[cfg(not(feature = "derive"))]
let result = crate::Frame::new_v0(id, order, &message);
result.expect("Failed to create TightBeam message")
}
#[cfg(all(feature = "builder", feature = "digest", feature = "sha3"))]
pub fn create_frame_with_frame_integrity() -> Frame {
use crate::crypto::hash::Sha3_256;
let message = create_test_message(None);
compose! {
V1: id: "fi-frame",
order: 1u64,
message: message,
frame_integrity: type Sha3_256
}
.expect("Failed to create frame with frame integrity")
}
#[cfg(all(feature = "secp256k1", feature = "signature"))]
pub fn create_test_signing_key() -> k256::ecdsa::SigningKey {
let secret_bytes = [1u8; 32];
crate::crypto::sign::ecdsa::SigningKey::from_bytes(&secret_bytes.into()).expect("Failed to create signing key")
}
#[cfg(all(feature = "secp256k1", feature = "signature", feature = "x509"))]
pub fn create_test_certificate(signing_key: &k256::ecdsa::SigningKey) -> crate::x509::Certificate {
use crate::der::Decode;
use crate::spki::EncodePublicKey;
let verifying_key = *signing_key.verifying_key();
let public_key_der = verifying_key
.to_public_key_der()
.expect("test verifying key encodes to SPKI DER");
let tbs_cert = crate::x509::TbsCertificate {
version: crate::x509::Version::V3,
serial_number: crate::x509::serial_number::SerialNumber::new(&[1]).expect("single-byte serial is valid"),
signature: crate::spki::AlgorithmIdentifierOwned {
oid: crate::oids::SIGNER_ECDSA_WITH_SHA3_256,
parameters: None,
},
issuer: x509_cert::name::RdnSequence::default(),
validity: crate::x509::time::Validity {
not_before: crate::x509::time::Time::UtcTime(
crate::der::asn1::UtcTime::from_unix_duration(core::time::Duration::from_secs(0))
.expect("epoch is a valid UtcTime"),
),
not_after: crate::x509::time::Time::UtcTime(
crate::der::asn1::UtcTime::from_unix_duration(core::time::Duration::from_secs(2_000_000_000))
.expect("fixed not-after is a valid UtcTime"),
),
},
subject: x509_cert::name::RdnSequence::default(),
subject_public_key_info: crate::spki::SubjectPublicKeyInfoOwned::from_der(public_key_der.as_bytes())
.expect("freshly encoded SPKI re-decodes"),
issuer_unique_id: None,
subject_unique_id: None,
extensions: None,
};
crate::x509::Certificate {
tbs_certificate: tbs_cert,
signature_algorithm: crate::spki::AlgorithmIdentifierOwned {
oid: crate::oids::SIGNER_ECDSA_WITH_SHA3_256,
parameters: None,
},
signature: crate::der::asn1::BitString::new(0, vec![0; 64]).expect("64-byte placeholder is a valid BitString"),
}
}
#[cfg(all(feature = "secp256k1", feature = "signature", feature = "x509"))]
fn test_extension<T>(value: &T) -> crate::x509::ext::Extension
where
T: crate::der::oid::AssociatedOid + crate::der::Encode,
{
crate::x509::ext::Extension {
extn_id: T::OID,
critical: true,
extn_value: crate::der::asn1::OctetString::new(value.to_der().expect("extension value encodes to DER"))
.expect("DER bytes wrap in an OctetString"),
}
}
#[cfg(all(feature = "secp256k1", feature = "signature", feature = "x509"))]
pub fn ca_extensions(ca: bool, key_cert_sign: bool, path_len: Option<u8>) -> Vec<crate::x509::ext::Extension> {
use crate::x509::ext::pkix::{BasicConstraints, KeyUsage, KeyUsages};
let basic_constraints = BasicConstraints { ca, path_len_constraint: path_len };
let usage = if key_cert_sign {
KeyUsages::KeyCertSign
} else {
KeyUsages::DigitalSignature
};
let key_usage = KeyUsage(usage.into());
vec![test_extension(&basic_constraints), test_extension(&key_usage)]
}
#[cfg(all(feature = "digest", feature = "sha3"))]
#[derive(Clone, Default)]
pub struct SixteenByteDigest(crate::crypto::hash::Sha3_256);
#[cfg(all(feature = "digest", feature = "sha3"))]
mod sixteen_byte_digest {
use super::SixteenByteDigest;
use crate::der::oid::AssociatedOid;
impl digest::Update for SixteenByteDigest {
fn update(&mut self, data: &[u8]) {
digest::Update::update(&mut self.0, data);
}
}
impl digest::OutputSizeUser for SixteenByteDigest {
type OutputSize = digest::consts::U16;
}
impl digest::FixedOutput for SixteenByteDigest {
fn finalize_into(self, out: &mut digest::Output<Self>) {
let full = digest::FixedOutput::finalize_fixed(self.0);
out.copy_from_slice(&full[..16]);
}
}
impl digest::Reset for SixteenByteDigest {
fn reset(&mut self) {
digest::Reset::reset(&mut self.0);
}
}
impl digest::FixedOutputReset for SixteenByteDigest {
fn finalize_into_reset(&mut self, out: &mut digest::Output<Self>) {
let full = digest::FixedOutputReset::finalize_fixed_reset(&mut self.0);
out.copy_from_slice(&full[..16]);
}
}
impl digest::HashMarker for SixteenByteDigest {}
impl AssociatedOid for SixteenByteDigest {
const OID: crate::der::asn1::ObjectIdentifier = crate::oids::HASH_SHA256;
}
}
#[cfg(all(feature = "secp256k1", feature = "signature", feature = "x509"))]
pub struct TestCertificateChain {
pub root: crate::x509::Certificate,
pub intermediate: crate::x509::Certificate,
pub leaf: crate::x509::Certificate,
pub root_key: k256::ecdsa::SigningKey,
pub intermediate_key: k256::ecdsa::SigningKey,
pub leaf_key: k256::ecdsa::SigningKey,
}
#[cfg(all(feature = "secp256k1", feature = "signature", feature = "x509"))]
pub fn create_test_certificate_chain() -> crate::error::Result<TestCertificateChain> {
use crate::crypto::hash::Sha3_256;
use crate::crypto::sign::ecdsa::{Secp256k1, Signature, SigningKey};
use crate::crypto::sign::sign_canonical;
use crate::der::asn1::{BitString, UtcTime};
use crate::der::{Decode, Encode};
use crate::spki::{AlgorithmIdentifierOwned, EncodePublicKey, SubjectPublicKeyInfoOwned};
use crate::x509::name::RdnSequence;
use crate::x509::serial_number::SerialNumber;
use crate::x509::time::{Time, Validity};
use crate::x509::{Certificate, TbsCertificate, Version};
use crate::der::asn1::{PrintableString, SetOfVec};
use crate::x509::attr::AttributeTypeAndValue;
use crate::x509::name::RelativeDistinguishedName;
let root_key = SigningKey::from_bytes(&[1u8; 32].into())?;
let intermediate_key = SigningKey::from_bytes(&[2u8; 32].into())?;
let leaf_key = SigningKey::from_bytes(&[3u8; 32].into())?;
let cn_oid = crate::der::oid::ObjectIdentifier::new_unwrap("2.5.4.3");
let root_cn_str = PrintableString::new("Root")?;
let root_cn = AttributeTypeAndValue { oid: cn_oid, value: crate::der::Any::from(&root_cn_str) };
let root_name = RdnSequence::from(vec![RelativeDistinguishedName::from(SetOfVec::try_from(vec![root_cn])?)]);
let inter_cn_str = PrintableString::new("Intermediate")?;
let inter_cn = AttributeTypeAndValue { oid: cn_oid, value: crate::der::Any::from(&inter_cn_str) };
let inter_name = RdnSequence::from(vec![RelativeDistinguishedName::from(SetOfVec::try_from(vec![inter_cn])?)]);
let leaf_cn_str = PrintableString::new("Leaf")?;
let leaf_cn = AttributeTypeAndValue { oid: cn_oid, value: crate::der::Any::from(&leaf_cn_str) };
let leaf_name = RdnSequence::from(vec![RelativeDistinguishedName::from(SetOfVec::try_from(vec![leaf_cn])?)]);
let validity = Validity {
not_before: Time::UtcTime(UtcTime::from_unix_duration(core::time::Duration::from_secs(0))?),
not_after: Time::UtcTime(UtcTime::from_unix_duration(core::time::Duration::from_secs(2_000_000_000))?),
};
let algorithm = AlgorithmIdentifierOwned { oid: crate::oids::SIGNER_ECDSA_WITH_SHA3_256, parameters: None };
let root_pub_der = root_key.verifying_key().to_public_key_der()?;
let root_tbs = TbsCertificate {
version: Version::V3,
serial_number: SerialNumber::new(&[1])?,
signature: algorithm.clone(),
issuer: root_name.clone(),
validity,
subject: root_name.clone(),
subject_public_key_info: SubjectPublicKeyInfoOwned::from_der(root_pub_der.as_bytes())?,
issuer_unique_id: None,
subject_unique_id: None,
extensions: Some(ca_extensions(true, true, None)),
};
let root_tbs_der = root_tbs.to_der()?;
let root_sig: Signature<Secp256k1> = sign_canonical::<Sha3_256, _>(&root_key, &root_tbs_der)?;
let root = Certificate {
tbs_certificate: root_tbs,
signature_algorithm: algorithm.clone(),
signature: BitString::new(0, root_sig.to_vec())?,
};
let inter_pub_der = intermediate_key.verifying_key().to_public_key_der()?;
let inter_tbs = TbsCertificate {
version: Version::V3,
serial_number: SerialNumber::new(&[2])?,
signature: algorithm.clone(),
issuer: root_name,
validity,
subject: inter_name.clone(),
subject_public_key_info: SubjectPublicKeyInfoOwned::from_der(inter_pub_der.as_bytes())?,
issuer_unique_id: None,
subject_unique_id: None,
extensions: Some(ca_extensions(true, true, None)),
};
let inter_tbs_der = inter_tbs.to_der()?;
let inter_sig: Signature<Secp256k1> = sign_canonical::<Sha3_256, _>(&root_key, &inter_tbs_der)?;
let intermediate = Certificate {
tbs_certificate: inter_tbs,
signature_algorithm: algorithm.clone(),
signature: BitString::new(0, inter_sig.to_vec())?,
};
let leaf_pub_der = leaf_key.verifying_key().to_public_key_der()?;
let leaf_tbs = TbsCertificate {
version: Version::V3,
serial_number: SerialNumber::new(&[3])?,
signature: algorithm.clone(),
issuer: inter_name,
validity,
subject: leaf_name,
subject_public_key_info: SubjectPublicKeyInfoOwned::from_der(leaf_pub_der.as_bytes())?,
issuer_unique_id: None,
subject_unique_id: None,
extensions: None,
};
let leaf_tbs_der = leaf_tbs.to_der()?;
let leaf_sig: Signature<Secp256k1> = sign_canonical::<Sha3_256, _>(&intermediate_key, &leaf_tbs_der)?;
let leaf = Certificate {
tbs_certificate: leaf_tbs,
signature_algorithm: algorithm,
signature: BitString::new(0, leaf_sig.to_vec())?,
};
Ok(TestCertificateChain { root, intermediate, leaf, root_key, intermediate_key, leaf_key })
}
#[cfg(feature = "aead")]
pub fn create_test_cipher_key() -> (
crate::crypto::common::Key<crate::crypto::aead::Aes256Gcm>,
crate::crypto::aead::Aes256Gcm,
) {
use crate::crypto::aead::KeyInit;
let key_bytes = [0x33; 32];
let key = crate::crypto::common::Key::<crate::crypto::aead::Aes256Gcm>::from(key_bytes);
let cipher = crate::crypto::aead::Aes256Gcm::new(&key);
(key, cipher)
}
#[cfg(feature = "x509")]
pub fn create_expired_test_certificate() -> crate::x509::Certificate {
crate::pem! {"
-----BEGIN CERTIFICATE-----
MIIF1TCCBVugAwIBAgIQdBJ26pggQyU+isEPM912FDAKBggqhkjOPQQDAzByMQsw
CQYDVQQGEwJVUzEOMAwGA1UECAwFVGV4YXMxEDAOBgNVBAcMB0hvdXN0b24xETAP
BgNVBAoMCFNTTCBDb3JwMS4wLAYDVQQDDCVTU0wuY29tIEVWIFNTTCBJbnRlcm1l
ZGlhdGUgQ0EgRUNDIFIyMB4XDTE5MDgxNjIyMzU1N1oXDTE5MDgxNzIyMzU1N1ow
gcgxCzAJBgNVBAYTAlVTMQ4wDAYDVQQIDAVUZXhhczEQMA4GA1UEBwwHSG91c3Rv
bjERMA8GA1UECgwIU1NMIENvcnAxFjAUBgNVBAUTDU5WMjAwODE2MTQyNDMxHzAd
BgNVBAMMFmV4cGlyZWQtZWNjLWV2LnNzbC5jb20xHTAbBgNVBA8MFFByaXZhdGUg
T3JnYW5pemF0aW9uMRcwFQYLKwYBBAGCNzwCAQIMBk5ldmFkYTETMBEGCysGAQQB
gjc8AgEDEwJVUzB2MBAGByqGSM49AgEGBSuBBAAiA2IABKFkgNgOHrsYyyJHlGXK
6C6SupADk+CLOKpMuoIdduURE4k5aXkmQ2yacYNmfqTTgC4oss/c2swxX9KDZdjW
PYpFXfeCkorzKcX6RDJdUjD/78TiAT7fIyqcNyxuRpr4bqOCA10wggNZMB8GA1Ud
IwQYMBaAFITu+Hk+CGR2SGQ59bG/iwJeC7wnMH4GCCsGAQUFBwEBBHIwcDBMBggr
BgEFBQcwAoZAaHR0cDovL3d3dy5zc2wuY29tL3JlcG9zaXRvcnkvU1NMY29tLVN1
YkNBLUVWLVNTTC1FQ0MtMzg0LVIyLmNydDAgBggrBgEFBQcwAYYUaHR0cDovL29j
c3BzLnNzbC5jb20wPQYDVR0RBDYwNIIWZXhwaXJlZC1lY2MtZXYuc3NsLmNvbYIa
d3d3LmV4cGlyZWQtZWNjLWV2LnNzbC5jb20wXwYDVR0gBFgwVjAHBgVngQwBATAN
BgsqhGgBhvZ3AgUBATA8BgwrBgEEAYKpMAEDAQQwLDAqBggrBgEFBQcCARYeaHR0
cHM6Ly93d3cuc3NsLmNvbS9yZXBvc2l0b3J5MB0GA1UdJQQWMBQGCCsGAQUFBwMC
BggrBgEFBQcDATBHBgNVHR8EQDA+MDygOqA4hjZodHRwOi8vY3Jscy5zc2wuY29t
L1NTTGNvbS1TdWJDQS1FVi1TU0wtRUNDLTM4NC1SMi5jcmwwHQYDVR0OBBYEFNK1
Fhn8lNacFjqY/nVutrEtu8JTMA4GA1UdDwEB/wQEAwIHgDCCAX0GCisGAQQB1nkC
BAIEggFtBIIBaQFnAHUAdH7agzGtMxCRIZzOJU9CcMK//V5CIAjGNzV55hB7zFYA
AAFsnJvm3QAABAMARjBEAiAN3B7UPSzzszy+uYfXAZXKfHp6X8vkFL6FsvDknpv9
cQIgTcE3kmHDPlQRwhkccghbl/ekwgY8CZHOSYmQcZzAKhoAdgDuS723dc5guuFC
aR+r4Z5mow9+X7By2IMAxHuJeqj9ywAAAWycm+XxAAAEAwBHMEUCIF8Z0jTHQ0bU
xRMWFVkPo/Fq8taoiTMF9rAX4/QZtmdNAiEA2kodKU2CXO2WOT257aF0v2gBLh7T
2f8rrj8MYf4A1soAdgBVgdTCFpA2AUrqC5tXPFPwwOQ4eHAlCBcvo6odBxPTDAAA
AWycm+ZjAAAEAwBHMEUCIQC/j+yfrh55hcKfaGRBvIOX/Wf+NWy/AUep9UiQaV/0
oQIgO2WX6jOEyXN9ZtBDTxaspPhIcCIWOXNfn9PkzrEzaWQwCgYIKoZIzj0EAwMD
aAAwZQIxAIM0MOzr0GX3Zeg3OZCOEKYe/yIXT2FlDMMVAFK0WdHI+lMVwQGacR0A
+9Cvs7zTlQIwLkVrf3XF+P3afMrGhljvWAqPNHpf/jJsddq0DmSHgITOWCJXfytT
dLAFZesIxt4p
-----END CERTIFICATE-----
"}
.expect("Failed to parse expired test certificate")
}
pub fn create_test_hash_info() -> crate::DigestInfo {
crate::DigestInfo {
algorithm: crate::AlgorithmIdentifier {
oid: crate::der::asn1::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.2.1"), parameters: None,
},
digest: crate::asn1::OctetString::new(vec![0u8; 32]).expect("Failed to create OctetString"),
}
}
#[cfg(all(feature = "x509", feature = "secp256k1", feature = "signature"))]
pub fn create_expiring_test_certificate(
) -> crate::error::Result<(crate::x509::Certificate, crate::crypto::sign::ecdsa::Secp256k1SigningKey)> {
let cert = crate::pem! {"
-----BEGIN CERTIFICATE-----
MIIBiTCCAS+gAwIBAgIBATALBglghkgBZQMEAwowLTErMCkGA1UEAwwiRXhwaXJp
bmcgVG9tb3Jyb3cgVGVzdCBDZXJ0aWZpY2F0ZTAeFw0yNTExMDkwNjM1NDBaFw0y
NTExMTAwNjM1NDBaMC0xKzApBgNVBAMMIkV4cGlyaW5nIFRvbW9ycm93IFRlc3Qg
Q2VydGlmaWNhdGUwVjAQBgcqhkjOPQIBBgUrgQQACgNCAAQbhMVWexJkQJldPtWq
ugVl1x4YNGBIGf+cF/Xp1d0Hj3C+r49Yi1QVB/7WpkLFq0Lf34Egp/Y53lEi1Hpp
qOjRo0IwQDAdBgNVHQ4EFgQUQ65suXrNrzVLgobdsptJLJXpWHAwDwYDVR0TAQH/
BAUwAwEB/zAOBgNVHQ8BAf8EBAMCAQYwCwYJYIZIAWUDBAMKA0cAMEQCIA7h0vvv
anyhANdtbmk2etj57E5fh1lI3Fg2bePzYq0kAiAL9r2v4pwP8s1uec4FT7HAUYf6
T4zOUKsmsdnnAwTztg==
-----END CERTIFICATE-----
"}?;
let key_bytes: [u8; 32] = [0x01; 32];
let signing_key = crate::crypto::sign::ecdsa::Secp256k1SigningKey::from_bytes(&key_bytes.into())?;
Ok((cert, signing_key))
}
pub fn create_test_encryption_info() -> crate::EncryptedContentInfo {
crate::EncryptedContentInfo {
content_type: crate::oids::COMPRESSION_CONTENT,
content_enc_alg: crate::AlgorithmIdentifier {
oid: crate::der::asn1::ObjectIdentifier::new_unwrap("2.16.840.1.101.3.4.1.42"), parameters: None,
},
encrypted_content: Some(
crate::der::asn1::OctetString::new(vec![0u8; 12]).expect("12-byte placeholder wraps in an OctetString"),
),
}
}
pub fn create_test_signer_info() -> crate::SignerInfo {
use crate::cms::content_info::CmsVersion;
use crate::cms::signed_data::SignerIdentifier;
use crate::der::asn1::OctetString;
use crate::x509::ext::pkix::SubjectKeyIdentifier;
let skid = SubjectKeyIdentifier::from(OctetString::new([0u8; 20]).expect("20-byte SKID wraps in an OctetString"));
crate::SignerInfo {
version: CmsVersion::V1,
sid: SignerIdentifier::SubjectKeyIdentifier(skid),
digest_alg: crate::AlgorithmIdentifierOwned { oid: crate::oids::HASH_SHA3_256, parameters: None },
signed_attrs: None,
signature_algorithm: crate::AlgorithmIdentifierOwned {
oid: crate::oids::SIGNER_ECDSA_WITH_SHA3_256,
parameters: None,
},
signature: OctetString::new([0u8; 64]).expect("64-byte placeholder wraps in an OctetString"),
unsigned_attrs: None,
}
}
#[macro_export]
macro_rules! test_builder {
(
name: $test_name:ident,
builder_type: $builder_type:ty,
version: $version:expr,
message: || $message_body:expr,
setup: |$builder:ident, $msg:ident| $setup_body:expr,
assertions: |$msg_result:ident, $frame_result:ident| $assertions_body:expr
) => {
$crate::test_builder!(@impl
$test_name,
$builder_type,
$version,
{ $message_body },
|$builder, $msg| $setup_body,
|$msg_result, $frame_result| $assertions_body
);
};
(
name: $test_name:ident,
builder_type: $builder_type:ty,
version: $version:expr,
message: $message:expr,
setup: |$builder:ident, $msg:ident| $setup_body:expr,
assertions: |$msg_result:ident, $frame_result:ident| $assertions_body:expr
) => {
$crate::test_builder!(@impl
$test_name,
$builder_type,
$version,
$message,
|$builder, $msg| $setup_body,
|$msg_result, $frame_result| $assertions_body
);
};
(@impl
$test_name:ident,
$builder_type:ty,
$version:expr,
$message:expr,
|$builder:ident, $msg:ident| $setup_body:expr,
|$msg_result:ident, $frame_result:ident| $assertions_body:expr
) => {
#[test]
fn $test_name() -> $crate::error::Result<()> {
let $msg = $message;
let msg_clone = $msg.clone();
let $builder: $builder_type = <$builder_type>::from($version);
let $frame_result = $setup_body;
let $msg_result = msg_clone;
$assertions_body
}
};
}
pub trait ExpectedMatcher {
fn matches(&self, frame: &Frame) -> bool;
}
impl ExpectedMatcher for crate::Frame {
fn matches(&self, frame: &Frame) -> bool {
frame.metadata.id == self.metadata.id
}
}
impl ExpectedMatcher for Arc<crate::Frame> {
fn matches(&self, frame: &Frame) -> bool {
self.metadata.id == frame.metadata.id
}
}
impl<E> ExpectedMatcher for Result<crate::Frame, E> {
fn matches(&self, frame: &Frame) -> bool {
match self {
Ok(f) => frame.metadata.id == f.metadata.id,
Err(_) => false,
}
}
}
impl<T> ExpectedMatcher for T
where
T: crate::Message + PartialEq,
{
fn matches(&self, frame: &Frame) -> bool {
if let Ok(decoded) = crate::decode::<T>(&frame.message) {
decoded == *self
} else {
false
}
}
}
#[macro_export]
macro_rules! test_worker {
(
name: $test_name:ident,
setup: || $setup_body:expr,
assertions: |$worker:ident| $assertions_body:expr
) => {
#[tokio::test]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
use $crate::colony::worker::Worker;
let builder = $setup_body;
let trace = std::sync::Arc::new($crate::trace::TraceCollector::new());
let mut worker = <_ as $crate::colony::worker::Worker>::start(builder, trace).await?;
let result = {
let $worker = &mut worker;
$assertions_body.await
};
worker.kill().await?;
result
}
};
}
#[macro_export]
macro_rules! test_servlet {
(
name: $test_name:ident,
worker_threads: $threads:literal,
protocol: $protocol:ident,
setup: || $setup_body:expr,
assertions: |$client:ident| $assertions_body:expr
) => {
#[tokio::test(flavor = "multi_thread", worker_threads = $threads)]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let worker = $setup_body.await?;
let addr = worker.addr();
let mut $client = $crate::client! {
connect $protocol: addr
};
let result = $assertions_body.await;
worker.stop();
result
}
};
(
name: $test_name:ident,
protocol: $protocol:ident,
setup: || $setup_body:expr,
assertions: |$client:ident| $assertions_body:expr
) => {
#[tokio::test]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let worker = ($setup_body).await?;
let addr = worker.addr();
let mut $client = $crate::client! {
async $protocol: connect addr
}
.await?;
let result = $assertions_body.await;
worker.stop();
result
}
};
}
#[macro_export]
macro_rules! test_drone {
(
name: $test_name:ident,
worker_threads: $threads:literal,
protocol: $protocol:ident,
drone: $drone_type:ty,
config: $config:expr,
setup: |$setup_drone:ident| $setup_body:expr,
assertions: |$client:ident, $channels:ident| $assertions_body:expr
) => {
#[tokio::test(flavor = "multi_thread", worker_threads = $threads)]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let drone = <$drone_type as $crate::colony::servlet::Servlet<()>>::start(
$crate::trace::TraceCollector::new(),
$config,
)
.await?;
let $setup_drone = drone;
let drone = $setup_body.await;
let addr = drone.addr();
let mut $client = $crate::client! {
connect $protocol: addr
};
let $channels = ((), ());
let result = $assertions_body.await;
drone.stop();
result
}
};
(
name: $test_name:ident,
protocol: $protocol:ident,
drone: $drone_type:ty,
config: $config:expr,
setup: |$setup_drone:ident| $setup_body:expr,
assertions: |$client:ident, $channels:ident| $assertions_body:expr
) => {
#[tokio::test]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let drone = <$drone_type as $crate::colony::servlet::Servlet<()>>::start(
::std::sync::Arc::new($crate::trace::TraceCollector::new()),
$config,
)
.await?;
let $setup_drone = drone;
let drone = $setup_body.await;
let addr = drone.addr();
let mut $client = $crate::client! {
connect $protocol: addr
};
let $channels = ((), ());
let result = $assertions_body.await;
drone.stop();
result
}
};
(
name: $test_name:ident,
worker_threads: $threads:literal,
protocol: $protocol:ident,
drone: $drone_type:ty,
config: $config:expr,
assertions: |$client:ident, $channels:ident| $assertions_body:expr
) => {
#[tokio::test(flavor = "multi_thread", worker_threads = $threads)]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let drone = <$drone_type as $crate::colony::servlet::Servlet<()>>::start(
$crate::trace::TraceCollector::new(),
$config,
)
.await?;
let addr = drone.addr();
let mut $client = $crate::client! {
connect $protocol: addr
};
let $channels = ((), ());
let result = $assertions_body.await;
drone.stop();
result
}
};
(
name: $test_name:ident,
protocol: $protocol:ident,
drone: $drone_type:ty,
config: $config:expr,
assertions: |$client:ident, $channels:ident| $assertions_body:expr
) => {
#[tokio::test]
async fn $test_name() -> Result<(), Box<dyn std::error::Error>> {
let drone = <$drone_type as $crate::colony::servlet::Servlet<()>>::start(
$crate::trace::TraceCollector::new(),
$config,
)
.await?;
let addr = drone.addr();
let mut $client = $crate::client! {
connect $protocol: addr
};
let $channels = ((), ());
let result = $assertions_body.await;
drone.stop();
result
}
};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::builder::MetadataBuilder;
use crate::{Metadata, Version};
#[test]
#[cfg(feature = "std")]
fn test_create_test_message() -> crate::error::Result<()> {
let message = create_test_message(Some("Test content"));
assert_eq!(message.content, "Test content");
Ok(())
}
test_builder! {
name: test_metadata_builder_basic,
builder_type: MetadataBuilder,
version: Version::V0,
message: (),
setup: |builder, _msg| {
builder
.with_id("test-id")
.with_order(1696521600)
.build()
},
assertions: |_msg, result| {
let metadata: Metadata = result?;
assert_eq!(metadata.id, b"test-id");
assert_eq!(metadata.order, 1696521600);
assert!(metadata.integrity.is_none());
Ok(())
}
}
}