1
// Copyright 2020, Collabora Ltd.
2
// SPDX-License-Identifier: MIT OR Apache-2.0
3

            
4
use std::fs::File;
5
use std::io::prelude::*;
6
use std::{path::PathBuf, time::Duration};
7

            
8
use bytes::Bytes;
9
use futures::prelude::*;
10
use hawkbit::ddi::{
11
    Client, ConfirmationResponse, Error, Execution, Finished, MaintenanceWindow, Mode, Type,
12
};
13
use httpmock::Method::GET;
14
use httpmock::{Then, When};
15
use serde::Serialize;
16
use serde_json::json;
17
use tempdir::TempDir;
18

            
19
use hawkbit_mock::ddi::{
20
    ChunkProtocol, Deployment, DeploymentBuilder, Server, ServerBuilder, Target,
21
};
22

            
23
26
fn init() {
24
26
    let _ = env_logger::builder().is_test(true).try_init();
25
26
}
26

            
27
30
fn add_target(server: &Server, name: &str) -> (Client, Target) {
28
30
    let target = server.add_target(name);
29

            
30
30
    let client = Client::new(
31
30
        &server.base_url(),
32
30
        &server.tenant,
33
30
        &target.name,
34
30
        target.client_auth.clone(),
35
30
        None,
36
30
        None,
37
30
        None,
38
    )
39
30
    .expect("DDI creation failed");
40

            
41
30
    (client, target)
42
30
}
43

            
44
#[tokio::test]
45
3
async fn poll() {
46
2
    init();
47

            
48
2
    let server = ServerBuilder::default().tenant("my-tenant").build();
49
2
    let (client, target) = add_target(&server, "Target1");
50

            
51
2
    assert_eq!(target.poll_hits(), 0);
52

            
53
    // Try polling twice
54
5
    for i in 0..2 {
55
5
        let reply = client.poll().await.expect("poll failed");
56
5
        assert_eq!(reply.polling_sleep().unwrap(), Duration::from_secs(60));
57
5
        assert!(reply.config_data_request().is_none());
58
5
        assert!(reply.update().is_none());
59
5
        assert_eq!(target.poll_hits(), i + 1);
60
2
    }
61
2
}
62

            
63
#[tokio::test]
64
3
async fn upload_config() {
65
2
    init();
66

            
67
2
    let server = ServerBuilder::default().build();
68
2
    let (client, target) = add_target(&server, "Target1");
69

            
70
2
    let expected_config_data = json!({
71
2
        "mode" : "merge",
72
2
        "data" : {
73
2
            "awesome" : true,
74
        },
75
2
        "status" : {
76
2
            "result" : {
77
2
            "finished" : "success"
78
            },
79
2
            "execution" : "closed",
80
2
            "details" : [ "Some stuffs" ]
81
        }
82
    });
83
2
    target.request_config(expected_config_data);
84

            
85
2
    let reply = client.poll().await.expect("poll failed");
86
2
    let config_data_req = reply
87
2
        .config_data_request()
88
2
        .expect("missing config data request");
89
2
    assert!(reply.update().is_none());
90

            
91
    #[derive(Serialize)]
92
    struct Config {
93
        awesome: bool,
94
    }
95

            
96
2
    let config = Config { awesome: true };
97

            
98
2
    config_data_req
99
2
        .upload(
100
2
            Execution::Closed,
101
2
            Finished::Success,
102
2
            Some(Mode::Merge),
103
2
            config,
104
2
            vec!["Some stuffs"],
105
2
        )
106
2
        .await
107
2
        .expect("upload config failed");
108

            
109
2
    assert_eq!(target.poll_hits(), 1);
110
3
    assert_eq!(target.config_data_hits(), 1);
111
2
}
112

            
113
24
fn artifact_path() -> PathBuf {
114
24
    let mut test_artifact = PathBuf::new();
115
24
    test_artifact.push("tests");
116
24
    test_artifact.push("data");
117
24
    test_artifact.push("test.txt");
118

            
119
24
    test_artifact
120
24
}
121

            
122
14
fn get_deployment(needs_confirmation: bool, valid_checksums: bool) -> Deployment {
123
14
    let test_artifact = artifact_path();
124

            
125
14
    let artifacts = if valid_checksums {
126
12
        vec![(
127
12
            test_artifact,
128
12
            "5eb63bbbe01eeed093cb22bb8f5acdc3",
129
12
            "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
130
12
            "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
131
12
        )]
132
    } else {
133
2
        vec![(test_artifact, "badger", "badger", "badger")]
134
    };
135

            
136
14
    DeploymentBuilder::new("10", Type::Forced, Type::Attempt)
137
14
        .confirmation_required(needs_confirmation)
138
14
        .maintenance_window(MaintenanceWindow::Available)
139
14
        .chunk(
140
14
            ChunkProtocol::BOTH,
141
14
            "app-both",
142
14
            "1.0",
143
14
            "some-chunk",
144
14
            artifacts.clone(),
145
        )
146
14
        .chunk_with_metadata(
147
14
            ChunkProtocol::HTTP,
148
14
            "app-http",
149
14
            "1.0",
150
14
            "some-chunk",
151
14
            artifacts.clone(),
152
14
            vec![("key1".to_string(), "value1".to_string())],
153
        )
154
14
        .chunk_with_metadata(
155
14
            ChunkProtocol::HTTPS,
156
14
            "app-https",
157
14
            "1.0",
158
14
            "some-chunk",
159
14
            artifacts,
160
14
            vec![
161
14
                ("key2".to_string(), "value2".to_string()),
162
14
                ("key3".to_string(), "value3".to_string()),
163
            ],
164
        )
165
14
        .build()
166
14
}
167

            
168
#[tokio::test]
169
3
async fn deployment() {
170
2
    init();
171

            
172
2
    let server = ServerBuilder::default().build();
173
2
    let (client, target) = add_target(&server, "Target1");
174
2
    target.push_deployment(get_deployment(false, true));
175

            
176
2
    let reply = client.poll().await.expect("poll failed");
177
2
    assert!(reply.config_data_request().is_none());
178
2
    assert_eq!(target.deployment_hits(), 0);
179

            
180
2
    let update = reply.update().expect("missing update");
181
2
    let update = update.fetch().await.expect("failed to fetch update info");
182
2
    assert_eq!(target.deployment_hits(), 1);
183
2
    assert_eq!(update.action_id(), "10");
184
2
    assert_eq!(update.download_type(), Type::Forced);
185
2
    assert_eq!(update.update_type(), Type::Attempt);
186
2
    assert_eq!(
187
2
        update.maintenance_window(),
188
        Some(MaintenanceWindow::Available)
189
    );
190
2
    assert_eq!(update.chunks().count(), 3);
191

            
192
2
    let mut chunks = update.chunks();
193
7
    for p in &[
194
3
        ChunkProtocol::BOTH,
195
3
        ChunkProtocol::HTTP,
196
3
        ChunkProtocol::HTTPS,
197
3
    ] {
198
2
        // Check chunk
199
7
        let chunk = chunks.next().unwrap();
200
7
        let name = match p {
201
3
            ChunkProtocol::BOTH => "app-both",
202
3
            ChunkProtocol::HTTP => "app-http",
203
3
            ChunkProtocol::HTTPS => "app-https",
204
2
        };
205
7
        assert_eq!(chunk.part(), name);
206
7
        assert_eq!(chunk.version(), "1.0");
207
7
        assert_eq!(chunk.name(), "some-chunk");
208
7
        assert_eq!(chunk.artifacts().count(), 1);
209
2

            
210
7
        let art = chunk.artifacts().next().unwrap();
211
7
        assert_eq!(art.filename(), "test.txt");
212
7
        assert_eq!(art.size(), 11);
213
2

            
214
7
        let out_dir = TempDir::new("test-hawkbitrs").expect("Failed to create temp dir");
215
7
        let artifacts = chunk
216
7
            .download(out_dir.path())
217
7
            .await
218
7
            .expect("Failed to download update");
219
2

            
220
2
        // Check artifact
221
7
        assert_eq!(artifacts.len(), 1);
222
7
        let p = artifacts[0].file();
223
7
        assert_eq!(p.file_name().unwrap(), "test.txt");
224
7
        assert!(p.exists());
225
2

            
226
2
        #[cfg(feature = "hash-md5")]
227
7
        artifacts[0].check_md5().await.expect("invalid md5");
228
2
        #[cfg(feature = "hash-sha1")]
229
7
        artifacts[0].check_sha1().await.expect("invalid sha1");
230
2
        #[cfg(feature = "hash-sha256")]
231
7
        artifacts[0].check_sha256().await.expect("invalid sha256");
232
2
    }
233
2
}
234

            
235
#[tokio::test]
236
3
async fn resume_download() {
237
2
    init();
238

            
239
2
    let server = ServerBuilder::default().build();
240
2
    let (client, target) = add_target(&server, "Target1");
241

            
242
2
    let test_artifact = artifact_path();
243

            
244
2
    let artifacts = vec![(
245
2
        test_artifact,
246
2
        "5eb63bbbe01eeed093cb22bb8f5acdc3",
247
2
        "2aae6c35c94fcfb415dbe95f408b9ce91ee846ed",
248
2
        "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9",
249
2
    )];
250

            
251
2
    let deployment = DeploymentBuilder::new("10", Type::Forced, Type::Attempt)
252
2
        .confirmation_required(false)
253
2
        .maintenance_window(MaintenanceWindow::Available)
254
2
        .chunk_with_mock(
255
2
            ChunkProtocol::BOTH,
256
2
            "app-both",
257
2
            "1.0",
258
2
            "some-chunk",
259
2
            artifacts.clone(),
260
            // Modify the artifact to be a partial download
261
2
            Box::new(|when: When, then: Then| {
262
2
                let when = when.method(GET).path("/download/test.txt");
263

            
264
2
                when.header("Range", "bytes=5-");
265

            
266
2
                then.status(206).body(" world");
267
2
            }),
268
        )
269
2
        .build();
270

            
271
2
    target.push_deployment(deployment);
272

            
273
2
    let reply = client.poll().await.expect("poll failed");
274
2
    assert!(reply.config_data_request().is_none());
275
2
    assert_eq!(target.deployment_hits(), 0);
276

            
277
2
    let update = reply.update().expect("missing update");
278
2
    let update = update.fetch().await.expect("failed to fetch update info");
279
2
    assert_eq!(target.deployment_hits(), 1);
280
2
    assert_eq!(update.action_id(), "10");
281
2
    assert_eq!(update.download_type(), Type::Forced);
282
2
    assert_eq!(update.update_type(), Type::Attempt);
283
2
    assert_eq!(
284
2
        update.maintenance_window(),
285
        Some(MaintenanceWindow::Available)
286
    );
287

            
288
2
    let mut chunks = update.chunks();
289
2
    let chunk = chunks.next().unwrap();
290
2
    assert_eq!(chunk.name(), "some-chunk");
291
2
    assert_eq!(chunk.artifacts().count(), 1);
292

            
293
2
    let art = chunk.artifacts().next().unwrap();
294
2
    assert_eq!(art.filename(), "test.txt");
295
2
    assert_eq!(art.size(), 11);
296

            
297
2
    let out_dir = TempDir::new("test-hawkbitrs").expect("Failed to create temp dir");
298
2
    let mut partial_download_file = out_dir.path().to_path_buf();
299
2
    partial_download_file.push("some-chunk");
300
2
    partial_download_file.push("test.txt.part");
301
    // Write a wrong prefix to the part file to be able to test whether
302
    // the download is resumed or simply overwritten.
303
2
    tokio::fs::create_dir_all(partial_download_file.parent().unwrap())
304
2
        .await
305
2
        .unwrap();
306
2
    tokio::fs::write(partial_download_file, b"HELLO")
307
2
        .await
308
2
        .unwrap();
309
2
    let artifacts = chunk
310
2
        .download(out_dir.path())
311
2
        .await
312
2
        .expect("Failed to download update");
313

            
314
    // Check artifact
315
2
    assert_eq!(artifacts.len(), 1);
316
2
    let p = artifacts[0].file();
317
2
    assert_eq!(p.file_name().unwrap(), "test.txt");
318
2
    assert!(p.exists());
319
3
    assert_eq!(tokio::fs::read_to_string(p).await.unwrap(), "HELLO world");
320
2
}
321

            
322
#[tokio::test]
323
3
async fn send_deployment_feedback() {
324
2
    init();
325

            
326
2
    let server = ServerBuilder::default().build();
327
2
    let deploy = get_deployment(false, true);
328
2
    let deploy_id = deploy.id.clone();
329
2
    let (client, target) = add_target(&server, "Target1");
330
2
    target.push_deployment(deploy);
331

            
332
2
    let reply = client.poll().await.expect("poll failed");
333
2
    let update = reply.update().expect("missing update");
334
2
    let update = update.fetch().await.expect("failed to fetch update info");
335

            
336
    // Send feedback without progress
337
2
    let mut mock = target.expect_deployment_feedback(
338
2
        &deploy_id,
339
2
        Execution::Proceeding,
340
2
        Finished::None,
341
2
        None,
342
2
        vec!["Downloading"],
343
    );
344
2
    assert_eq!(mock.calls(), 0);
345

            
346
2
    update
347
2
        .send_feedback(Execution::Proceeding, Finished::None, vec!["Downloading"])
348
2
        .await
349
2
        .expect("Failed to send feedback");
350
2
    assert_eq!(mock.calls(), 1);
351
2
    mock.delete();
352

            
353
    // Send feedback with progress
354
2
    let mut mock = target.expect_deployment_feedback(
355
2
        &deploy_id,
356
2
        Execution::Closed,
357
2
        Finished::Success,
358
2
        Some(json!({"awesome": true})),
359
2
        vec!["Done"],
360
    );
361
2
    assert_eq!(mock.calls(), 0);
362

            
363
    #[derive(Serialize)]
364
    struct Progress {
365
        awesome: bool,
366
    }
367
2
    let progress = Progress { awesome: true };
368

            
369
2
    update
370
2
        .send_feedback_with_progress(
371
2
            Execution::Closed,
372
2
            Finished::Success,
373
2
            Some(progress),
374
2
            vec!["Done"],
375
2
        )
376
2
        .await
377
2
        .expect("Failed to send feedback");
378
2
    assert_eq!(mock.calls(), 1);
379
3
    mock.delete();
380
2
}
381

            
382
#[tokio::test]
383
3
async fn confirmation() {
384
2
    init();
385

            
386
2
    let server = ServerBuilder::default().build();
387
2
    let deploy = get_deployment(true, true);
388
2
    let deploy_id = deploy.id.clone();
389
2
    let (client, target) = add_target(&server, "Target1");
390
2
    target.push_deployment(deploy);
391

            
392
2
    let reply = client.poll().await.expect("poll failed");
393
2
    let confirmation = reply
394
2
        .confirmation_base()
395
2
        .expect("missing confirmation request");
396

            
397
    // Decline the confirmation
398
2
    let mut mock = target.expect_confirmation_feedback(
399
2
        &deploy_id,
400
2
        Some(-1),
401
2
        ConfirmationResponse::Denied,
402
2
        vec![],
403
    );
404
2
    assert_eq!(mock.calls(), 0);
405

            
406
2
    confirmation
407
2
        .decline()
408
2
        .await
409
2
        .expect("Failed to send feedback");
410
2
    assert_eq!(mock.calls(), 1);
411
2
    mock.delete();
412

            
413
2
    let reply = client.poll().await.expect("poll failed");
414
2
    let confirmation = reply
415
2
        .confirmation_base()
416
2
        .expect("missing confirmation request");
417
2
    let update_info = confirmation
418
2
        .update_info()
419
2
        .await
420
2
        .expect("failed to fetch update info");
421

            
422
2
    let action_id = update_info.action_id();
423
2
    assert_eq!(action_id, deploy_id);
424

            
425
2
    assert_eq!(
426
2
        update_info.metadata(),
427
2
        confirmation.metadata().await.unwrap()
428
    );
429

            
430
    // Accept the confirmation
431
2
    let mut mock = target.expect_confirmation_feedback(
432
2
        &deploy_id,
433
2
        Some(1),
434
2
        ConfirmationResponse::Confirmed,
435
2
        vec![],
436
    );
437
2
    assert_eq!(mock.calls(), 0);
438

            
439
2
    confirmation
440
2
        .confirm()
441
2
        .await
442
2
        .expect("Failed to send feedback");
443
2
    assert_eq!(mock.calls(), 1);
444
3
    mock.delete();
445
2
}
446

            
447
#[tokio::test]
448
3
async fn confirmation_metadata() {
449
2
    init();
450

            
451
2
    let server = ServerBuilder::default().build();
452
2
    let deploy = get_deployment(true, true);
453
2
    let (client, target) = add_target(&server, "Target1");
454
2
    target.push_deployment(deploy);
455

            
456
2
    let reply = client.poll().await.expect("poll failed");
457
2
    let confirmation = reply
458
2
        .confirmation_base()
459
2
        .expect("missing confirmation request");
460

            
461
2
    let metadata = confirmation
462
2
        .metadata()
463
2
        .await
464
2
        .expect("failed to fetch metadata");
465
2
    assert_eq!(metadata.len(), 3);
466
2
    assert_eq!(metadata[0], ("key1".to_string(), "value1".to_string()));
467
2
    assert_eq!(metadata[1], ("key2".to_string(), "value2".to_string()));
468
3
    assert_eq!(metadata[2], ("key3".to_string(), "value3".to_string()));
469
2
}
470

            
471
#[tokio::test]
472
3
async fn config_then_deploy() {
473
2
    init();
474

            
475
2
    let server = ServerBuilder::default().build();
476
2
    let (client, target) = add_target(&server, "Target1");
477

            
478
2
    let reply = client.poll().await.expect("poll failed");
479
2
    assert!(reply.config_data_request().is_none());
480
2
    assert!(reply.update().is_none());
481

            
482
    // server requests config
483
2
    let expected_config_data = json!({
484
2
        "mode" : "merge",
485
2
        "data" : {
486
2
            "awesome" : true,
487
        },
488
2
        "status" : {
489
2
            "result" : {
490
2
            "finished" : "success"
491
            },
492
2
            "execution" : "closed",
493
2
            "details" : [ "Some stuffs" ]
494
        }
495
    });
496
2
    target.request_config(expected_config_data);
497

            
498
2
    let reply = client.poll().await.expect("poll failed");
499
2
    assert!(reply.config_data_request().is_some());
500
2
    assert!(reply.update().is_none());
501

            
502
    // server pushes an update
503
2
    target.push_deployment(get_deployment(false, true));
504

            
505
2
    let reply = client.poll().await.expect("poll failed");
506
2
    assert!(reply.config_data_request().is_some());
507
3
    assert!(reply.update().is_some());
508
2
}
509

            
510
#[tokio::test]
511
3
async fn download_stream() {
512
2
    init();
513

            
514
2
    let server = ServerBuilder::default().build();
515
2
    let (client, target) = add_target(&server, "Target1");
516

            
517
2
    target.push_deployment(get_deployment(false, true));
518
2
    let reply = client.poll().await.expect("poll failed");
519

            
520
2
    let update = reply.update().expect("missing update");
521
2
    let update = update.fetch().await.expect("failed to fetch update info");
522
2
    let chunk = update.chunks().next().unwrap();
523
2
    let art = chunk.artifacts().next().unwrap();
524

            
525
12
    async fn check_download(mut stream: Box<dyn Stream<Item = Result<Bytes, Error>> + Unpin>) {
526
8
        let mut downloaded: Vec<u8> = Vec::new();
527
16
        while let Some(b) = stream.next().await {
528
8
            downloaded.extend(b.unwrap().as_ref());
529
8
        }
530

            
531
        // Compare downloaded content with the actual file
532
8
        let mut art_file = File::open(&artifact_path()).expect("failed to open artifact");
533
8
        let mut expected = Vec::new();
534
8
        art_file
535
8
            .read_to_end(&mut expected)
536
8
            .expect("failed to read artifact");
537

            
538
8
        assert_eq!(downloaded, expected);
539
8
    }
540

            
541
    // Download artifact using the stream API
542
2
    let stream = art
543
2
        .download_stream()
544
2
        .await
545
2
        .expect("failed to get download stream");
546
2
    check_download(Box::new(stream)).await;
547

            
548
2
    cfg_if::cfg_if! {
549
2
        if #[cfg(feature = "hash-md5")] {
550
3
            let stream = art
551
3
                .download_stream_with_md5_check()
552
3
                .await
553
3
                .expect("failed to get download stream");
554
3
            check_download(Box::new(stream)).await;
555
2
        }
556
2
    }
