-
Notifications
You must be signed in to change notification settings - Fork 0
/
common.rs
248 lines (208 loc) · 8.77 KB
/
common.rs
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
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
/* Date Created: 29/12/2023. */
//! Light common functions in used in tests.
use std::{fs::File, io::Read as _,};
use std::net::TcpListener;
use actix_web::http::{StatusCode, header};
use reqwest::tls::Certificate;
use learn_actix_web::bh_libs::api_status::ApiStatus;
use learn_actix_web::run;
use learn_actix_web::models::LoginSuccessResponse;
use learn_actix_web::helper::endpoint::http_status_code;
// use learn_actix_web::helper::messages::LOGIN_FAILURE_MSG;
pub struct TestApp {
pub app_url: String,
}
impl TestApp {
pub fn mock_access_token(&self) -> String {
String::from("chirstian.koblick.10004@gmail.com")
}
}
pub async fn spawn_app() -> TestApp {
let listener = TcpListener::bind("0.0.0.0:0")
.expect("Failed to bind random port");
// We retrieve the port assigned to us by the OS
let port = listener.local_addr().unwrap().port();
let server = run(listener).await.unwrap();
let _ = tokio::spawn(server);
TestApp {
app_url: format!("https://127.0.0.1:{}", port)
}
}
/// See https://github.com/actix/examples/tree/master/https-tls/openssl
/// https://docs.rs/reqwest/latest/reqwest/tls/struct.Certificate.html
///
/// This certificate has been generated on Ubuntu 22.10.
///
/// It seems that ``.add_root_certificate(load_certificate())`` only
/// required on the first run? Without it, certicate fails with:
///
/// ```
/// Failed to execute request.: reqwest::Error { kind: Request, url: Url { scheme: "https",
/// cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)),
/// port: Some(36159), path: "/api/login", query: None, fragment: None },
/// source: hyper::Error(Connect, Ssl(Error { code: ErrorCode(1), cause:
/// Some(Ssl(ErrorStack([Error { code: 167772294, library: "SSL routines",
/// function: "tls_post_process_server_certificate", reason: "certificate verify failed",
/// file: "../ssl/statem/statem_clnt.c", line: 1889 }]))) }, X509VerifyResult { code: 18,
/// error: "self-signed certificate" })) }
/// ```
///
/// After the first run (?), ``.add_root_certificate(load_certificate())`` can be removed?
///
pub fn load_certificate() -> Certificate {
let mut buf = Vec::new();
File::open("./cert/cert-pass.pem")
.unwrap()
.read_to_end(&mut buf).unwrap();
Certificate::from_pem(&buf).unwrap()
}
/// Creates a reqwest::Client to work with the HTTPS server.
/// There is no need to load the certificate that the server is using.
///
/// # References
///
/// https://stackoverflow.com/questions/76724036/how-to-resolve-a-rust-reqwest-error-invalid-certificate
/// Answer https://stackoverflow.com/questions/76724036/how-to-resolve-a-rust-reqwest-error-invalid-certificate#comment135281495_76724036
///
/// mentions using ``.danger_accept_invalid_cert(true)``.
///
/// Without calling ``.danger_accept_invalid_certs(true)`` -- note the s -- it would
/// result in the following error:
///
/// ```
/// Failed to execute request.: reqwest::Error { kind: Request, url: Url { scheme: "https", cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(62730), path: "/ui/home", query: None, fragment: None }, source: hyper::Error(Connect, Os { code: -2146762487, kind: Uncategorized, message: "A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider." }) }
/// note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
/// ```
///
/// or
///
/// ```
/// Failed to execute request.: reqwest::Error { kind: Request, url: Url { scheme: "http", cannot_be_a_base: false, username: "", password: None, host: Some(Ipv4(127.0.0.1)), port: Some(65030), path: "/api/login", query: None, fragment: None }, source: hyper::Error(Io, Os { code: 10054, kind: ConnectionReset, message: "An existing connection was forcibly closed by the remote host." }) }
/// ```
///
pub fn reqwest_client() -> reqwest::Client {
reqwest::Client::builder()
.add_root_certificate(load_certificate())
.danger_accept_invalid_certs(true)
.cookie_store(true)
.build()
.unwrap()
}
pub fn make_full_url(root: &str, path: &str) -> String {
format!("{}{}", root, path)
}
pub fn make_data_url(root: &str, path: &str) -> String {
format!("{}/data{}", root, path)
}
pub fn make_ui_url(root: &str, path: &str) -> String {
format!("{}/ui{}", root, path)
}
pub fn make_api_url(root: &str, path: &str) -> String {
format!("{}/api{}", root, path)
}
pub fn write_to_file(file_name: &str, content: &str) {
use std::fs::File;
use std::io::Write;
let mut file = File::create(file_name).unwrap();
file.write_all(content.as_bytes()).unwrap();
}
pub async fn assert_html_home_page(response: reqwest::Response) {
assert_eq!(response.status(), StatusCode::OK);
let res = response.text().await;
assert!(res.is_ok(), "Should have a HTML response.");
// This should now always succeed.
if let Ok(html) = res {
assert!(html.contains("<title>Rust Web 1 | Home</title>"), "HTML: title.");
assert!(html.contains("<button type=\"submit\">Logout</button>"), "HTML: logout button.");
}
}
pub async fn assert_html_login_page(response: reqwest::Response) {
assert_eq!(response.status(), StatusCode::OK);
let res = response.text().await;
assert!(res.is_ok(), "Should have a HTML response.");
// This should now always succeed.
if let Ok(html) = res {
assert!(html.contains("<title>Rust Web 1 | Login</title>"), "HTML: title.");
assert!(html.contains("<button type=\"submit\">Login</button>"), "HTML: Login button.");
}
}
pub fn assert_access_token_in_header(response: &reqwest::Response, access_token: &str) {
let header = response.headers().get(header::AUTHORIZATION);
assert_eq!(header.is_some(), true);
assert_eq!(header.unwrap().to_str().unwrap(), access_token);
}
/// TO_DO: this works, but feels clunky. Need reworks!
pub fn assert_access_token_in_cookie(response: &reqwest::Response, access_token: &str) {
// Assertain that cookie header::AUTHORIZATION is present.
let mut found: bool = false;
for c in response.cookies() {
if c.name() == header::AUTHORIZATION.as_str() {
assert_eq!(c.value(), access_token);
found = true;
break;
}
}
assert_eq!(found, true);
}
pub async fn assert_json_successful_login(
response: reqwest::Response,
email: &str,
access_token: &str) {
assert_eq!(response.status(), StatusCode::OK);
let res = response.json::<LoginSuccessResponse>().await;
assert!(res.is_ok(), "Should have a JSON response.");
// This should now always succeed.
if let Ok(json_obj) = res {
assert_eq!(json_obj.api_status.get_code(), http_status_code(StatusCode::OK));
assert_eq!(json_obj.data.email, email);
assert_eq!(json_obj.data.access_token, access_token);
}
}
pub fn assert_access_token_not_in_header(response: &reqwest::Response) {
let header = response.headers().get(header::AUTHORIZATION);
assert_eq!(header.is_none(), true);
}
/// TO_DO: this works, but feels clunky. Need reworks!
pub fn assert_access_token_not_in_cookie(response: &reqwest::Response) {
// Assertain that cookie header::AUTHORIZATION is present.
let mut found: bool = false;
for c in response.cookies() {
if c.name() == header::AUTHORIZATION.as_str() {
found = true;
break;
}
}
assert_eq!(found, false);
}
pub async fn assert_redirected_html_login_page(
response: reqwest::Response,
status_code: StatusCode,
message: &str) {
assert_eq!(response.status(), status_code);
let res = response.text().await;
assert!(res.is_ok(), "Should have a HTML response.");
// This should now always succeed.
if let Ok(html) = res {
assert!(html.contains("<title>Rust Web 1 | Login</title>"), "HTML: title.");
assert!(html.contains(&format!("<h2>{}</h2>", message)), "HTML: redirect message.");
assert!(html.contains("<button type=\"submit\">Login</button>"), "HTML: Login button.");
}
}
pub async fn assert_json_failure(
response: reqwest::Response,
status_code: StatusCode,
reason: &str,
reason_is_sub_text: bool) {
assert_eq!(response.status(), status_code);
let res = response.json::<ApiStatus>().await;
assert!(res.is_ok(), "Should have a JSON response.");
// This should now always succeed.
if let Ok(json_obj) = res {
assert_eq!(json_obj.get_code(), http_status_code(status_code));
match reason_is_sub_text {
true => assert!(json_obj.get_message().unwrap().contains(reason)),
false => assert_eq!(json_obj.get_message().unwrap(), reason)
};
assert_eq!(json_obj.get_session_id(), None);
}
}