Skip to content

Commit 04e3ce3

Browse files
committed
frontend: Make features code more modular
By making it more modular it is easier to add unit tests fro current behavior.
1 parent 1937862 commit 04e3ce3

File tree

1 file changed

+154
-22
lines changed

1 file changed

+154
-22
lines changed

src/web/features.rs

+154-22
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ use router::Router;
99
use serde::Serialize;
1010
use std::collections::{HashMap, VecDeque};
1111

12+
const DEFAULT_NAME: &str = "default";
13+
1214
#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
1315
struct FeaturesPage {
1416
metadata: MetaData,
@@ -38,28 +40,14 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
3840

3941
let row = cexpect!(req, rows.get(0));
4042

43+
let mut features = None;
4144
let mut default_len = 0;
42-
let features = row
43-
.get::<'_, usize, Option<Vec<Feature>>>(0)
44-
.map(|raw| {
45-
raw.into_iter()
46-
.filter(|feature| !feature.is_private())
47-
.map(|feature| (feature.name.clone(), feature))
48-
.collect::<HashMap<String, Feature>>()
49-
})
50-
.map(|mut feature_map| {
51-
let mut features = get_tree_structure_from_default(&mut feature_map);
52-
let mut remaining = feature_map
53-
.into_iter()
54-
.map(|(_, feature)| feature)
55-
.collect::<Vec<Feature>>();
56-
remaining.sort_by_key(|feature| feature.subfeatures.len());
57-
58-
default_len = features.len();
59-
60-
features.extend(remaining.into_iter().rev());
61-
features
62-
});
45+
46+
if let Some(raw) = row.get(0) {
47+
let result = order_features_and_count_default_len(raw);
48+
features = Some(result.0);
49+
default_len = result.1;
50+
}
6351

6452
FeaturesPage {
6553
metadata: cexpect!(req, MetaData::from_crate(&mut conn, &name, &version)),
@@ -69,11 +57,26 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
6957
.into_response(req)
7058
}
7159

60+
fn order_features_and_count_default_len(raw: Vec<Feature>) -> (Vec<Feature>, usize) {
61+
let mut feature_map = get_feature_map(raw);
62+
let mut features = get_tree_structure_from_default(&mut feature_map);
63+
let mut remaining: Vec<_> = feature_map
64+
.into_iter()
65+
.map(|(_, feature)| feature)
66+
.collect();
67+
remaining.sort_by_key(|feature| feature.subfeatures.len());
68+
69+
let default_len = features.len();
70+
71+
features.extend(remaining.into_iter().rev());
72+
(features, default_len)
73+
}
74+
7275
fn get_tree_structure_from_default(feature_map: &mut HashMap<String, Feature>) -> Vec<Feature> {
7376
let mut features = Vec::new();
7477
let mut queue: VecDeque<String> = VecDeque::new();
7578

76-
queue.push_back("default".into());
79+
queue.push_back(DEFAULT_NAME.into());
7780
while !queue.is_empty() {
7881
let name = queue.pop_front().unwrap();
7982
if let Some(feature) = feature_map.remove(&name) {
@@ -86,3 +89,132 @@ fn get_tree_structure_from_default(feature_map: &mut HashMap<String, Feature>) -
8689
}
8790
features
8891
}
92+
93+
fn get_feature_map(raw: Vec<Feature>) -> HashMap<String, Feature> {
94+
raw.into_iter()
95+
.filter(|feature| !feature.is_private())
96+
.map(|feature| (feature.name.clone(), feature))
97+
.collect()
98+
}
99+
100+
#[cfg(test)]
101+
mod tests {
102+
use crate::db::types::Feature;
103+
use crate::web::features::{
104+
get_feature_map, get_tree_structure_from_default, order_features_and_count_default_len,
105+
DEFAULT_NAME,
106+
};
107+
108+
#[test]
109+
fn test_feature_map_filters_private() {
110+
let private1 = Feature::new("_private1".into(), vec!["feature1".into()]);
111+
let feature2 = Feature::new("feature2".into(), Vec::new());
112+
113+
let raw = vec![private1.clone(), feature2.clone()];
114+
let feature_map = get_feature_map(raw);
115+
116+
assert_eq!(feature_map.len(), 1);
117+
assert!(feature_map.contains_key(&feature2.name));
118+
assert!(!feature_map.contains_key(&private1.name));
119+
}
120+
121+
#[test]
122+
fn test_default_tree_structure_with_nested_default() {
123+
let default = Feature::new(DEFAULT_NAME.into(), vec!["feature1".into()]);
124+
let non_default = Feature::new("non-default".into(), Vec::new());
125+
let feature1 = Feature::new(
126+
"feature1".into(),
127+
vec!["feature2".into(), "feature3".into()],
128+
);
129+
let feature2 = Feature::new("feature2".into(), Vec::new());
130+
let feature3 = Feature::new("feature3".into(), Vec::new());
131+
132+
let raw = vec![
133+
default.clone(),
134+
non_default.clone(),
135+
feature3.clone(),
136+
feature2.clone(),
137+
feature1.clone(),
138+
];
139+
let mut feature_map = get_feature_map(raw);
140+
let default_tree = get_tree_structure_from_default(&mut feature_map);
141+
142+
assert_eq!(feature_map.len(), 1);
143+
assert_eq!(default_tree.len(), 4);
144+
assert!(feature_map.contains_key(&non_default.name));
145+
assert!(!feature_map.contains_key(&default.name));
146+
assert_eq!(default_tree.get(0), Some(&default));
147+
assert_eq!(default_tree.get(1), Some(&feature1));
148+
assert_eq!(default_tree.get(2), Some(&feature2));
149+
assert_eq!(default_tree.get(3), Some(&feature3));
150+
}
151+
152+
#[test]
153+
fn test_default_tree_structure_without_default() {
154+
let feature1 = Feature::new(
155+
"feature1".into(),
156+
vec!["feature2".into(), "feature3".into()],
157+
);
158+
let feature2 = Feature::new("feature2".into(), Vec::new());
159+
let feature3 = Feature::new("feature3".into(), Vec::new());
160+
161+
let raw = vec![feature3.clone(), feature2.clone(), feature1.clone()];
162+
let mut feature_map = get_feature_map(raw);
163+
let default_tree = get_tree_structure_from_default(&mut feature_map);
164+
165+
assert_eq!(feature_map.len(), 3);
166+
assert_eq!(default_tree.len(), 0);
167+
assert!(feature_map.contains_key(&feature1.name));
168+
assert!(feature_map.contains_key(&feature2.name));
169+
assert!(feature_map.contains_key(&feature3.name));
170+
}
171+
172+
#[test]
173+
fn test_default_tree_structure_single_default() {
174+
let default = Feature::new(DEFAULT_NAME.into(), Vec::new());
175+
let non_default = Feature::new("non-default".into(), Vec::new());
176+
177+
let raw = vec![default.clone(), non_default.clone()];
178+
let mut feature_map = get_feature_map(raw);
179+
let default_tree = get_tree_structure_from_default(&mut feature_map);
180+
181+
assert_eq!(feature_map.len(), 1);
182+
assert_eq!(default_tree.len(), 1);
183+
assert!(feature_map.contains_key(&non_default.name));
184+
assert!(!feature_map.contains_key(&default.name));
185+
assert_eq!(default_tree.get(0), Some(&default));
186+
}
187+
188+
#[test]
189+
fn test_order_features_and_get_len_without_default() {
190+
let feature1 = Feature::new(
191+
"feature1".into(),
192+
vec!["feature10".into(), "feature11".into()],
193+
);
194+
let feature2 = Feature::new("feature2".into(), vec!["feature20".into()]);
195+
let feature3 = Feature::new("feature3".into(), Vec::new());
196+
197+
let raw = vec![feature3.clone(), feature2.clone(), feature1.clone()];
198+
let (features, default_len) = order_features_and_count_default_len(raw);
199+
200+
assert_eq!(features.len(), 3);
201+
assert_eq!(default_len, 0);
202+
assert_eq!(features.get(0), Some(&feature1));
203+
assert_eq!(features.get(1), Some(&feature2));
204+
assert_eq!(features.get(2), Some(&feature3));
205+
}
206+
207+
#[test]
208+
fn test_order_features_and_get_len_single_default() {
209+
let default = Feature::new(DEFAULT_NAME.into(), Vec::new());
210+
let non_default = Feature::new("non-default".into(), Vec::new());
211+
212+
let raw = vec![default.clone(), non_default.clone()];
213+
let (features, default_len) = order_features_and_count_default_len(raw);
214+
215+
assert_eq!(features.len(), 2);
216+
assert_eq!(default_len, 1);
217+
assert_eq!(features.get(0), Some(&default));
218+
assert_eq!(features.get(1), Some(&non_default));
219+
}
220+
}

0 commit comments

Comments
 (0)