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
use anyhow::{Context, Result};
use flate2::read::GzDecoder;
use serde_json::Value;
use sqlx::{PgPool, Postgres, Transaction};
use std::io::Read;
use stormchaser_model::BackendId;
use stormchaser_model::StorageBackend;
use tar::Archive;
pub async fn persist_step_test_reports(
payload: &Value,
tx: &mut Transaction<'_, Postgres>,
run_id: stormchaser_model::RunId,
step_id: stormchaser_model::StepInstanceId,
pool: &PgPool,
) -> Result<()> {
if let Some(reports) = payload["test_reports"].as_object() {
for (_key, report_val) in reports {
let name = report_val
.get("name")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let file_name = report_val
.get("file_name")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let format = report_val
.get("format")
.and_then(|v| v.as_str())
.unwrap_or("unknown");
let hash = report_val
.get("hash")
.and_then(|v| v.as_str())
.unwrap_or("");
if report_val
.get("is_claim")
.and_then(|v| v.as_bool())
.unwrap_or(false)
{
let remote_path = report_val.get("remote_path").and_then(|v| v.as_str());
let backend_id = report_val.get("backend_id").and_then(|v| {
if let Some(s) = v.as_str() {
uuid::Uuid::parse_str(s).ok().map(BackendId::new)
} else {
None
}
});
if let (Some(path), Some(bid)) = (remote_path, backend_id) {
// Download and parse
let backend: StorageBackend =
crate::db::storage::get_storage_backend_by_id(pool, bid.into_inner())
.await?
.ok_or_else(|| anyhow::anyhow!("Storage backend not found"))?;
let client = crate::s3::get_s3_client(&backend).await?;
let bucket = backend.config["bucket"]
.as_str()
.context("Missing bucket")?;
let response = client.get_object().bucket(bucket).key(path).send().await?;
let data = response.body.collect().await?.to_vec();
// It's a tar.gz
let mut summaries = Vec::new();
let mut test_cases = Vec::new();
let mut raw_contents = Vec::new();
{
// Scope for !Send Archive
let tar_gz = GzDecoder::new(&data[..]);
let mut archive = Archive::new(tar_gz);
for entry in archive.entries()? {
let mut entry = entry?;
let mut content = String::new();
entry.read_to_string(&mut content)?;
if format == "junit" {
if let Ok((summary, cases)) =
crate::junit::parse_junit(&content, name, run_id, step_id)
{
summaries.push(summary);
test_cases.extend(cases);
}
}
raw_contents.push(content);
}
}
for case in test_cases {
crate::db::insert_step_test_case(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
&case,
)
.await?;
}
if let Some(final_summary) = crate::junit::aggregate_summaries(&summaries) {
crate::db::insert_step_test_summary(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
&final_summary,
)
.await?;
}
let combined_raw = raw_contents.join("\n---\n");
crate::db::insert_step_test_report(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
file_name,
format,
Some(&combined_raw),
hash,
Some(bid.into_inner()),
Some(path),
)
.await?;
}
} else if let Some(content) = report_val.get("content").and_then(|v| v.as_str()) {
// Legacy in-memory report
if format == "junit" {
if let Ok((summary, cases)) =
crate::junit::parse_junit(content, name, run_id, step_id)
{
crate::db::insert_step_test_summary(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
&summary,
)
.await?;
for case in cases {
crate::db::insert_step_test_case(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
&case,
)
.await?;
}
}
}
crate::db::insert_step_test_report(
&mut **tx,
run_id.into_inner(),
step_id.into_inner(),
name,
file_name,
format,
Some(content),
hash,
None,
None,
)
.await?;
}
}
}
Ok(())
}