use crate::flags::Flags;
use rustlavel_core::Config;
use rustlavel_http::plugin::{Plugin, Setup};
pub struct FeatureFlags {
flags: Flags,
}
impl FeatureFlags {
pub fn new(flags: Flags) -> Self {
FeatureFlags { flags }
}
pub fn from_config(flags: Flags, config: &Config) -> Self {
FeatureFlags { flags: flags.configured(config) }
}
pub fn flags(&self) -> &Flags {
&self.flags
}
}
impl Plugin for FeatureFlags {
fn name(&self) -> &'static str {
"flags"
}
fn register(self: Box<Self>, setup: &mut Setup<'_>) {
setup.state(self.flags.clone());
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::guard::FlagsExt;
use crate::scope::Scope;
use rustlavel_core::{Context, Error};
use rustlavel_http::router::Router;
use rustlavel_http::{Request, TestClient};
fn flags() -> Flags {
Flags::new().define("new-checkout", |scope: Scope| async move { scope.id().ends_with('7') })
}
fn registered(plugin: FeatureFlags) -> TestClient {
let mut router = Router::new();
let config = Config::with_defaults();
let mut context = Some(Context::builder());
router.get("/checkout", |request: Request| async move {
let on = request.flag("new-checkout").await?;
Ok::<_, Error>(format!("new-checkout={on}"))
});
let mut setup = Setup { router: &mut router, config: &config, context: &mut context };
Box::new(plugin).register(&mut setup);
TestClient::new(router).with_context(context.expect("the builder survives setup").build())
}
#[tokio::test]
async fn the_plugin_puts_a_registry_where_handlers_can_find_it() {
registered(FeatureFlags::new(flags()))
.get("/checkout")
.await
.assert_ok()
.assert_see("new-checkout=false");
}
#[tokio::test]
async fn an_override_written_through_the_plugins_registry_is_seen_by_a_handler() {
let plugin = FeatureFlags::new(flags());
plugin.flags().activate("new-checkout").await.unwrap();
registered(plugin).get("/checkout").await.assert_ok().assert_see("new-checkout=true");
}
#[tokio::test]
async fn without_the_plugin_a_handler_gets_the_configuration_error() {
let mut router = Router::new();
router.get("/checkout", |request: Request| async move {
Ok::<_, Error>(format!("{}", request.flag("new-checkout").await?))
});
TestClient::new(router).get("/checkout").await.assert_status(500);
}
#[tokio::test]
async fn configuration_reaches_the_registry_through_the_plugin() {
let config = Config::new();
config.set("flags.off", "new-checkout");
let plugin = FeatureFlags::from_config(flags(), &config);
plugin.flags().activate("new-checkout").await.unwrap();
registered(plugin).get("/checkout").await.assert_ok().assert_see("new-checkout=false");
}
#[test]
fn the_plugin_is_named_for_route_list() {
assert_eq!(FeatureFlags::new(flags()).name(), "flags");
}
}