tera_rand/
net.rs

1use crate::common::{gen_value_in_range, parse_arg};
2use crate::error::cidr_prefix_length_out_of_bounds;
3use rand::{rng, Rng};
4use std::collections::HashMap;
5use std::net::{Ipv4Addr, Ipv6Addr};
6use tera::{to_value, Result, Value};
7
8/// A Tera function to generate a random IPv4 address.
9///
10/// The `start` parameter takes an IPv4 address to indicate the beginning of the
11/// range (inclusive). If `start` is not passed in, it defaults to `0.0.0.0`.
12///
13/// The `end` parameter also takes an IPv4 address indicating the end of the range,
14/// which is also inclusive. An inclusive range allows `255.255.255.255` to be sampled where an
15/// exclusive range does not. If `end` is not passed in, it defaults to `255.255.255.255`.
16///
17/// It is possible to pass in both `start` and `end`, just one of them, or neither.
18///
19/// # Example usage
20///
21/// ```edition2021
22/// use tera::{Context, Tera};
23/// use tera_rand::random_ipv4;
24///
25/// let mut tera: Tera = Tera::default();
26/// tera.register_function("random_ipv4", random_ipv4);
27/// let context: Context = Context::new();
28///
29/// // bound by both start and end
30/// let rendered: String = tera
31///     .render_str(r#"{{ random_ipv4(start="127.0.0.0", end="128.0.0.0") }}"#,  &context)
32///     .unwrap();
33/// // bound by just start
34/// let rendered: String = tera
35///     .render_str(r#"{{ random_ipv4(start="127.0.0.0") }}"#, &context)
36///     .unwrap();
37/// // bound by just end
38/// let rendered: String = tera
39///     .render_str(r#"{{ random_ipv4(end="128.0.0.0") }}"#, &context)
40///     .unwrap();
41/// // bound by neither start nor end
42/// let rendered: String = tera
43///     .render_str(r#"{{ random_ipv4() }}"#, &context)
44///     .unwrap();
45/// ```
46pub fn random_ipv4(args: &HashMap<String, Value>) -> Result<Value> {
47    let start_opt: Option<u32> = parse_arg(args, "start")?.map(|start: Ipv4Addr| start.into());
48
49    let end_opt: Option<u32> = parse_arg(args, "end")?.map(|end: Ipv4Addr| end.into());
50
51    let random_ipv4: u32 = gen_value_in_range(start_opt, end_opt, u32::MIN, u32::MAX);
52    let random_ipv4: Ipv4Addr = random_ipv4.into();
53
54    let json_value: Value = to_value(random_ipv4)?;
55    Ok(json_value)
56}
57
58/// A Tera function to generate a random IPv6 address.
59///
60/// The `start` parameter takes an IPv6 address to indicate the beginning of the
61/// range (inclusive). If `start` is not passed in, it defaults to `::`.
62///
63/// The `end` parameter also takes an IPv6 address indicating the end of the range,
64/// which is also inclusive. An inclusive range allows `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`
65/// to be sampled where an exclusive range does not. If `end` is not passed in, it defaults to
66/// `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`.
67///
68/// It is possible to pass in both `start` and `end`, just one of them, or neither.
69///
70/// # Example usage
71///
72/// ```edition2021
73/// use tera::{Context, Tera};
74/// use tera_rand::random_ipv6;
75///
76/// let mut tera: Tera = Tera::default();
77/// tera.register_function("random_ipv6", random_ipv6);
78/// let context: Context = Context::new();
79///
80/// // bound by both start and end
81/// let rendered: String = tera
82///     .render_str(r#"{{ random_ipv6(start="fc00::", end="fd00::") }}"#, &context)
83///     .unwrap();
84/// // bound by just start
85/// let rendered: String = tera
86///     .render_str(r#"{{ random_ipv6(start="fc00::") }}"#, &context)
87///     .unwrap();
88/// // bound by just end
89/// let rendered: String = tera
90///     .render_str(r#"{{ random_ipv6(end="fd00::") }}"#, &context)
91///     .unwrap();
92/// // bound by neither start nor end
93/// let rendered: String = tera
94///     .render_str("{{ random_ipv6() }}", &context)
95///     .unwrap();
96/// ```
97pub fn random_ipv6(args: &HashMap<String, Value>) -> Result<Value> {
98    let start_opt: Option<u128> =
99        parse_arg(args, "start")?.map(|start_ipv6: Ipv6Addr| start_ipv6.into());
100
101    let end_opt: Option<u128> = parse_arg(args, "end")?.map(|end_ipv6: Ipv6Addr| end_ipv6.into());
102
103    let random_ipv6: u128 = gen_value_in_range(start_opt, end_opt, u128::MIN, u128::MAX);
104    let random_ipv6: Ipv6Addr = random_ipv6.into();
105
106    let json_value: Value = to_value(random_ipv6)?;
107    Ok(json_value)
108}
109
110/// A Tera function to generate a random IPv4 CIDR address.
111///
112/// The `length_start` parameter takes an integer between 0 and 32 (inclusive) to indicate the
113/// random prefix length of the generated CIDR should be at least `length_start`. If
114/// `length_start` is not passed in, it defaults to 0.
115///
116/// The `length_end` parameter takes an integer between 0 and 32 (inclusive) to indicate the
117/// random prefix length of the generated CIDR should be at most `length_end`. If
118/// `length_end` is not passed in, it defaults to 32.
119///
120/// The `addr_start` parameter takes an IPv4 address. This address will be used as the inclusive
121/// lower bound for generating the random address before the address is masked into a prefix.
122/// If `addr_start` is not passed in, it defaults to `0.0.0.0`.
123///
124/// The `addr_end` parameter takes an IPv4 address. This address will be used as the inclusive
125/// upper bound for generating the random address before the address is masked into a prefix.
126/// If `addr_start` is not passed in, it defaults to `0.0.0.0`.
127///
128/// All of these parameters are optional, and it is possible to use any combination.
129///
130/// # Example usage
131///
132/// ```edition2021
133/// use tera::{Context, Tera};
134/// use tera_rand::random_ipv4_cidr;
135///
136/// let mut tera: Tera = Tera::default();
137/// tera.register_function("random_ipv4_cidr", random_ipv4_cidr);
138/// let context: Context = Context::new();
139///
140/// // bound by neither prefix bit values nor the prefix length
141/// let rendered: String = tera
142///     .render_str("{{ random_ipv4_cidr() }}", &context)
143///     .unwrap();
144///
145/// // prefix length bound by start and end lengths
146/// let rendered: String = tera
147///     .render_str(r#"{{ random_ipv4_cidr(length_start=16, length_end=24) }}"#, &context)
148///     .unwrap();
149/// // prefix length bound by start length
150/// let rendered: String = tera
151///     .render_str(r#"{{ random_ipv4_cidr(length_start=16) }}"#, &context)
152///     .unwrap();
153/// // prefix length bound by end length
154/// let rendered: String = tera
155///     .render_str(r#"{{ random_ipv4_cidr(length_end=24) }}"#, &context)
156///     .unwrap();
157///
158/// // prefix bits bound by a start address and end address
159/// let rendered: String = tera
160///     .render_str(r#"{{ random_ipv4_cidr(addr_start="10.120.0.0", addr_end="10.140.0.0") }}"#, &context)
161///     .unwrap();
162/// // prefix bits bound by a start address
163/// let rendered: String = tera
164///     .render_str(r#"{{ random_ipv4_cidr(start="10.120.0.0") }}"#, &context )
165///     .unwrap();
166/// // prefix bits bound by an end address
167/// let rendered: String = tera
168///     .render_str(r#"{{ random_ipv4_cidr(addr_end="10.140.0.0") }}"#, &context)
169///     .unwrap();
170///
171/// // prefix length bound by both prefix bit values and length
172/// let rendered: String = tera
173///     .render_str(
174///         r#"{{ random_ipv4_cidr(
175///                   addr_start="10.120.0.0",
176///                   addr_end="10.140.0.0",
177///                   length_start=16,
178///                   length_end=24
179///               ) }}"#,
180///         &context
181///     )
182///     .unwrap();
183/// ```
184pub fn random_ipv4_cidr(args: &HashMap<String, Value>) -> Result<Value> {
185    let addr_start_opt: Option<u32> =
186        parse_arg(args, "addr_start")?.map(|addr_start: Ipv4Addr| addr_start.into());
187    let addr_end_opt: Option<u32> =
188        parse_arg(args, "addr_end")?.map(|addr_end: Ipv4Addr| addr_end.into());
189
190    let random_addr: u32 = gen_value_in_range(addr_start_opt, addr_end_opt, u32::MIN, u32::MAX);
191
192    let length_start: u32 =
193        parse_cidr_prefix_length_and_check_bounds(args, "length_start", 0u32, u32::BITS)?
194            .unwrap_or(0u32);
195    let length_end: u32 =
196        parse_cidr_prefix_length_and_check_bounds(args, "length_end", 0u32, u32::BITS)?
197            .unwrap_or(u32::BITS);
198
199    let random_prefix_length: u32 = rng().random_range(length_start..=length_end);
200    let bits_to_shift: u32 = u32::BITS - random_prefix_length;
201
202    let random_prefix: u32 = match bits_to_shift {
203        u32::BITS => 0u32,
204        bits_to_shift => random_addr >> bits_to_shift << bits_to_shift,
205    };
206    let random_prefix: Ipv4Addr = random_prefix.into();
207
208    let random_cidr: String = format!("{}/{}", random_prefix.to_string(), random_prefix_length);
209    let json_value: Value = to_value(random_cidr)?;
210    Ok(json_value)
211}
212
213/// A Tera function to generate a random IPv6 CIDR address.
214///
215/// The `length_start` parameter takes an integer between 0 and 128 (inclusive) to indicate the
216/// random prefix length of the generated CIDR should be at least `length_start`. If
217/// `length_start` is not passed in, it defaults to 0.
218///
219/// The `length_end` parameter takes an integer between 0 and 128 (inclusive) to indicate the
220/// random prefix length of the generated CIDR should be at most `length_end`. If
221/// `length_end` is not passed in, it defaults to 128.
222///
223/// The `addr_start` parameter takes an IPv4 address. This address will be used as the inclusive
224/// lower bound for generating the random address before the address is masked into a prefix.
225/// If `addr_start` is not passed in, it defaults to `::`.
226///
227/// The `addr_end` parameter takes an IPv4 address. This address will be used as the inclusive
228/// upper bound for generating the random address before the address is masked into a prefix.
229/// If `addr_start` is not passed in, it defaults to `ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff`.
230///
231/// All of these parameters are optional, and it is possible to use any combination.
232///
233/// # Example usage
234///
235/// ```edition2021
236/// use tera::{Context, Tera};
237/// use tera_rand::random_ipv6_cidr;
238///
239/// let mut tera: Tera = Tera::default();
240/// tera.register_function("random_ipv6_cidr", random_ipv6_cidr);
241/// let context: Context = Context::new();
242///
243/// // bound by neither prefix bit values nor the prefix length
244/// let rendered: String = tera
245///     .render_str("{{ random_ipv6_cidr() }}", &context)
246///     .unwrap();
247///
248/// // prefix length bound by start and end lengths
249/// let rendered: String = tera
250///     .render_str(r#"{{ random_ipv6_cidr(length_start=64, length_end=80) }}"#, &context)
251///     .unwrap();
252/// // prefix length bound by start length
253/// let rendered: String = tera
254///     .render_str(r#"{{ random_ipv6_cidr(length_start=64) }}"#, &context)
255///     .unwrap();
256/// // prefix length bound by end length
257/// let rendered: String = tera
258///     .render_str(r#"{{ random_ipv6_cidr(length_end=80) }}"#, &context)
259///     .unwrap();
260///
261/// // prefix bits bound by a start address and end address
262/// let rendered: String = tera
263///     .render_str(r#"{{ random_ipv6_cidr(addr_start="fc00::", addr_end="fd00::") }}"#, &context)
264///     .unwrap();
265/// // prefix bits bound by a start address
266/// let rendered: String = tera
267///     .render_str(r#"{{ random_ipv6_cidr(start="fc00::") }}"#, &context )
268///     .unwrap();
269/// // prefix bits bound by an end address
270/// let rendered: String = tera
271///     .render_str(r#"{{ random_ipv6_cidr(addr_end="fd00::") }}"#, &context)
272///     .unwrap();
273///
274/// // prefix length bound by both prefix bit values and length
275/// let rendered: String = tera
276///     .render_str(
277///         r#"{{ random_ipv6_cidr(
278///                   addr_start="fc00::",
279///                   addr_end="fd00::",
280///                   length_start=64,
281///                   length_end=80
282///               ) }}"#,
283///         &context
284///     )
285///     .unwrap();
286/// ```
287pub fn random_ipv6_cidr(args: &HashMap<String, Value>) -> Result<Value> {
288    let addr_start_opt: Option<u128> =
289        parse_arg(args, "addr_start")?.map(|addr_start: Ipv6Addr| addr_start.into());
290    let addr_end_opt: Option<u128> =
291        parse_arg(args, "addr_end")?.map(|addr_end: Ipv6Addr| addr_end.into());
292
293    let random_addr: u128 = gen_value_in_range(addr_start_opt, addr_end_opt, u128::MIN, u128::MAX);
294
295    let length_start: u32 =
296        parse_cidr_prefix_length_and_check_bounds(args, "length_start", 0u32, u128::BITS)?
297            .unwrap_or(0u32);
298    let length_end: u32 =
299        parse_cidr_prefix_length_and_check_bounds(args, "length_end", 0u32, u128::BITS)?
300            .unwrap_or(u128::BITS);
301
302    let random_prefix_length: u32 = rng().random_range(length_start..=length_end);
303    let bits_to_shift: u32 = u128::BITS - random_prefix_length;
304
305    let random_prefix: u128 = match bits_to_shift {
306        u128::BITS => 0u128,
307        bits_to_shift => random_addr >> bits_to_shift << bits_to_shift,
308    };
309    let random_prefix: Ipv6Addr = random_prefix.into();
310
311    let random_cidr: String = format!("{}/{}", random_prefix.to_string(), random_prefix_length);
312    let json_value: Value = to_value(random_cidr)?;
313    Ok(json_value)
314}
315
316fn parse_cidr_prefix_length_and_check_bounds(
317    args: &HashMap<String, Value>,
318    parameter: &'static str,
319    start_bound: u32,
320    end_bound: u32,
321) -> tera::Result<Option<u32>> {
322    parse_arg(args, parameter)?
323        .map(|length: u32| {
324            if length < start_bound || length > end_bound {
325                Err(cidr_prefix_length_out_of_bounds(
326                    length,
327                    start_bound,
328                    end_bound,
329                ))
330            } else {
331                Ok(length)
332            }
333        })
334        .transpose()
335}
336
337#[cfg(test)]
338mod tests {
339    use crate::common::tests::{test_tera_rand_function, test_tera_rand_function_returns_error};
340    use crate::net::*;
341    use tracing_test::traced_test;
342
343    // ipv4 address
344    #[test]
345    #[traced_test]
346    fn test_random_ipv4() {
347        test_tera_rand_function(
348            random_ipv4,
349            "random_ipv4",
350            r#"{ "some_field": "{{ random_ipv4() }}" }"#,
351            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+" }"#,
352        );
353    }
354
355    #[test]
356    #[traced_test]
357    fn test_random_ipv4_with_both_start_and_end() {
358        test_tera_rand_function(
359            random_ipv4,
360            "random_ipv4",
361            r#"{ "some_field": "{{ random_ipv4(start="127.0.0.1", end="127.0.0.3") }}" }"#,
362            r#"\{ "some_field": "(127\.0\.0\.1|127\.0\.0\.2|127\.0\.0\.3)" }"#,
363        );
364    }
365
366    #[test]
367    #[traced_test]
368    fn test_random_ipv4_near_max() {
369        test_tera_rand_function(
370            random_ipv4,
371            "random_ipv4",
372            r#"{ "some_field": "{{ random_ipv4(start="255.255.255.253", end="255.255.255.255") }}" }"#,
373            r#"\{ "some_field": "(255\.255\.255\.253|255\.255\.255\.254|255\.255\.255\.255)" }"#,
374        );
375    }
376
377    #[test]
378    #[traced_test]
379    fn test_random_ipv4_near_min() {
380        test_tera_rand_function(
381            random_ipv4,
382            "random_ipv4",
383            r#"{ "some_field": "{{ random_ipv4(start="0.0.0.0", end="0.0.0.2") }}" }"#,
384            r#"\{ "some_field": "(0\.0\.0\.0|0\.0\.0\.1|0\.0\.0\.2)" }"#,
385        );
386    }
387
388    #[test]
389    #[traced_test]
390    fn test_random_ipv4_with_start_only() {
391        test_tera_rand_function(
392            random_ipv4,
393            "random_ipv4",
394            r#"{ "some_field": "{{ random_ipv4(start="255.255.255.253") }}" }"#,
395            r#"\{ "some_field": "(255\.255\.255\.253|255\.255\.255\.254|255\.255\.255\.255)" }"#,
396        );
397    }
398
399    #[test]
400    #[traced_test]
401    fn test_random_ipv4_with_end_only() {
402        test_tera_rand_function(
403            random_ipv4,
404            "random_ipv4",
405            r#"{ "some_field": "{{ random_ipv4(end="0.0.0.2") }}" }"#,
406            r#"\{ "some_field": "(0\.0\.0\.0|0\.0\.0\.1|0\.0\.0\.2)" }"#,
407        );
408    }
409
410    // ipv6 address
411    #[test]
412    #[traced_test]
413    fn test_random_ipv6() {
414        test_tera_rand_function(
415            random_ipv6,
416            "random_ipv6",
417            r#"{ "some_field": "{{ random_ipv6() }}" }"#,
418            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}" }"#,
419        );
420    }
421
422    #[test]
423    #[traced_test]
424    fn test_random_ipv6_with_both_start_and_end() {
425        test_tera_rand_function(
426            random_ipv6,
427            "random_ipv6",
428            r#"{ "some_field": "{{ random_ipv6(start="fe80::", end="fe80::2") }}" }"#,
429            r#"\{ "some_field": "(fe80::|fe80::1|fe80::2)" }"#,
430        );
431    }
432
433    #[test]
434    #[traced_test]
435    fn test_random_ipv6_near_max() {
436        test_tera_rand_function(
437            random_ipv6,
438            "random_ipv6",
439            r#"{ "some_field": "{{ random_ipv6(start="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd", end="ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff") }}" }"#,
440            r#"\{ "some_field": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff(d|e|f)" }"#,
441        );
442    }
443
444    #[test]
445    #[traced_test]
446    fn test_random_ipv6_near_min() {
447        test_tera_rand_function(
448            random_ipv6,
449            "random_ipv6",
450            r#"{ "some_field": "{{ random_ipv6(start="::", end="::2") }}" }"#,
451            r#"\{ "some_field": "(::|::1|::2)" }"#,
452        );
453    }
454
455    #[test]
456    #[traced_test]
457    fn test_random_ipv6_with_start_only() {
458        test_tera_rand_function(
459            random_ipv6,
460            "random_ipv6",
461            r#"{ "some_field": "{{ random_ipv6(start="ffff:ffff:ffff:ffff:ffff:ffff:ffff:fffd") }}" }"#,
462            r#"\{ "some_field": "ffff:ffff:ffff:ffff:ffff:ffff:ffff:fff(d|e|f)" }"#,
463        );
464    }
465
466    #[test]
467    #[traced_test]
468    fn test_random_ipv6_with_end_only() {
469        test_tera_rand_function(
470            random_ipv6,
471            "random_ipv6",
472            r#"{ "some_field": "{{ random_ipv6(end="::2") }}" }"#,
473            r#"\{ "some_field": "(::|::1|::2)" }"#,
474        );
475    }
476
477    // ipv4 cidr
478    #[test]
479    #[traced_test]
480    fn test_random_ipv4_cidr() {
481        test_tera_rand_function(
482            random_ipv4_cidr,
483            "random_ipv4_cidr",
484            r#"{ "some_field": "{{ random_ipv4_cidr() }}" }"#,
485            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/\d+" }"#,
486        );
487    }
488
489    #[test]
490    #[traced_test]
491    fn test_random_ipv4_cidr_with_prefix_length_start_and_end() {
492        test_tera_rand_function(
493            random_ipv4_cidr,
494            "random_ipv4_cidr",
495            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=28, length_end=30) }}" }"#,
496            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(28|29|30)" }"#,
497        );
498    }
499
500    #[test]
501    #[traced_test]
502    fn test_random_ipv4_cidr_with_prefix_length_start() {
503        test_tera_rand_function(
504            random_ipv4_cidr,
505            "random_ipv4_cidr",
506            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=30) }}" }"#,
507            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(30|31|32)" }"#,
508        );
509    }
510
511    #[test]
512    #[traced_test]
513    fn test_random_ipv4_cidr_with_prefix_length_end() {
514        test_tera_rand_function(
515            random_ipv4_cidr,
516            "random_ipv4_cidr",
517            r#"{ "some_field": "{{ random_ipv4_cidr(length_end=2) }}" }"#,
518            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/(0|1|2)" }"#,
519        );
520    }
521
522    #[test]
523    #[traced_test]
524    fn test_random_ipv4_cidr_with_32_bit_prefix() {
525        test_tera_rand_function(
526            random_ipv4_cidr,
527            "random_ipv4_cidr",
528            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=32, length_end=32) }}" }"#,
529            r#"\{ "some_field": "\d+\.\d+\.\d+\.\d+/32" }"#,
530        );
531    }
532
533    #[test]
534    #[traced_test]
535    fn test_random_ipv4_cidr_with_0_bit_prefix() {
536        test_tera_rand_function(
537            random_ipv4_cidr,
538            "random_ipv4_cidr",
539            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=0, length_end=0) }}" }"#,
540            r#"\{ "some_field": "0\.0\.0\.0/0" }"#,
541        );
542    }
543
544    #[test]
545    #[traced_test]
546    fn test_random_ipv4_cidr_with_too_large_prefix_length_returns_error() {
547        test_tera_rand_function_returns_error(
548            random_ipv4_cidr,
549            "random_ipv4_cidr",
550            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=0, length_end=33) }}" }"#,
551        );
552    }
553
554    #[test]
555    #[traced_test]
556    fn test_random_ipv4_cidr_with_too_small_prefix_length_returns_error() {
557        test_tera_rand_function_returns_error(
558            random_ipv4_cidr,
559            "random_ipv4_cidr",
560            r#"{ "some_field": "{{ random_ipv4_cidr(length_start=-1, length_end=16) }}" }"#,
561        );
562    }
563
564    // ipv6 cidr
565    #[test]
566    #[traced_test]
567    fn test_random_ipv6_cidr() {
568        test_tera_rand_function(
569            random_ipv6_cidr,
570            "random_ipv6_cidr",
571            r#"{ "some_field": "{{ random_ipv6_cidr() }}" }"#,
572            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/\d+" }"#,
573        );
574    }
575
576    #[test]
577    #[traced_test]
578    fn test_random_ipv6_cidr_with_prefix_length_start_and_end() {
579        test_tera_rand_function(
580            random_ipv6_cidr,
581            "random_ipv6_cidr",
582            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=86, length_end=88) }}" }"#,
583            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(86|87|88)" }"#,
584        );
585    }
586
587    #[test]
588    #[traced_test]
589    fn test_random_ipv6_cidr_with_prefix_length_start() {
590        test_tera_rand_function(
591            random_ipv6_cidr,
592            "random_ipv6_cidr",
593            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=126) }}" }"#,
594            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(126|127|128)" }"#,
595        );
596    }
597
598    #[test]
599    #[traced_test]
600    fn test_random_ipv6_cidr_with_prefix_length_end() {
601        test_tera_rand_function(
602            random_ipv6_cidr,
603            "random_ipv6_cidr",
604            r#"{ "some_field": "{{ random_ipv6_cidr(length_end=2) }}" }"#,
605            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/(0|1|2)" }"#,
606        );
607    }
608
609    #[test]
610    #[traced_test]
611    fn test_random_ipv6_cidr_with_128_bit_prefix() {
612        test_tera_rand_function(
613            random_ipv6_cidr,
614            "random_ipv6_cidr",
615            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=128, length_end=128) }}" }"#,
616            r#"\{ "some_field": "([\da-f]{0,4}:){1,7}[\da-f]{0,4}/128" }"#,
617        );
618    }
619
620    #[test]
621    #[traced_test]
622    fn test_random_ipv6_cidr_with_0_bit_prefix() {
623        test_tera_rand_function(
624            random_ipv6_cidr,
625            "random_ipv6_cidr",
626            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=0, length_end=0) }}" }"#,
627            r#"\{ "some_field": "::/0" }"#,
628        );
629    }
630
631    #[test]
632    #[traced_test]
633    fn test_random_ipv6_cidr_with_too_large_prefix_length_returns_error() {
634        test_tera_rand_function_returns_error(
635            random_ipv6_cidr,
636            "random_ipv6_cidr",
637            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=0, length_end=129) }}" }"#,
638        );
639    }
640
641    #[test]
642    #[traced_test]
643    fn test_random_ipv6_cidr_with_too_small_prefix_length_returns_error() {
644        test_tera_rand_function_returns_error(
645            random_ipv6_cidr,
646            "random_ipv6_cidr",
647            r#"{ "some_field": "{{ random_ipv6_cidr(length_start=-1, length_end=16) }}" }"#,
648        );
649    }
650}