Cargo 测试在本地机器上通过,但在 github 操作上失败
Cargo test pass on local machine but fails on github actions
我写了一个测试来验证 POST 是否被正确解析。这是终点:
#[post("/test_weather_data")]
pub(crate) async fn test_weather_data_post(weather_data: String) -> Result<String> {
let wd: Vec<Reading> = serde_json::from_str(&*weather_data).unwrap();
Ok(format!("id: {}, index: {}", &wd[0].id, &wd[0].index))
}
测试:
#[actix_rt::test]
async fn test_weather_data_ok() {
let mut app = test::init_service(App::new().service(controller::test_weather_data_post)).await;
let payload = r#"[{"measurement_time_default":"2021-03-24T20:50:00+01:00","id":228,"index":201,"field_description":"relative_humidity","measurement":77.5}]"#;
let resp = test::TestRequest::post()
.uri("/test_weather_data")
.header(header::CONTENT_TYPE, "application/json")
.set_payload(payload)
.send_request(&mut app)
.await;
let result = test::read_body(resp).await;
assert_eq!(result, "id: 228, index: 201".as_bytes());
}
结构:
#[derive(Deserialize, Insertable)]
pub struct Reading {
#[serde(with = "my_date_format")]
pub measurement_time_default: DateTime<Local>,
pub id: i32,
pub index: i32,
pub field_description: String,
pub measurement: f32,
}
模块my_date_format:
mod my_date_format {
use chrono::{DateTime, Local, TimeZone};
use serde::{self, Deserialize, Deserializer};
const FORMAT: &str = "%+";
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Local>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Local
.datetime_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
}
cargo test
通过我的本地 mac mini (M1) 但在 github actions
上失败并出现错误
thread 'test::test_weather_data_ok' panicked at 'called `Result::unwrap()` on an `Err` value: Error("no possible date and time matching input", line: 1, column: 56)', src/controller.rs:63:65
这是我在 github 上的 YML 文件,这是 github 分发的默认文件:
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
有一个 与 exit code 101
类似,但这种情况下的错误是由于目录布局更改造成的,因此它似乎不适用于这种情况。
在测试中省略 as_bytes() 会产生相同的错误。
看起来错误是在尝试使用 let wd: Vec<Reading> = serde_json::from_str(&*weather_data).unwrap();
处的 serde 解析 json 时发生的,它似乎与 json 有效负载中的时间戳有关。通常 GitHub 操作以 UTC 执行,这可能与您本地计算机的时钟不同,这有时会导致错误。
也有可能每个平台支持的时间戳格式不同,但这取决于被反序列化的数据结构的定义,这在问题中没有显示
chrono
的文档指出 datetime_from_str
仅当文本中没有时区或时区与目标 TimeZone
匹配时才会成功,这解释了如果时区不同。文档指向 DateTime::parse_from_str
以将具有任何时区的 DateTime
解析为 DateTime<FixedOffset>
对象,所以这样的事情会起作用
DateTime::parse_from_str(&s, FORMAT)
.map(Into::into)
.map_err(serde::de::Error::custom)
Into::into
需要将类型从 DateTime<FixedOffset>
映射到 DateTime<Local>
以匹配您的 return 类型。
也就是说,chrono
直接以rfc3339为默认格式实现序列化。您需要启用 serde
功能标志。然后你可以摆脱你的自定义反序列化代码,只需使用
#[derive(Deserialize, Insertable)]
pub struct Reading {
pub measurement_time_default: DateTime<Local>,
pub id: i32,
pub index: i32,
pub field_description: String,
pub measurement: f32,
}
我写了一个测试来验证 POST 是否被正确解析。这是终点:
#[post("/test_weather_data")]
pub(crate) async fn test_weather_data_post(weather_data: String) -> Result<String> {
let wd: Vec<Reading> = serde_json::from_str(&*weather_data).unwrap();
Ok(format!("id: {}, index: {}", &wd[0].id, &wd[0].index))
}
测试:
#[actix_rt::test]
async fn test_weather_data_ok() {
let mut app = test::init_service(App::new().service(controller::test_weather_data_post)).await;
let payload = r#"[{"measurement_time_default":"2021-03-24T20:50:00+01:00","id":228,"index":201,"field_description":"relative_humidity","measurement":77.5}]"#;
let resp = test::TestRequest::post()
.uri("/test_weather_data")
.header(header::CONTENT_TYPE, "application/json")
.set_payload(payload)
.send_request(&mut app)
.await;
let result = test::read_body(resp).await;
assert_eq!(result, "id: 228, index: 201".as_bytes());
}
结构:
#[derive(Deserialize, Insertable)]
pub struct Reading {
#[serde(with = "my_date_format")]
pub measurement_time_default: DateTime<Local>,
pub id: i32,
pub index: i32,
pub field_description: String,
pub measurement: f32,
}
模块my_date_format:
mod my_date_format {
use chrono::{DateTime, Local, TimeZone};
use serde::{self, Deserialize, Deserializer};
const FORMAT: &str = "%+";
pub fn deserialize<'de, D>(deserializer: D) -> Result<DateTime<Local>, D::Error>
where
D: Deserializer<'de>,
{
let s = String::deserialize(deserializer)?;
Local
.datetime_from_str(&s, FORMAT)
.map_err(serde::de::Error::custom)
}
}
cargo test
通过我的本地 mac mini (M1) 但在 github actions
上失败并出现错误
thread 'test::test_weather_data_ok' panicked at 'called `Result::unwrap()` on an `Err` value: Error("no possible date and time matching input", line: 1, column: 56)', src/controller.rs:63:65
这是我在 github 上的 YML 文件,这是 github 分发的默认文件:
name: Rust
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
env:
CARGO_TERM_COLOR: always
jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
- name: Build
run: cargo build --verbose
- name: Run tests
run: cargo test --verbose
有一个 exit code 101
类似,但这种情况下的错误是由于目录布局更改造成的,因此它似乎不适用于这种情况。
在测试中省略 as_bytes() 会产生相同的错误。
看起来错误是在尝试使用 let wd: Vec<Reading> = serde_json::from_str(&*weather_data).unwrap();
处的 serde 解析 json 时发生的,它似乎与 json 有效负载中的时间戳有关。通常 GitHub 操作以 UTC 执行,这可能与您本地计算机的时钟不同,这有时会导致错误。
也有可能每个平台支持的时间戳格式不同,但这取决于被反序列化的数据结构的定义,这在问题中没有显示
chrono
的文档指出 datetime_from_str
仅当文本中没有时区或时区与目标 TimeZone
匹配时才会成功,这解释了如果时区不同。文档指向 DateTime::parse_from_str
以将具有任何时区的 DateTime
解析为 DateTime<FixedOffset>
对象,所以这样的事情会起作用
DateTime::parse_from_str(&s, FORMAT)
.map(Into::into)
.map_err(serde::de::Error::custom)
Into::into
需要将类型从 DateTime<FixedOffset>
映射到 DateTime<Local>
以匹配您的 return 类型。
也就是说,chrono
直接以rfc3339为默认格式实现序列化。您需要启用 serde
功能标志。然后你可以摆脱你的自定义反序列化代码,只需使用
#[derive(Deserialize, Insertable)]
pub struct Reading {
pub measurement_time_default: DateTime<Local>,
pub id: i32,
pub index: i32,
pub field_description: String,
pub measurement: f32,
}