557
2

            
558
2
    cfg_if::cfg_if! {
559
2
        if #[cfg(feature = "hash-sha1")] {
560
3
            let stream = art
561
3
                .download_stream_with_sha1_check()
562
3
                .await
563
3
                .expect("failed to get download stream");
564
3
            check_download(Box::new(stream)).await;
565
2
        }
566
2
    }
567
2

            
568
2
    cfg_if::cfg_if! {
569
2
        if #[cfg(feature = "hash-sha256")] {
570
3
            let stream = art
571
3
                .download_stream_with_sha256_check()
572
3
                .await
573
3
                .expect("failed to get download stream");
574
3
            check_download(Box::new(stream)).await;
575
2
        }
576
2
    }
577
2
}
578

            
579
#[cfg(feature = "hash-digest")]
580
#[tokio::test]
581
3
async fn wrong_checksums() {
582
    use assert_matches::assert_matches;
583
    use hawkbit::ddi::ChecksumType;
584

            
585
2
    init();
586

            
587
2
    let server = ServerBuilder::default().build();
588
2
    let (client, target) = add_target(&server, "Target1");
589

            
590
2
    target.push_deployment(get_deployment(false, false));
591
2
    let reply = client.poll().await.expect("poll failed");
592

            
593
2
    let update = reply.update().expect("missing update");
594
2
    let update = update.fetch().await.expect("failed to fetch update info");
595
2
    let chunk = update.chunks().next().unwrap();
596
2
    let art = chunk.artifacts().next().unwrap();
597

            
598
2
    let out_dir = TempDir::new("test-hawkbitrs").expect("Failed to create temp dir");
599
2
    let downloaded = art
600
2
        .download(out_dir.path())
601
2
        .await
602
2
        .expect("failed to download artifact");
603

            
604
    #[cfg(feature = "hash-md5")]
605
    assert_matches!(
606
2
        downloaded.check_md5().await,
607
        Err(Error::ChecksumError(ChecksumType::Md5))
608
    );
609
    #[cfg(feature = "hash-sha1")]
610
    assert_matches!(
611
2
        downloaded.check_sha1().await,
612
        Err(Error::ChecksumError(ChecksumType::Sha1))
613
    );
614
    #[cfg(feature = "hash-sha256")]
615
    assert_matches!(
616
2
        downloaded.check_sha256().await,
617
        Err(Error::ChecksumError(ChecksumType::Sha256))
618
    );
619

            
620
2
    cfg_if::cfg_if! {
621
2
        if #[cfg(feature = "hash-md5")] {
622
3
            let stream = art
623
3
                .download_stream_with_md5_check()
624
3
                .await
625
3
                .expect("failed to get download stream");
626
5
            let end = stream.skip_while(|b| future::ready(b.is_ok())).next().await;
627
3
            assert_matches!(end, Some(Err(Error::ChecksumError(ChecksumType::Md5))));
628
2
        }
629
2
    }
