• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Unleash / unleash-edge / 15441100167

04 Jun 2025 11:28AM UTC coverage: 78.265% (+10.3%) from 67.995%
15441100167

Pull #970

github

web-flow
Merge 8ad457ed6 into 34ad3228b
Pull Request #970: task(rust): Update Rust version to 1.87.0

10140 of 12956 relevant lines covered (78.26%)

158.35 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.89
/server/src/delta_cache.rs
1
use unleash_types::client_features::{ClientFeature, DeltaEvent, Segment};
2

3
#[derive(Debug, Clone)]
4
pub struct DeltaHydrationEvent {
5
    pub event_id: u32,
6
    pub features: Vec<ClientFeature>,
7
    pub segments: Vec<Segment>,
8
}
9

10
#[derive(Debug, Clone)]
11
pub struct DeltaCache {
12
    max_length: usize,
13
    events: Vec<DeltaEvent>,
14
    hydration_event: DeltaHydrationEvent,
15
}
16

17
impl DeltaCache {
18
    pub fn new(hydration_event: DeltaHydrationEvent, max_length: usize) -> Self {
11✔
19
        let mut cache = DeltaCache {
11✔
20
            max_length,
11✔
21
            events: Vec::new(),
11✔
22
            hydration_event: hydration_event.clone(),
11✔
23
        };
11✔
24
        cache.add_base_event_from_hydration(&hydration_event);
11✔
25
        cache
11✔
26
    }
11✔
27

28
    fn add_base_event_from_hydration(&mut self, hydration_event: &DeltaHydrationEvent) {
11✔
29
        let last_feature = hydration_event
11✔
30
            .features
11✔
31
            .last()
11✔
32
            .cloned()
11✔
33
            .expect("Hydration event must have at least one feature");
11✔
34

11✔
35
        self.add_events(&vec![DeltaEvent::FeatureUpdated {
11✔
36
            event_id: hydration_event.event_id,
11✔
37
            feature: last_feature,
11✔
38
        }]);
11✔
39
    }
11✔
40

41
    pub fn has_revision(&self, revision: u32) -> bool {
12✔
42
        self.get_events()
12✔
43
            .iter()
12✔
44
            .any(|e| e.get_event_id() == revision)
15✔
45
    }
12✔
46

47
    pub fn add_events(&mut self, events: &[DeltaEvent]) {
21✔
48
        for event in events.iter() {
25✔
49
            self.events.push(event.clone());
25✔
50
            self.update_hydration_event(event);
25✔
51

25✔
52
            if self.events.len() > self.max_length {
25✔
53
                self.events.remove(0);
5✔
54
            }
20✔
55
        }
56
    }
21✔
57

58
    pub fn get_events(&self) -> &Vec<DeltaEvent> {
32✔
59
        &self.events
32✔
60
    }
32✔
61

62
    pub fn is_missing_revision(&self, revision_id: u32) -> bool {
×
63
        !self
×
64
            .events
×
65
            .iter()
×
66
            .any(|event| event.get_event_id() == revision_id)
×
67
    }
×
68

69
    pub fn get_hydration_event(&self) -> &DeltaHydrationEvent {
25✔
70
        &self.hydration_event
25✔
71
    }
25✔
72

73
    fn update_hydration_event(&mut self, event: &DeltaEvent) {
25✔
74
        let event_id = event.get_event_id();
25✔
75
        self.hydration_event.event_id = event_id;
25✔
76
        match event {
25✔
77
            DeltaEvent::FeatureUpdated { feature, .. } => {
19✔
78
                if let Some(existing) = self
19✔
79
                    .hydration_event
19✔
80
                    .features
19✔
81
                    .iter_mut()
19✔
82
                    .find(|f| f.name == feature.name)
23✔
83
                {
14✔
84
                    *existing = feature.clone();
14✔
85
                } else {
14✔
86
                    self.hydration_event.features.push(feature.clone());
5✔
87
                }
5✔
88
            }
89
            DeltaEvent::FeatureRemoved { feature_name, .. } => {
2✔
90
                self.hydration_event
2✔
91
                    .features
2✔
92
                    .retain(|f| f.name != feature_name.clone());
4✔
93
            }
2✔
94
            DeltaEvent::SegmentUpdated { segment, .. } => {
2✔
95
                if let Some(existing) = self
2✔
96
                    .hydration_event
2✔
97
                    .segments
2✔
98
                    .iter_mut()
2✔
99
                    .find(|s| s.id == segment.id)
2✔
100
                {
1✔
101
                    *existing = segment.clone();
1✔
102
                } else {
1✔
103
                    self.hydration_event.segments.push(segment.clone());
1✔
104
                }
1✔
105
            }
106
            DeltaEvent::SegmentRemoved { segment_id, .. } => {
2✔
107
                self.hydration_event
2✔
108
                    .segments
2✔
109
                    .retain(|s| s.id != *segment_id);
2✔
110
            }
2✔
111
            DeltaEvent::Hydration { .. } => {
×
112
                // do nothing, as hydration will never end up in update events
×
113
            }
×
114
        }
115
    }
25✔
116
}
117

