implement conditional put/patch

Signed-off-by: Alexander Onnikov <Alexander.Onnikov@xored.com>
This commit is contained in:
Alexander Onnikov
2025-09-22 23:41:20 +07:00
parent 2469c00b1d
commit 72dae37dd6
8 changed files with 542 additions and 48 deletions
+47
View File
@@ -37,3 +37,50 @@ pub async fn get_known() -> eyre::Result<()> {
Ok(())
}
#[tanu::test]
pub async fn get_conditional() -> eyre::Result<()> {
let key = random_key();
let text = random_text(1024);
let http = Client::new();
let res = http.key_put(&key).body(text.clone()).send().await?;
check!(res.status().is_success());
let res = http.key_get(&key).send().await?;
check!(res.status().is_success());
let etag = res.header("etag").expect("ETag not found");
// Test without If-None-Match (normal GET)
let res = http.key_get(&key).send().await?;
check!(res.status().is_success());
check_eq!(text, res.text().await?);
// Test with If-None-Match: *
let res = http
.key_get(&key)
.header("If-None-Match", "*")
.send()
.await?;
check_eq!(res.status(), http::StatusCode::NOT_MODIFIED);
// Test with correct ETag
let res = http
.key_get(&key)
.header("If-None-Match", etag)
.send()
.await?;
check_eq!(res.status(), http::StatusCode::NOT_MODIFIED);
// Test with incorrect ETag
let res = http
.key_get(&key)
.header("If-None-Match", "\"invalid-etag\"")
.send()
.await?;
check!(res.status().is_success());
check_eq!(text, res.text().await?);
Ok(())
}
+45
View File
@@ -64,3 +64,48 @@ pub async fn head_known_with_jsonpatch() -> eyre::Result<()> {
Ok(())
}
#[tanu::test]
pub async fn head_conditional() -> eyre::Result<()> {
let key = random_key();
let text = random_text(1024);
let http = Client::new();
let res = http.key_put(&key).body(text.clone()).send().await?;
check!(res.status().is_success());
let res = http.key_head(&key).send().await?;
check!(res.status().is_success());
let etag = res.header("etag").expect("ETag not found");
// Test without If-None-Match (normal GET)
let res = http.key_head(&key).send().await?;
check!(res.status().is_success());
// Test with If-None-Match: *
let res = http
.key_head(&key)
.header("If-None-Match", "*")
.send()
.await?;
check_eq!(res.status(), http::StatusCode::NOT_MODIFIED);
// Test with correct ETag
let res = http
.key_head(&key)
.header("If-None-Match", etag)
.send()
.await?;
check_eq!(res.status(), http::StatusCode::NOT_MODIFIED);
// Test with incorrect ETag
let res = http
.key_head(&key)
.header("If-None-Match", "\"invalid-etag\"")
.send()
.await?;
check!(res.status().is_success());
Ok(())
}
+56 -2
View File
@@ -1,11 +1,11 @@
use hulyrs::StatusCode;
use serde_json::{self as json, Value, json};
use tanu::{check, check_eq, eyre, http::Client};
use tanu::{check, check_eq, check_ne, eyre, http::Client};
use crate::util::*;
#[tanu::test((10, 10))]
pub async fn put_and_patch_contact((initial, patch): (usize, usize)) -> eyre::Result<()> {
pub async fn put_and_patch_content((initial, patch): (usize, usize)) -> eyre::Result<()> {
let key = random_key();
let initial = random_text(1024 * initial);
let patch = random_text(1024 * patch);
@@ -234,3 +234,57 @@ async fn get_json_patch_safe() -> eyre::Result<()> {
Ok(())
}
#[derive(PartialEq, Eq)]
pub enum IfMatch {
ETag,
Some(&'static str),
None,
}
#[tanu::test(1, IfMatch::None, StatusCode::CREATED)]
#[tanu::test(2, IfMatch::ETag, StatusCode::CREATED)]
#[tanu::test(3, IfMatch::Some("*"), StatusCode::CREATED)]
#[tanu::test(4, IfMatch::Some("\"unknown\""), StatusCode::PRECONDITION_FAILED)]
pub async fn put_and_patch_conditional(
_: usize,
if_match: IfMatch,
status: StatusCode,
) -> eyre::Result<()> {
let key = random_key();
let initial = random_text(1024);
let patch = random_text(1024);
let http = Client::new();
// create new blob
let res = http.key_put(&key).body(initial.clone()).send().await?;
check!(res.status().is_success());
// check content
let res = http.key_get(&key).send().await?;
check!(res.status().is_success());
let etag = res.header("etag").expect("ETag not found");
let mut req = http.key_patch(&key).body(patch.clone());
req = match if_match {
IfMatch::ETag => req.header("If-Match", etag),
IfMatch::Some(etag) => req.header("If-Match", etag),
IfMatch::None => req,
};
let res = req.send().await?;
check_eq!(res.status(), status);
let res = http.key_get(&key).send().await?;
check!(res.status().is_success());
if status.is_success() {
check_ne!(res.header("etag"), Some(etag));
} else {
check_eq!(res.header("etag"), Some(etag));
}
Ok(())
}
+71
View File
@@ -341,3 +341,74 @@ pub async fn put_merge_patch(
Ok(())
}
#[derive(PartialEq, Eq)]
pub enum Condition {
ETag,
IfMatch(&'static str),
IfNoneMatch(&'static str),
}
#[tanu::test(1, Condition::ETag, StatusCode::CREATED)]
#[tanu::test(2, Condition::IfMatch("*"), StatusCode::PRECONDITION_FAILED)]
#[tanu::test(3, Condition::IfNoneMatch("*"), StatusCode::CREATED)]
#[tanu::test(4, Condition::IfNoneMatch("\"unknown\""), StatusCode::CREATED)]
pub async fn put_conditional_create(
_: usize,
condition: Condition,
status: StatusCode,
) -> eyre::Result<()> {
let key = random_key();
let body = random_text(1024);
let http = Client::new();
let mut req = http.key_put(&key).body(body.clone());
req = match condition {
Condition::ETag => req,
Condition::IfMatch(etag) => req.header("If-Match", etag),
Condition::IfNoneMatch(etag) => req.header("If-None-Match", etag),
};
let res = req.send().await?;
check_eq!(res.status(), status);
Ok(())
}
#[tanu::test(1, Condition::ETag, StatusCode::CREATED)]
#[tanu::test(2, Condition::IfMatch("*"), StatusCode::CREATED)]
#[tanu::test(3, Condition::IfNoneMatch("*"), StatusCode::PRECONDITION_FAILED)]
#[tanu::test(
4,
Condition::IfNoneMatch("\"unknown\""),
StatusCode::INTERNAL_SERVER_ERROR
)]
pub async fn put_conditional_update(
_: usize,
condition: Condition,
status: StatusCode,
) -> eyre::Result<()> {
let key = random_key();
let body = random_text(1024);
let http = Client::new();
let res = http.key_put(&key).body(body.clone()).send().await?;
check!(res.status().is_success());
let etag = res.header("etag").expect("ETag not found");
let mut req = http.key_put(&key).body(body.clone());
req = match condition {
Condition::ETag => req.header("If-Match", etag),
Condition::IfMatch(etag) => req.header("If-Match", etag),
Condition::IfNoneMatch(etag) => req.header("If-None-Match", etag),
};
let res = req.send().await?;
check_eq!(res.status(), status);
Ok(())
}