rustlavel_flags/
plugin.rs1use crate::flags::Flags;
15use rustlavel_core::Config;
16use rustlavel_http::plugin::{Plugin, Setup};
17
18pub struct FeatureFlags {
26 flags: Flags,
27}
28
29impl FeatureFlags {
30 pub fn new(flags: Flags) -> Self {
32 FeatureFlags { flags }
33 }
34
35 pub fn from_config(flags: Flags, config: &Config) -> Self {
44 FeatureFlags { flags: flags.configured(config) }
45 }
46
47 pub fn flags(&self) -> &Flags {
50 &self.flags
51 }
52}
53
54impl Plugin for FeatureFlags {
55 fn name(&self) -> &'static str {
56 "flags"
57 }
58
59 fn register(self: Box<Self>, setup: &mut Setup<'_>) {
60 setup.state(self.flags.clone());
64 }
65}
66
67#[cfg(test)]
68mod tests {
69 use super::*;
70 use crate::guard::FlagsExt;
71 use crate::scope::Scope;
72 use rustlavel_core::{Context, Error};
73 use rustlavel_http::router::Router;
74 use rustlavel_http::{Request, TestClient};
75
76 fn flags() -> Flags {
77 Flags::new().define("new-checkout", |scope: Scope| async move { scope.id().ends_with('7') })
78 }
79
80 fn registered(plugin: FeatureFlags) -> TestClient {
82 let mut router = Router::new();
83 let config = Config::with_defaults();
84 let mut context = Some(Context::builder());
85
86 router.get("/checkout", |request: Request| async move {
87 let on = request.flag("new-checkout").await?;
88 Ok::<_, Error>(format!("new-checkout={on}"))
89 });
90
91 let mut setup = Setup { router: &mut router, config: &config, context: &mut context };
92 Box::new(plugin).register(&mut setup);
93
94 TestClient::new(router).with_context(context.expect("the builder survives setup").build())
95 }
96
97 #[tokio::test]
98 async fn the_plugin_puts_a_registry_where_handlers_can_find_it() {
99 registered(FeatureFlags::new(flags()))
100 .get("/checkout")
101 .await
102 .assert_ok()
103 .assert_see("new-checkout=false");
104 }
105
106 #[tokio::test]
107 async fn an_override_written_through_the_plugins_registry_is_seen_by_a_handler() {
108 let plugin = FeatureFlags::new(flags());
111 plugin.flags().activate("new-checkout").await.unwrap();
112
113 registered(plugin).get("/checkout").await.assert_ok().assert_see("new-checkout=true");
114 }
115
116 #[tokio::test]
117 async fn without_the_plugin_a_handler_gets_the_configuration_error() {
118 let mut router = Router::new();
119 router.get("/checkout", |request: Request| async move {
120 Ok::<_, Error>(format!("{}", request.flag("new-checkout").await?))
121 });
122
123 TestClient::new(router).get("/checkout").await.assert_status(500);
124 }
125
126 #[tokio::test]
127 async fn configuration_reaches_the_registry_through_the_plugin() {
128 let config = Config::new();
129 config.set("flags.off", "new-checkout");
130
131 let plugin = FeatureFlags::from_config(flags(), &config);
132 plugin.flags().activate("new-checkout").await.unwrap();
134
135 registered(plugin).get("/checkout").await.assert_ok().assert_see("new-checkout=false");
136 }
137
138 #[test]
139 fn the_plugin_is_named_for_route_list() {
140 assert_eq!(FeatureFlags::new(flags()).name(), "flags");
141 }
142}