File size: 3,904 Bytes
c5a0215
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
use std::sync::Arc;
use std::time::{Duration, Instant};

use axum::body::Body;
use axum::http::{Request, StatusCode};
use http_body_util::BodyExt;
use solverforge_deliveries::api;
use solverforge_deliveries::data::{generate, DemoData};
use solverforge_deliveries::domain::{Plan, RoutingMode};
use tower::ServiceExt;
use tower_http::services::ServeDir;

pub fn test_app() -> axum::Router {
    let state = Arc::new(api::AppState::new());
    api::router(state)
        .merge(solverforge_ui::routes())
        .fallback_service(ServeDir::new("static"))
}

pub fn small_plan() -> serde_json::Value {
    let mut plan = generate(DemoData::Hartford);
    plan.deliveries.truncate(8);
    plan.vehicles.truncate(3);
    plan.normalize();
    plan.routing_mode = RoutingMode::StraightLine;
    serde_json::to_value(plan.refreshed_for_transport()).expect("plan should serialize")
}

pub fn bigger_plan() -> serde_json::Value {
    let mut plan = generate(DemoData::Philadelphia);
    plan.routing_mode = RoutingMode::StraightLine;
    serde_json::to_value(plan.refreshed_for_transport()).expect("plan should serialize")
}

pub fn completion_plan() -> serde_json::Value {
    let mut plan = generate(DemoData::Hartford);
    plan.deliveries.truncate(6);
    plan.vehicles.truncate(2);
    plan.normalize();
    plan.routing_mode = RoutingMode::StraightLine;
    serde_json::to_value(plan.refreshed_for_transport()).expect("plan should serialize")
}

pub fn empty_road_network_plan() -> serde_json::Value {
    let mut plan = Plan::new("Empty Road Network", Vec::new(), Vec::new());
    plan.routing_mode = RoutingMode::RoadNetwork;
    serde_json::to_value(plan.refreshed_for_transport()).expect("plan should serialize")
}

pub fn small_road_network_plan() -> serde_json::Value {
    let mut plan = generate(DemoData::Hartford);
    plan.deliveries.truncate(4);
    plan.vehicles.truncate(2);
    plan.normalize();
    plan.routing_mode = RoutingMode::RoadNetwork;
    serde_json::to_value(plan.refreshed_for_transport()).expect("plan should serialize")
}

pub async fn read_json(response: axum::response::Response) -> serde_json::Value {
    let body = read_body_text(response).await;
    serde_json::from_str(&body).expect("body should be valid JSON")
}

pub async fn read_body_text(response: axum::response::Response) -> String {
    let body = response
        .into_body()
        .collect()
        .await
        .expect("body should collect")
        .to_bytes();
    String::from_utf8(body.to_vec()).expect("body should be valid UTF-8")
}

pub fn assert_score_value<'a>(value: &'a serde_json::Value, label: &str) -> &'a str {
    let score = value
        .as_str()
        .unwrap_or_else(|| panic!("{label} should be a display string, got {value:?}"));
    assert_score_text(score, label);
    score
}

pub fn assert_score_text(score: &str, label: &str) {
    assert!(
        score.contains("hard/") && score.ends_with("soft") && !score.trim_start().starts_with('{'),
        "{label} should use SolverForge display-score format, got {score:?}"
    );
}

pub async fn poll_job_state(app: &axum::Router, job_id: &str, wanted: &str) -> serde_json::Value {
    let start = Instant::now();
    loop {
        let response = app
            .clone()
            .oneshot(
                Request::get(format!("/jobs/{job_id}"))
                    .body(Body::empty())
                    .expect("request should build"),
            )
            .await
            .expect("request should succeed");
        assert_eq!(response.status(), StatusCode::OK);
        let json = read_json(response).await;
        if json["lifecycleState"] == wanted {
            return json;
        }
        if start.elapsed() > Duration::from_secs(6) {
            panic!("job {job_id} did not reach {wanted} in time; last state={json:?}");
        }
        tokio::time::sleep(Duration::from_millis(80)).await;
    }
}