118
#[cfg(test)]
119
mod tests {
120
    use crate::delta_cache::{DeltaCache, DeltaHydrationEvent};
121
    use unleash_types::client_features::{ClientFeature, DeltaEvent, Segment};
122

123
    #[test]
124
    fn test_update_hydration_event_and_remove_event_when_over_limit() {
1✔
125
        let base_event = DeltaHydrationEvent {
1✔
126
            event_id: 1,
1✔
127
            features: vec![
1✔
128
                ClientFeature {
1✔
129
                    name: "test-flag".to_string(),
1✔
130
                    ..ClientFeature::default()
1✔
131
                },
1✔
132
                ClientFeature {
1✔
133
                    name: "my-feature-flag".to_string(),
1✔
134
                    ..ClientFeature::default()
1✔
135
                },
1✔
136
            ],
1✔
137
            segments: vec![
1✔
138
                Segment {
1✔
139
                    id: 1,
1✔
140
                    constraints: vec![],
1✔
141
                },
1✔
142
                Segment {
1✔
143
                    id: 2,
1✔
144
                    constraints: vec![],
1✔
145
                },
1✔
146
            ],
1✔
147
        };
1✔
148
        let max_length = 2;
1✔
149
        let mut delta_cache = DeltaCache::new(base_event.clone(), max_length);
1✔
150

1✔
151
        let initial_events = &vec![DeltaEvent::FeatureUpdated {
1✔
152
            event_id: 2,
1✔
153
            feature: ClientFeature {
1✔
154
                name: "my-feature-flag".to_string(),
1✔
155
                ..ClientFeature::default()
1✔
156
            },
1✔
157
        }];
1✔
158
        delta_cache.add_events(initial_events);
1✔
159

1✔
160
        let added_events = vec![
1✔
161
            DeltaEvent::FeatureUpdated {
1✔
162
                event_id: 3,
1✔
163
                feature: ClientFeature {
1✔
164
                    name: "another-feature-flag".to_string(),
1✔
165
                    ..ClientFeature::default()
1✔
166
                },
1✔
167
            },
1✔
168
            DeltaEvent::FeatureRemoved {
1✔
169
                event_id: 4,
1✔
170
                feature_name: "test-flag".to_string(),
1✔
171
                project: "default".to_string(),
1✔
172
            },
1✔
173
            DeltaEvent::SegmentUpdated {
1✔
174
                event_id: 5,
1✔
175
                segment: Segment {
1✔
176
                    id: 1,
1✔
177
                    constraints: vec![],
1✔
178
                },
1✔
179
            },
1✔
180
            DeltaEvent::SegmentRemoved {
1✔
181
                event_id: 6,
1✔
182
                segment_id: 2,
1✔
183
            },
1✔
184
            DeltaEvent::SegmentUpdated {
1✔
185
                event_id: 7,
1✔
186
                segment: Segment {
1✔
187
                    id: 3,
1✔
188
                    constraints: vec![],
1✔
189
                },
1✔
190
            },
1✔
191
        ];
1✔
192
        delta_cache.add_events(&added_events);
1✔
193

1✔
194
        let events: Vec<_> = delta_cache.get_events().to_vec();
1✔
195
        assert_eq!(events.len(), max_length);
1✔
196
        assert_eq!(events, added_events[added_events.len() - max_length..]);
1✔
197

198
        let hydration_event = delta_cache.get_hydration_event();
1✔
199
        assert_eq!(hydration_event.features.len(), 2);
1✔
200
        assert_eq!(hydration_event.event_id, 7);
1✔
201
        assert!(
1✔
202
            hydration_event
1✔
203
                .features
1✔
204
                .iter()
1✔
205
                .any(|f| f.name == "my-feature-flag")
1✔
206
        );
1✔
207
        assert!(
1✔
208
            hydration_event
1✔
209
                .features
1✔
210
                .iter()
1✔
211
                .any(|f| f.name == "another-feature-flag")
2✔
212
        );
1✔
213
        assert!(hydration_event.segments.iter().any(|s| s.id == 1));
1✔
214
    }
1✔
215

216
    #[test]
217
    fn test_prevent_mutation_of_previous_feature_updated_events() {
1✔
218
        let base_event = DeltaHydrationEvent {
1✔
219
            event_id: 1,
1✔
220
            features: vec![ClientFeature {
1✔
221
                name: "base-flag".to_string(),
1✔
222
                ..ClientFeature::default()
1✔
223
            }],
1✔
224
            segments: vec![],
1✔
225
        };
1✔
226
        let mut delta_cache = DeltaCache::new(base_event, 10);
1✔
227

1✔
228
        let initial_feature_event = DeltaEvent::FeatureUpdated {
1✔
229
            event_id: 129,
1✔
230
            feature: ClientFeature {
1✔
231
                name: "streaming-test".to_string(),
1✔
232
                enabled: false,
1✔
233
                ..ClientFeature::default()
1✔
234
            },
1✔
235
        };
1✔
236
        delta_cache.add_events(&vec![initial_feature_event.clone()]);
1✔
237

1✔
238
        let updated_feature_event = DeltaEvent::FeatureUpdated {
1✔
239
            event_id: 130,
1✔
240
            feature: ClientFeature {
1✔
241
                name: "streaming-test".to_string(),
1✔
242
                enabled: true,
1✔
243
                strategies: Some(vec![unleash_types::client_features::Strategy {
1✔
244
                    name: "new-strategy".into(),
1✔
245
                    sort_order: None,
1✔
246
                    segments: None,
1✔
247
                    variants: None,
1✔
248
                    constraints: None,
1✔
249
                    parameters: None,
1✔
250
                }]),
1✔
251
                ..ClientFeature::default()
1✔
252
            },
1✔
253
        };
1✔
254
        delta_cache.add_events(&vec![updated_feature_event.clone()]);
1✔
255

1✔
256
        assert_eq!(delta_cache.get_events()[1], initial_feature_event);
1✔
257
        assert_eq!(delta_cache.get_events()[2], updated_feature_event);
1✔
258
    }
1✔
259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc