use std::borrow::Cow;
use std::sync::Arc;
use ek_core::PluginContext;
use ek_core::audit::{deploy_audit, shape_audit};
use ek_core::envelope::{Envelope, ReplyErr, e_req_type};
use ek_core::plugin::Plugin;
use ek_core::service::ServiceDesc;
pub const AUDIT_LOG: &str = "eAuditLog.";
pub struct AuditLogLine {
pub name: String,
pub msg: String,
}
pub struct TinyPlugin {
name: String,
ctx: std::sync::OnceLock<PluginContext>,
}
impl TinyPlugin {
pub fn new(name: &str) -> Self {
Self {
name: name.to_string(),
ctx: std::sync::OnceLock::new(),
}
}
fn put_log(&self, msg: &str) {
if let Some(ctx) = self.ctx.get() {
let _ = ctx.put(ek_event::Event::with_data(
AUDIT_LOG,
Arc::new(AuditLogLine {
name: self.name.clone(),
msg: msg.to_string(),
}),
));
}
}
}
pub struct TinyReq;
pub struct TinyRes;
pub struct Ping;
pub struct Pong;
impl Plugin for TinyPlugin {
fn name(&self) -> &str {
&self.name
}
fn routes(&self) -> Vec<Cow<'static, str>> {
vec![
Cow::Owned(e_req_type(&self.name)),
Cow::Borrowed(ek_event::EVENT_TIMER),
]
}
fn version(&self) -> Cow<'static, str> {
Cow::Borrowed(env!("CARGO_PKG_VERSION"))
}
fn services(&self) -> Vec<ServiceDesc> {
vec![ServiceDesc::describe::<TinyReq, Result<TinyRes, ReplyErr>>(
format!("{}.ping", self.name),
"audit self-test ping (示例面;非通用服务)",
)]
}
fn on_init(&self, ctx: &PluginContext) {
let _ = self.ctx.set(ctx.clone());
}
fn on_start(&self) {
self.put_log("on_start");
}
fn on_stop(&self) {
self.put_log("on_stop");
}
fn on_mail(&self, event: &ek_event::Event) {
if event.event_type == e_req_type(&self.name) {
if let Some(env) = event.data.downcast_ref::<Envelope>() {
if env.payload.downcast_ref::<TinyReq>().is_some() {
let res: TinyRes = TinyRes;
let _ = env.reply.send(Box::new(Ok::<TinyRes, ReplyErr>(res)));
return;
}
let _ = env
.reply
.send(Box::new(Err::<(), ReplyErr>(ReplyErr::InvalidPayload)));
}
}
}
}
#[test]
fn tiny_plugin_passes_shape_and_deploy_audit() {
let tiny = TinyPlugin::new("audit_tiny");
let shape = shape_audit(&tiny, env!("CARGO_PKG_VERSION"));
assert!(shape.is_ok(), "结构自检应全绿: {:?}", shape.findings);
let dep = deploy_audit(Arc::new(TinyPlugin::new("audit_tiny_e2e")));
assert!(dep.is_ok(), "装配自检应全绿: {:?}", dep.findings);
}
#[test]
fn audit_flags_missing_request_line_when_service_face_exists() {
struct BadRoutes;
impl Plugin for BadRoutes {
fn name(&self) -> &str {
"audit_bad"
}
fn routes(&self) -> Vec<Cow<'static, str>> {
vec![Cow::Borrowed(ek_event::EVENT_TIMER)]
}
fn version(&self) -> Cow<'static, str> {
Cow::Borrowed(env!("CARGO_PKG_VERSION"))
}
fn services(&self) -> Vec<ServiceDesc> {
vec![ServiceDesc::describe::<Ping, Pong>(
"audit_bad.ping",
"服务面反例",
)]
}
fn on_mail(&self, _e: &ek_event::Event) {}
}
let rep = shape_audit(&BadRoutes, env!("CARGO_PKG_VERSION"));
assert!(
rep.findings.iter().any(|f| f.id == "Q2"),
"Q2必须命中: {:?}",
rep.findings
);
}
#[test]
fn audit_passes_pure_subscriber_without_service_face() {
struct PureSubscriber;
impl Plugin for PureSubscriber {
fn name(&self) -> &str {
"audit_pure_sub"
}
fn routes(&self) -> Vec<Cow<'static, str>> {
vec![
Cow::Borrowed(AUDIT_LOG),
Cow::Borrowed(ek_event::EVENT_TIMER),
]
}
fn version(&self) -> Cow<'static, str> {
Cow::Borrowed(env!("CARGO_PKG_VERSION"))
}
fn services(&self) -> Vec<ServiceDesc> {
Vec::new()
}
fn on_mail(&self, _e: &ek_event::Event) {}
}
let shape = shape_audit(&PureSubscriber, env!("CARGO_PKG_VERSION"));
assert!(shape.is_ok(), "纯订阅件应全绿: {:?}", shape.findings);
let dep = deploy_audit(Arc::new(PureSubscriber));
assert!(
dep.is_ok(),
"纯订阅件装配应全绿(免探针): {:?}",
dep.findings
);
}