Skip to content
This repository has been archived by the owner on Jun 21, 2024. It is now read-only.

Commit

Permalink
clean add test
Browse files Browse the repository at this point in the history
  • Loading branch information
neilkakkar committed May 2, 2024
1 parent 27e69e9 commit 68fe2bb
Show file tree
Hide file tree
Showing 9 changed files with 137 additions and 45 deletions.
33 changes: 16 additions & 17 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ metrics = "0.22.0"
metrics-exporter-prometheus = "0.14.0"
rand = "0.8.5"
rdkafka = { version = "0.36.0", features = ["cmake-build", "ssl", "tracing"] }
reqwest = { version = "0.12.3" }
reqwest = { version = "0.12.3", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
serde_derive = { version = "1.0" }
serde_json = { version = "1.0" }
Expand Down
11 changes: 6 additions & 5 deletions feature-flags/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,20 +16,21 @@ tracing = { workspace = true }
tracing-subscriber = { workspace = true, features = ["env-filter"] }
base64 = { workspace = true }
bytes = { workspace = true }
flate2 = { workspace = true }
governor = { workspace = true }
metrics = { workspace = true }
rand = { workspace = true }
rdkafka = { workspace = true }
redis = { version = "0.23.3", features = [
"tokio-comp",
"cluster",
"cluster-async",
] }
serde = { workspace = true }
serde_json = { workspace = true }
serde_urlencoded = { workspace = true }
thiserror = { workspace = true }

[lints]
workspace = true

[dev-dependencies]
assert-json-diff = { workspace = true }
once_cell = "1.18.0"
reqwest = { workspace = true }

7 changes: 2 additions & 5 deletions feature-flags/src/config.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,10 @@
use std::{net::SocketAddr, num::NonZeroU32};
use std::net::SocketAddr;

use envconfig::Envconfig;

#[derive(Envconfig, Clone)]
pub struct Config {
#[envconfig(default = "false")]
pub print_sink: bool,

#[envconfig(default = "127.0.0.1:3001")]
#[envconfig(default = "127.0.0.1:0")]
pub address: SocketAddr,

#[envconfig(default = "postgres://posthog:posthog@localhost:15432/test_database")]
Expand Down
7 changes: 1 addition & 6 deletions feature-flags/src/router.rs
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
use std::future::ready;
use std::sync::Arc;

use axum::http::Method;
use axum::{
routing::{get, post},
Router,
};
use axum::{routing::post, Router};

use crate::{redis::Client, v0_endpoint};

Expand Down
6 changes: 2 additions & 4 deletions feature-flags/src/v0_endpoint.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,11 @@
use std::collections::HashMap;
use std::ops::Deref;
use std::sync::Arc;

use axum::{debug_handler, Json};
use bytes::Bytes;
// TODO: stream this instead
use axum::extract::{MatchedPath, Query, State};
use axum::http::{HeaderMap, Method};
use axum_client_ip::InsecureClientIp;
use base64::Engine;
use tracing::instrument;

use crate::{
Expand Down Expand Up @@ -36,7 +33,7 @@ use crate::{
)]
#[debug_handler]
pub async fn flags(
state: State<router::State>,
_state: State<router::State>,
InsecureClientIp(ip): InsecureClientIp,
meta: Query<FlagsQueryParams>,
headers: HeaderMap,
Expand All @@ -56,6 +53,7 @@ pub async fn flags(
tracing::Span::current().record("version", meta.version.clone());
tracing::Span::current().record("method", method.as_str());
tracing::Span::current().record("path", path.as_str().trim_end_matches('/'));
tracing::Span::current().record("ip", ip.to_string());

let request = match headers
.get("content-type")
Expand Down
7 changes: 0 additions & 7 deletions feature-flags/src/v0_request.rs
Original file line number Diff line number Diff line change
@@ -1,23 +1,16 @@
use std::collections::HashMap;
// use std::io::prelude::*;

use bytes::Bytes;
use serde::{Deserialize, Serialize};
use serde_json::Value;
// use time::format_description::well_known::Iso8601;
// use time::OffsetDateTime;
use tracing::instrument;
// use uuid::Uuid;

use crate::api::FlagError;

#[derive(Deserialize, Default)]
pub struct FlagsQueryParams {
#[serde(alias = "v")]
pub version: Option<String>,

#[serde(alias = "_")]
sent_at: Option<i64>,
}

#[derive(Default, Debug, Deserialize, Serialize)]
Expand Down
66 changes: 66 additions & 0 deletions feature-flags/tests/common.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
use std::net::SocketAddr;
use std::str::FromStr;
use std::string::ToString;
use std::sync::Arc;

use once_cell::sync::Lazy;
use rand::distributions::Alphanumeric;
use rand::Rng;
use tokio::net::TcpListener;
use tokio::sync::Notify;

use feature_flags::config::Config;
use feature_flags::server::serve;

pub static DEFAULT_CONFIG: Lazy<Config> = Lazy::new(|| Config {
address: SocketAddr::from_str("127.0.0.1:0").unwrap(),
redis_url: "redis://localhost:6379/".to_string(),
write_database_url: "postgres://posthog:posthog@localhost:15432/test_database".to_string(),
read_database_url: "postgres://posthog:posthog@localhost:15432/test_database".to_string(),
max_concurrent_jobs: 1024,
max_pg_connections: 100,
});

pub struct ServerHandle {
pub addr: SocketAddr,
shutdown: Arc<Notify>,
}

impl ServerHandle {
pub async fn for_config(config: Config) -> ServerHandle {
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
let addr = listener.local_addr().unwrap();
let notify = Arc::new(Notify::new());
let shutdown = notify.clone();

tokio::spawn(async move {
serve(config, listener, async move { notify.notified().await }).await
});
ServerHandle { addr, shutdown }
}

pub async fn send_flags_request<T: Into<reqwest::Body>>(&self, body: T) -> reqwest::Response {
let client = reqwest::Client::new();
client
.post(format!("http://{:?}/flags", self.addr))
.body(body)
.send()
.await
.expect("failed to send request")
}
}

impl Drop for ServerHandle {
fn drop(&mut self) {
self.shutdown.notify_one()
}
}

pub fn random_string(prefix: &str, length: usize) -> String {
let suffix: String = rand::thread_rng()
.sample_iter(Alphanumeric)
.take(length)
.map(char::from)
.collect();
format!("{}_{}", prefix, suffix)
}
43 changes: 43 additions & 0 deletions feature-flags/tests/test_flags.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
use anyhow::Result;
use assert_json_diff::assert_json_include;

use reqwest::StatusCode;
use serde_json::{json, Value};

use crate::common::*;
mod common;

#[tokio::test]
async fn it_sends_flag_request() -> Result<()> {
let token = random_string("token", 16);
let distinct_id = "user_distinct_id".to_string();

let config = DEFAULT_CONFIG.clone();

let server = ServerHandle::for_config(config).await;

let payload = json!({
"token": token,
"distinct_id": distinct_id,
"groups": {"group1": "group1"}
});
let res = server.send_flags_request(payload.to_string()).await;
assert_eq!(StatusCode::OK, res.status());

// We don't want to deserialize the data into a flagResponse struct here,
// because we want to assert the shape of the raw json data.
let json_data = res.json::<Value>().await?;

assert_json_include!(
actual: json_data,
expected: json!({
"errorWhileComputingFlags": false,
"featureFlags": {
"beta-feature": "variant-1",
"rollout-flag": "true",
}
})
);

Ok(())
}

0 comments on commit 68fe2bb

Please sign in to comment.