lava_api_mock/
devicetypes.rs

1use strum::{Display, EnumString};
2
3use boulder::{BuildableWithPersianRug, GeneratableWithPersianRug};
4use boulder::{Cycle, Inc, Pattern, Some as GSome};
5use django_query::{
6    filtering::FilterableWithPersianRug, row::IntoRowWithPersianRug,
7    sorting::SortableWithPersianRug,
8};
9use persian_rug::{contextual, Context, Proxy};
10
11/// An alias from the LAVA API
12#[derive(
13    Clone,
14    Debug,
15    FilterableWithPersianRug,
16    SortableWithPersianRug,
17    IntoRowWithPersianRug,
18    BuildableWithPersianRug,
19    GeneratableWithPersianRug,
20)]
21#[boulder(persian_rug(context = C, access(Alias<C>)))]
22#[django(persian_rug(context = C, access(Alias<C>)))]
23#[contextual(C)]
24pub struct Alias<C: Context + 'static> {
25    #[django(exclude)]
26    _marker: core::marker::PhantomData<C>,
27    #[boulder(default="test-alias", generator=Pattern!("test-alias-{}", Inc(1)))]
28    #[django(sort, op(in, contains, icontains, startswith, endswith))]
29    pub name: String,
30}
31
32/// An architecture from the LAVA API
33// FIXME: we implement IntoRowWithPersianRug to get AsForeignKey derived for us
34#[derive(
35    Clone,
36    Debug,
37    FilterableWithPersianRug,
38    IntoRowWithPersianRug,
39    BuildableWithPersianRug,
40    GeneratableWithPersianRug,
41)]
42#[boulder(persian_rug(context = C, access(Architecture<C>)))]
43#[django(persian_rug(context = C, access(Architecture<C>)))]
44#[contextual(C)]
45pub struct Architecture<C: Context + 'static> {
46    #[django(exclude)]
47    _marker: core::marker::PhantomData<C>,
48    #[boulder(default="test-arch", generator=Pattern!("test-arch-{}", Inc(1)))]
49    #[django(op(in, contains, icontains, startswith, endswith))]
50    pub name: String,
51}
52
53/// A bit width from the LAVA API
54// FIXME: we implement IntoRowWithPersianRug to get AsForeignKey derived for us
55#[derive(
56    Clone,
57    Debug,
58    FilterableWithPersianRug,
59    IntoRowWithPersianRug,
60    BuildableWithPersianRug,
61    GeneratableWithPersianRug,
62)]
63#[boulder(persian_rug(context = C, access(BitWidth<C>)))]
64#[django(persian_rug(context = C, access(BitWidth<C>)))]
65#[contextual(C)]
66pub struct BitWidth<C: Context + 'static> {
67    #[django(exclude)]
68    _marker: core::marker::PhantomData<C>,
69    #[boulder(default=64u64, generator=Cycle::new(vec![32u64,64u64].into_iter()))]
70    #[django(op(in))]
71    pub width: u64,
72}
73
74/// A core from the LAVA API
75// FIXME: we implement IntoRowWithPersianRug to get AsForeignKey derived for us
76#[derive(
77    Clone,
78    Debug,
79    FilterableWithPersianRug,
80    IntoRowWithPersianRug,
81    BuildableWithPersianRug,
82    GeneratableWithPersianRug,
83)]
84#[boulder(persian_rug(context = C, access(Core<C>)))]
85#[django(persian_rug(context = C, access(Core<C>)))]
86#[contextual(C)]
87pub struct Core<C: Context + 'static> {
88    #[django(exclude)]
89    _marker: core::marker::PhantomData<C>,
90    #[boulder(default="test-core", generator=Pattern!("test-core-{}", Inc(1)))]
91    #[django(op(in, contains, icontains, startswith, endswith))]
92    pub name: String,
93}
94
95/// A processor family from the LAVA API
96// FIXME: we implement IntoRowWithPersianRug to get AsForeignKey derived for us
97#[derive(
98    Clone,
99    Debug,
100    FilterableWithPersianRug,
101    IntoRowWithPersianRug,
102    BuildableWithPersianRug,
103    GeneratableWithPersianRug,
104)]
105#[boulder(persian_rug(context = C, access(ProcessorFamily<C>)))]
106#[django(persian_rug(context = C, access(ProcessorFamily<C>)))]
107#[contextual(C)]
108pub struct ProcessorFamily<C: Context + 'static> {
109    #[django(exclude)]
110    _marker: core::marker::PhantomData<C>,
111    #[boulder(default="test-processor-family", generator=Pattern!("test-processor-family-{}", Inc(1)))]
112    #[django(op(in, contains, icontains, startswith, endswith))]
113    pub name: String,
114}
115
116/// A device type from the LAVA API
117// FIXME: Verify: the docs say this is not sortable
118// FIXME: Only implementing sortable so that we can do a nested sort on the name key
119#[derive(
120    Clone,
121    Debug,
122    FilterableWithPersianRug,
123    SortableWithPersianRug,
124    IntoRowWithPersianRug,
125    BuildableWithPersianRug,
126    GeneratableWithPersianRug,
127)]
128#[boulder(
129    persian_rug(
130        context = C,
131        access(
132            DeviceType<C>,
133            Alias<C>,
134            Architecture<C>,
135            BitWidth<C>,
136            Core<C>,
137            ProcessorFamily<C>
138        )
139    )
140)]
141#[django(
142    persian_rug(
143        context = C,
144        access(
145            DeviceType<C>,
146            Alias<C>,
147            Architecture<C>,
148            BitWidth<C>,
149            Core<C>,
150            ProcessorFamily<C>
151        )
152    )
153)]
154#[contextual(C)]
155pub struct DeviceType<C: Context + 'static> {
156    #[boulder(default="test-device-type", generator=Pattern!("test-device-type-{}", Inc(0)))]
157    #[django(sort, op(in, contains, icontains, startswith, endswith))]
158    pub name: String,
159    #[boulder(buildable_with_persian_rug, generatable_with_persian_rug)]
160    #[django(traverse, foreign_key = "name")]
161    pub architecture: Option<Proxy<Architecture<C>>>,
162    #[boulder(buildable_with_persian_rug, generatable_with_persian_rug)]
163    #[django(traverse, foreign_key = "name")]
164    pub processor: Option<Proxy<ProcessorFamily<C>>>,
165    #[boulder(default=Some("test-cpu-model".to_string()), generator=GSome(Pattern!("test-cpu-model-{}", Inc(0))))]
166    #[django(op(in, contains, icontains, startswith, endswith))]
167    pub cpu_model: Option<String>,
168    #[boulder(generatable_with_persian_rug, sequence = 2usize)]
169    #[django(traverse, foreign_key = "name")]
170    pub aliases: Vec<Proxy<Alias<C>>>,
171    #[boulder(buildable_with_persian_rug, generatable_with_persian_rug)]
172    #[django(traverse, foreign_key = "width")]
173    pub bits: Option<Proxy<BitWidth<C>>>,
174    #[boulder(generatable_with_persian_rug,sequence=4usize,sequence_generator=Cycle::new(vec![4usize,8usize,16usize].into_iter()))]
175    #[django(traverse, foreign_key = "name")]
176    pub cores: Vec<Proxy<Core<C>>>,
177    #[boulder(default=Some(4), generator=GSome(Cycle::new(vec![4,8,16].into_iter())))]
178    #[django(op(in))]
179    pub core_count: Option<u64>,
180    #[boulder(default=Some("Example device type description.".to_string()))]
181    #[django(op(in, contains, icontains, startswith, endswith))]
182    pub description: Option<String>,
183    #[boulder(default = 10)]
184    #[django(op(in))]
185    pub health_frequency: i64,
186    #[boulder(default = false)]
187    #[django(op(in))]
188    pub disable_health_check: bool,
189    #[boulder(default=HealthDenominator::Hours)]
190    pub health_denominator: HealthDenominator,
191    #[boulder(default = true)]
192    #[django(op(in))]
193    pub display: bool,
194}
195
196/// The units for the [`health_frequency`](Device::health_frequency)
197/// field in [`Device`] from the LAVA API
198#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Display, EnumString)]
199#[strum(serialize_all = "snake_case")]
200pub enum HealthDenominator {
201    Hours,
202    Jobs,
203}
204
205impl django_query::filtering::ops::Scalar for HealthDenominator {}
206impl django_query::row::StringCellValue for HealthDenominator {}
207
208#[cfg(test)]
209mod test {
210    use super::*;
211
212    use crate::state::{SharedState, State};
213
214    use boulder::{GeneratorWithPersianRugIterator, Repeat};
215    use test_log::test;
216
217    #[test(tokio::test)]
218    async fn test_output() {
219        let mut p = SharedState::new();
220        {
221            let m = p.mutate();
222
223            let gen = Proxy::<DeviceType<State>>::generator()
224                .architecture(|| None)
225                .processor(|| None)
226                .cpu_model(Repeat::new([None, Some("".to_string())]))
227                .aliases(Vec::new)
228                .bits(|| None)
229                .cores(Vec::new)
230                .core_count(|| None)
231                .description(Repeat::new([None, Some("".to_string())]))
232                .health_frequency(|| 10);
233
234            let _ = GeneratorWithPersianRugIterator::new(gen, m)
235                .take(5)
236                .collect::<Vec<_>>();
237        }
238
239        let server = wiremock::MockServer::start().await;
240
241        let ep = p.endpoint::<DeviceType<_>>(Some(&server.uri()), None);
242
243        wiremock::Mock::given(wiremock::matchers::method("GET"))
244            .and(wiremock::matchers::path("/api/v0.2/devicetypes/"))
245            .respond_with(ep)
246            .mount(&server)
247            .await;
248
249        let body: serde_json::Value = reqwest::get(&format!(
250            "{}/api/v0.2/devicetypes/?limit=2&offset=2",
251            server.uri()
252        ))
253        .await
254        .expect("error getting device types")
255        .json()
256        .await
257        .expect("error parsing device types");
258
259        let next = format!("{}/api/v0.2/devicetypes/?limit=2&offset=4", server.uri());
260        let prev = format!("{}/api/v0.2/devicetypes/?limit=2", server.uri());
261
262        assert_eq!(
263            body,
264            serde_json::json! {
265                {
266                    "count": 5,
267                    "next": next,
268                    "previous": prev,
269                    "results": [
270                        {
271                            "name": "test-device-type-2",
272                            "architecture": null,
273                            "processor": null,
274                            "cpu_model": null,
275                            "aliases": [],
276                            "bits": null,
277                            "cores": [],
278                            "core_count": null,
279                            "description": null,
280                            "health_frequency": 10,
281                            "disable_health_check": false,
282                            "health_denominator": "hours",
283                            "display": true
284                        },
285                        {
286                            "name": "test-device-type-3",
287                            "architecture": null,
288                            "processor": null,
289                            "cpu_model": "",
290                            "aliases": [],
291                            "bits": null,
292                            "cores": [],
293                            "core_count": null,
294                            "description": "",
295                            "health_frequency": 10,
296                            "disable_health_check": false,
297                            "health_denominator": "hours",
298                            "display": true
299                        }
300                    ]
301                }
302            }
303        );
304    }
305}