patch tags
This commit is contained in:
@@ -37,6 +37,7 @@ pub struct StoredObject {
|
||||
pub key: String,
|
||||
pub bytes: Vec<u8>,
|
||||
pub content_type: Option<String>,
|
||||
pub content_disposition: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Default)]
|
||||
@@ -51,11 +52,13 @@ impl ObjectStorage for FakeStorage {
|
||||
key: &str,
|
||||
bytes: Vec<u8>,
|
||||
content_type: Option<String>,
|
||||
content_disposition: Option<String>,
|
||||
) -> Result<()> {
|
||||
let stored = StoredObject {
|
||||
key: key.to_string(),
|
||||
bytes,
|
||||
content_type,
|
||||
content_disposition,
|
||||
};
|
||||
let mut guard = self.objects.lock().await;
|
||||
guard.insert(stored.key.clone(), stored);
|
||||
@@ -119,6 +122,8 @@ impl TestApp {
|
||||
jwt_issuer: "test-issuer".to_string(),
|
||||
jwt_audience: "test-audience".to_string(),
|
||||
jwt_expiry_minutes: 60,
|
||||
download_token_audience: "test-download".to_string(),
|
||||
download_token_expiry_minutes: 60,
|
||||
refresh_token_expiry_days: 30,
|
||||
refresh_cookie_secure: false,
|
||||
refresh_cookie_domain: None,
|
||||
|
||||
@@ -22,6 +22,7 @@ struct DocumentInfo {
|
||||
deleted_at: Option<String>,
|
||||
issued_at: Option<String>,
|
||||
tags: Vec<TagSummary>,
|
||||
download_path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -42,6 +43,7 @@ struct DocumentAssetInfo {
|
||||
struct DocumentListItem {
|
||||
id: Uuid,
|
||||
current_version: i32,
|
||||
download_path: String,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
@@ -154,6 +156,7 @@ async fn upload_and_list_document() -> Result<()> {
|
||||
assert_eq!(detail.document.deleted_at, None);
|
||||
assert!(detail.document.issued_at.is_none());
|
||||
assert!(detail.document.tags.is_empty());
|
||||
assert!(detail.document.download_path.starts_with("/download/"));
|
||||
assert_eq!(detail.current_version.size_bytes, file_bytes.len() as i64);
|
||||
assert!(detail.assets.is_empty());
|
||||
|
||||
@@ -173,6 +176,7 @@ async fn upload_and_list_document() -> Result<()> {
|
||||
let item = list.pop().unwrap();
|
||||
assert_eq!(item.id, detail.document.id);
|
||||
assert_eq!(item.current_version, 1);
|
||||
assert!(item.download_path.starts_with("/download/"));
|
||||
|
||||
let download = app
|
||||
.get(
|
||||
@@ -186,6 +190,15 @@ async fn upload_and_list_document() -> Result<()> {
|
||||
assert!(download_info.url.contains(&detail.current_version.s3_key));
|
||||
assert_eq!(download_info.filename, "doc.txt");
|
||||
|
||||
let redirect = app.get(&detail.document.download_path, None).await?;
|
||||
assert_eq!(redirect.status(), StatusCode::TEMPORARY_REDIRECT);
|
||||
let location = redirect
|
||||
.headers()
|
||||
.get("location")
|
||||
.expect("redirect location header");
|
||||
let location = location.to_str().expect("location header utf8");
|
||||
assert!(location.contains(&detail.current_version.s3_key));
|
||||
|
||||
app.cleanup().await?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -21,11 +21,16 @@ struct DocumentInfo {
|
||||
#[derive(Deserialize)]
|
||||
struct TagInfo {
|
||||
label: String,
|
||||
#[allow(dead_code)]
|
||||
color: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct TagResponse {
|
||||
id: Uuid,
|
||||
label: String,
|
||||
color: Option<String>,
|
||||
usage_count: i64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
@@ -75,6 +80,53 @@ async fn tag_assignment_flow() -> Result<()> {
|
||||
assert_eq!(create_tag.status(), StatusCode::OK);
|
||||
let body = body_to_vec(create_tag.into_body()).await?;
|
||||
let tag: TagResponse = serde_json::from_slice(&body)?;
|
||||
assert_eq!(tag.label, "Important");
|
||||
assert_eq!(tag.color.as_deref(), Some("#FF0000"));
|
||||
assert_eq!(tag.usage_count, 0);
|
||||
|
||||
let update = app
|
||||
.patch_json(
|
||||
&format!("/api/tags/{}", tag.id),
|
||||
&serde_json::json!({
|
||||
"label": "Critical",
|
||||
"color": "#00FF00"
|
||||
}),
|
||||
Some(&token),
|
||||
)
|
||||
.await?;
|
||||
let updated_status = update.status();
|
||||
let updated_body = body_to_vec(update.into_body()).await?;
|
||||
if updated_status != StatusCode::OK {
|
||||
panic!(
|
||||
"update tag failed: {}",
|
||||
String::from_utf8_lossy(&updated_body)
|
||||
);
|
||||
}
|
||||
let updated: TagResponse = serde_json::from_slice(&updated_body)?;
|
||||
assert_eq!(updated.label, "Critical");
|
||||
assert_eq!(updated.color.as_deref(), Some("#00FF00"));
|
||||
assert_eq!(updated.usage_count, 0);
|
||||
|
||||
let clear_color = app
|
||||
.patch_json(
|
||||
&format!("/api/tags/{}", tag.id),
|
||||
&serde_json::json!({
|
||||
"color": null
|
||||
}),
|
||||
Some(&token),
|
||||
)
|
||||
.await?;
|
||||
let cleared_status = clear_color.status();
|
||||
let cleared_body = body_to_vec(clear_color.into_body()).await?;
|
||||
if cleared_status != StatusCode::OK {
|
||||
panic!(
|
||||
"clear color failed: {}",
|
||||
String::from_utf8_lossy(&cleared_body)
|
||||
);
|
||||
}
|
||||
let cleared: TagResponse = serde_json::from_slice(&cleared_body)?;
|
||||
assert_eq!(cleared.color, None);
|
||||
assert_eq!(cleared.usage_count, 0);
|
||||
|
||||
let assign = app
|
||||
.post_json(
|
||||
@@ -97,7 +149,7 @@ async fn tag_assignment_flow() -> Result<()> {
|
||||
let refreshed_body = body_to_vec(refreshed.into_body()).await?;
|
||||
let refreshed_detail: DocumentDetail = serde_json::from_slice(&refreshed_body)?;
|
||||
assert_eq!(refreshed_detail.document.tags.len(), 1);
|
||||
assert_eq!(refreshed_detail.document.tags[0].label, "Important");
|
||||
assert_eq!(refreshed_detail.document.tags[0].label, "Critical");
|
||||
|
||||
let remove = app
|
||||
.delete(
|
||||
|
||||
Reference in New Issue
Block a user