1use super::{FeatureCategory, FeatureTest};
10
11pub const TESTS: &[FeatureTest] = &[
12 FeatureTest {
14 name: "stream_def_basic",
15 covers: &["stream_def"],
16 code: r#"
17 stream Counter {
18 state { count: 0 }
19 on_tick { state.count = state.count + 1; }
20 }
21 function test() { return 1; }
22 "#,
23 function: "test",
24 category: FeatureCategory::Domain,
25 requires_data: false,
26 },
27 FeatureTest {
28 name: "stream_def_with_params",
29 covers: &["stream_def", "function_params"],
30 code: r#"
31 stream MovingAverage(period) {
32 state { sum: 0, count: 0 }
33 on_tick {
34 state.sum = state.sum + data[0].close;
35 state.count = state.count + 1;
36 }
37 }
38 function test() { return 1; }
39 "#,
40 function: "test",
41 category: FeatureCategory::Domain,
42 requires_data: false,
43 },
44 FeatureTest {
46 name: "stream_state_simple",
47 covers: &["stream_state", "object_literal"],
48 code: r#"
49 stream TestStream {
50 state { value: 0 }
51 on_tick { }
52 }
53 function test() { return 1; }
54 "#,
55 function: "test",
56 category: FeatureCategory::Domain,
57 requires_data: false,
58 },
59 FeatureTest {
60 name: "stream_state_multiple_fields",
61 covers: &["stream_state", "object_literal", "object_field"],
62 code: r#"
63 stream TestStream {
64 state {
65 count: 0,
66 sum: 0.0,
67 high: -999999,
68 low: 999999,
69 active: false
70 }
71 on_tick { }
72 }
73 function test() { return 1; }
74 "#,
75 function: "test",
76 category: FeatureCategory::Domain,
77 requires_data: false,
78 },
79 FeatureTest {
80 name: "stream_state_nested",
81 covers: &["stream_state", "object_literal"],
82 code: r#"
83 stream TestStream {
84 state {
85 position: { size: 0, entry_price: 0 },
86 stats: { wins: 0, losses: 0 }
87 }
88 on_tick { }
89 }
90 function test() { return 1; }
91 "#,
92 function: "test",
93 category: FeatureCategory::Domain,
94 requires_data: false,
95 },
96 FeatureTest {
98 name: "stream_on_tick_basic",
99 covers: &["stream_on_tick", "block_expr"],
100 code: r#"
101 stream TickCounter {
102 state { ticks: 0 }
103 on_tick {
104 state.ticks = state.ticks + 1;
105 }
106 }
107 function test() { return 1; }
108 "#,
109 function: "test",
110 category: FeatureCategory::Domain,
111 requires_data: false,
112 },
113 FeatureTest {
114 name: "stream_on_tick_with_logic",
115 covers: &["stream_on_tick", "if_stmt", "block_expr"],
116 code: r#"
117 stream HighTracker {
118 state { high: 0 }
119 on_tick {
120 if (data[0].high > state.high) {
121 state.high = data[0].high;
122 }
123 }
124 }
125 function test() { return 1; }
126 "#,
127 function: "test",
128 category: FeatureCategory::Domain,
129 requires_data: false,
130 },
131 FeatureTest {
132 name: "stream_on_tick_emit",
133 covers: &["stream_on_tick", "function_call"],
134 code: r#"
135 stream SignalEmitter {
136 state { triggered: false }
137 on_tick {
138 if (data[0].close > data[1].close && !state.triggered) {
139 emit("buy_signal", { price: data[0].close });
140 state.triggered = true;
141 }
142 }
143 }
144 function test() { return 1; }
145 "#,
146 function: "test",
147 category: FeatureCategory::Domain,
148 requires_data: false,
149 },
150 FeatureTest {
152 name: "stream_on_bar_basic",
153 covers: &["stream_on_bar", "block_expr"],
154 code: r#"
155 stream BarCounter {
156 state { bars: 0 }
157 on_bar {
158 state.bars = state.bars + 1;
159 }
160 }
161 function test() { return 1; }
162 "#,
163 function: "test",
164 category: FeatureCategory::Domain,
165 requires_data: false,
166 },
167 FeatureTest {
168 name: "stream_on_bar_aggregation",
169 covers: &["stream_on_bar", "block_expr", "assignment"],
170 code: r#"
171 stream VolumeAccumulator {
172 state { total_volume: 0, bar_count: 0 }
173 on_bar {
174 state.total_volume = state.total_volume + bar.volume;
175 state.bar_count = state.bar_count + 1;
176 }
177 }
178 function test() { return 1; }
179 "#,
180 function: "test",
181 category: FeatureCategory::Domain,
182 requires_data: false,
183 },
184 FeatureTest {
185 name: "stream_on_bar_with_condition",
186 covers: &["stream_on_bar", "if_stmt"],
187 code: r#"
188 stream GreenBarCounter {
189 state { green_bars: 0, red_bars: 0 }
190 on_bar {
191 if (bar.close > bar.open) {
192 state.green_bars = state.green_bars + 1;
193 } else {
194 state.red_bars = state.red_bars + 1;
195 }
196 }
197 }
198 function test() { return 1; }
199 "#,
200 function: "test",
201 category: FeatureCategory::Domain,
202 requires_data: false,
203 },
204 FeatureTest {
206 name: "stream_combined_handlers",
207 covers: &[
208 "stream_def",
209 "stream_state",
210 "stream_on_tick",
211 "stream_on_bar",
212 ],
213 code: r#"
214 stream CompleteStream {
215 state {
216 tick_count: 0,
217 bar_count: 0,
218 last_price: 0
219 }
220
221 on_tick {
222 state.tick_count = state.tick_count + 1;
223 state.last_price = data[0].close;
224 }
225
226 on_bar {
227 state.bar_count = state.bar_count + 1;
228 }
229 }
230 function test() { return 1; }
231 "#,
232 function: "test",
233 category: FeatureCategory::Domain,
234 requires_data: false,
235 },
236];
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use std::collections::BTreeSet;
242
243 #[test]
244 fn test_stream_tests_defined() {
245 assert!(!TESTS.is_empty());
246 assert!(
248 TESTS.len() >= 8,
249 "Expected at least 8 stream tests, got {}",
250 TESTS.len()
251 );
252 }
253
254 #[test]
255 fn test_stream_tests_cover_main_rules() {
256 let all_rules: BTreeSet<_> = TESTS
257 .iter()
258 .flat_map(|t| t.covers.iter().copied())
259 .collect();
260
261 assert!(
263 all_rules.contains(&"stream_def"),
264 "Missing stream_def coverage"
265 );
266 assert!(
267 all_rules.contains(&"stream_state"),
268 "Missing stream_state coverage"
269 );
270 assert!(
271 all_rules.contains(&"stream_on_tick"),
272 "Missing stream_on_tick coverage"
273 );
274 assert!(
275 all_rules.contains(&"stream_on_bar"),
276 "Missing stream_on_bar coverage"
277 );
278 }
279}