630
2

            
631
2
    cfg_if::cfg_if! {
632
2
        if #[cfg(feature = "hash-sha1")] {
633
3
            let stream = art
634
3
                .download_stream_with_sha1_check()
635
3
                .await
636
3
                .expect("failed to get download stream");
637
5
            let end = stream.skip_while(|b| future::ready(b.is_ok())).next().await;
638
3
            assert_matches!(end, Some(Err(Error::ChecksumError(ChecksumType::Sha1))));
639
2
        }
640
2
    }
641
2

            
642
2
    cfg_if::cfg_if! {
643
2
        if #[cfg(feature = "hash-sha256")] {
644
3
            let stream = art
645
3
                .download_stream_with_sha256_check()
646
3
                .await
647
3
                .expect("failed to get download stream");
648
5
            let end = stream.skip_while(|b| future::ready(b.is_ok())).next().await;
649
3
            assert_matches!(end, Some(Err(Error::ChecksumError(ChecksumType::Sha256))));
650
2
        }
651
2
    }
652
2
}
653

            
654
#[tokio::test]
655
3
async fn cancel_action() {
656
2
    init();
657

            
658
2
    let server = ServerBuilder::default().build();
659
2
    let (client, target) = add_target(&server, "Target1");
660
2
    target.cancel_action("10");
661

            
662
2
    let reply = client.poll().await.expect("poll failed");
663
2
    assert!(reply.config_data_request().is_none());
664
2
    assert!(reply.update().is_none());
665
2
    let cancel_action = reply.cancel_action().expect("missing cancel action");
666

            
667
2
    let id = cancel_action
668
2
        .id()
669
2
        .await
670
2
        .expect("failed to fetch cancel action id");
671
2
    assert_eq!(id, "10");
672

            
673
2
    assert_eq!(target.poll_hits(), 1);
674
2
    assert_eq!(target.cancel_action_hits(), 1);
675

            
676
2
    let mut mock = target.expect_cancel_feedback(
677
2
        &id,
678
2
        Execution::Proceeding,
679
2
        Finished::None,
680
2
        vec!["Cancelling"],
681
    );
682
2
    assert_eq!(mock.calls(), 0);
683

            
684
2
    cancel_action
685
2
        .send_feedback(Execution::Proceeding, Finished::None, vec!["Cancelling"])
686
2
        .await
687
2
        .expect("Failed to send feedback");
688
2
    assert_eq!(mock.calls(), 1);
689
3
    mock.delete();
690
2
}
691

            
692
#[tokio::test]
693
3
async fn client_authorization() {
694
2
    init();
695

            
696
2
    let server = ServerBuilder::default()
697
2
        .target_authorization(hawkbit_mock::ddi::TargetAuthorization::None)
698
2
        .build();
699
2
    let (client, _target) = add_target(&server, "Target1");
700

            
701
2
    client.poll().await.expect("poll failed");
702

            
703
2
    let server = ServerBuilder::default()
704
2
        .target_authorization(hawkbit_mock::ddi::TargetAuthorization::TargetToken)
705
2
        .build();
706
2
    let _ = add_target(&server, "Target2");
707
    // create the client manually to control the authorization method
708
2
    let client1 = Client::new(
709
2
        &server.base_url(),
710
2
        &server.tenant,
711
2
        "Target2",
712
2
        hawkbit::ddi::ClientAuthorization::TargetToken("KeyTarget2".to_string()),
713
2
        None,
714
2
        None,
715
2
        None,
716
    )
717
2
    .unwrap();
718
2
    let client2 = Client::new(
719
2
        &server.base_url(),
720
2
        &server.tenant,
721
2
        "Target2",
722
2
        hawkbit::ddi::ClientAuthorization::TargetToken("WrongToken".to_string()),
723
2
        None,
724
2
        None,
725
2
        None,
726
    )
727
2
    .unwrap();
728
2
    let client3 = Client::new(
729
2
        &server.base_url(),
730
2
        &server.tenant,
731
2
        "Target2",
732
2
        hawkbit::ddi::ClientAuthorization::None,
733
2
        None,
734
2
        None,
735
2
        None,
736
    )
737
2
    .unwrap();
738

            
739
2
    client1.poll().await.expect("poll failed");
740
2
    client2
741
2
        .poll()
742
2
        .await
743
2
        .expect_err("poll with wrong token succeeded");
744
2
    client3
745
2
        .poll()
746
2
        .await
747
2
        .expect_err("poll without token succeeded");
748

            
749
2
    let server = ServerBuilder::default()
750
2
        .target_authorization(hawkbit_mock::ddi::TargetAuthorization::GatewayToken)
751
2
        .build();
752
2
    let _ = add_target(&server, "Target3");
753
    // create the client manually to control the authorization method
754
2
    let client1 = Client::new(
755
2
        &server.base_url(),
756
2
        &server.tenant,
757
2
        "Target3",
758
2
        hawkbit::ddi::ClientAuthorization::GatewayToken("KeyDEFAULT".to_string()),
759
2
        None,
760
2
        None,
761
2
        None,
762
    )
763
2
    .unwrap();
764
2
    let client2 = Client::new(
765
2
        &server.base_url(),
766
2
        &server.tenant,
767
2
        "Target3",
768
2
        hawkbit::ddi::ClientAuthorization::GatewayToken("WrongToken".to_string()),
769
2
        None,
770
2
        None,
771
2
        None,
772
    )
773
2
    .unwrap();
774
2
    let client3 = Client::new(
775
2
        &server.base_url(),
776
2
        &server.tenant,
777
2
        "Target3",
778
2
        hawkbit::ddi::ClientAuthorization::TargetToken("KeyTarget3".to_string()),
779
2
        None,
780
2
        None,
781
2
        None,
782
    )
783
2
    .unwrap();
784

            
785
2
    client1.poll().await.expect("poll failed");
786
2
    client2
787
2
        .poll()
788
2
        .await
789
2
        .expect_err("poll with wrong token succeeded");
790
3
    client3
791
3
        .poll()
792
3
        .await
793
3
        .expect_err("poll with TargetToken succeeded");
794
2
}
795

            
796
#[tokio::test]
797
3
async fn certificate() {
798
2
    init();
799

            
800
    // the mock server used by hawkbit_mock does not support https, so we
801
    // cannot test it fully, but we can at least test loading a certificate
802
    // file.
803
2
    let server = ServerBuilder::default()
804
2
        .target_authorization(hawkbit_mock::ddi::TargetAuthorization::None)
805
2
        .build();
806
2
    let (_client, _target) = add_target(&server, "Target1");
807
2
    let client1 = Client::new(
808
2
        &server.base_url(),
809
2
        &server.tenant,
810
2
        "Target1",
811
2
        hawkbit::ddi::ClientAuthorization::TargetToken("KeyTarget1".to_string()),
812
2
        Some("tests/data/test.txt"),
813
2
        None,
814
2
        None,
815
    )
816
2
    .unwrap();
817

            
818
    // this returns an error, as the mock server does not use https
819
3
    client1.poll().await.unwrap_err();
820
2
}