use aurabase::create_client;
use wiremock::matchers::{body_json, method, path};
use wiremock::{Mock, MockServer, ResponseTemplate};
fn test_client(base_url: &str) -> aurabase::AuraClient {
create_client(base_url, "aura_anon_test", None)
}
#[tokio::test]
async fn copy_sends_keys_relative_to_bucket_not_prefixed() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/storage/avatars/copy"))
.and(body_json(serde_json::json!({
"src_key": "3f2a.png",
"dst_key": "copie.png",
})))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"data": { "key": "copie.png", "bucket": "avatars" }
})))
.mount(&server)
.await;
let client = test_client(&server.uri());
let res = client
.storage()
.copy("avatars", "3f2a.png", "copie.png")
.await
.expect("copy() a échoué");
assert!(
res.error.is_none(),
"copy() a renvoyé une erreur — la clé envoyée ne correspond pas à \
celle attendue par le mock (donc probablement encore préfixée) : {:?}",
res.error
);
}
#[tokio::test]
async fn move_object_sends_keys_relative_to_bucket_not_prefixed() {
let server = MockServer::start().await;
Mock::given(method("POST"))
.and(path("/v1/storage/avatars/move"))
.and(body_json(serde_json::json!({
"src_key": "3f2a.png",
"dst_key": "deplace.png",
})))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"data": { "key": "deplace.png", "bucket": "avatars" }
})))
.mount(&server)
.await;
let client = test_client(&server.uri());
let res = client
.storage()
.move_object("avatars", "3f2a.png", "deplace.png")
.await
.expect("move_object() a échoué");
assert!(
res.error.is_none(),
"move_object() a renvoyé une erreur — la clé envoyée ne correspond \
pas à celle attendue par le mock (donc probablement encore \
préfixée) : {:?}",
res.error
);
}
#[tokio::test]
async fn copy_accepts_key_roundtripped_from_upload() {
let server = MockServer::start().await;
let uploaded_key = "129cf0e4-a03d-4d98-bffb-6e59d55152ad.txt";
Mock::given(method("POST"))
.and(path("/v1/storage/docs/copy"))
.and(body_json(serde_json::json!({
"src_key": uploaded_key,
"dst_key": "archive.txt",
})))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"data": { "key": "archive.txt", "bucket": "docs" }
})))
.mount(&server)
.await;
let client = test_client(&server.uri());
let res = client
.storage()
.copy("docs", uploaded_key, "archive.txt")
.await
.expect("copy() a échoué");
assert!(res.error.is_none(), "{:?}", res.error);
}
#[tokio::test]
async fn list_strips_logical_bucket_prefix_from_key() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/storage/avatars"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"data": [
{
"key": "avatars/1.png",
"bucket": "avatars",
"content_type": "image/png",
"size": 3
}
]
})))
.mount(&server)
.await;
let client = test_client(&server.uri());
let res = client
.storage()
.list("avatars", None, None, None)
.await
.expect("list() a échoué");
let files = res.data.expect("aucune donnée renvoyée par list()");
assert_eq!(
files[0].name, "1.png",
"la clé renvoyée par list() doit être relative au bucket, comme \
celle renvoyée par upload() — reçu : {:?}",
files[0].name
);
}
#[tokio::test]
async fn list_leaves_key_without_bucket_prefix_untouched() {
let server = MockServer::start().await;
Mock::given(method("GET"))
.and(path("/v1/storage/avatars"))
.respond_with(ResponseTemplate::new(200).set_body_json(serde_json::json!({
"data": [
{
"key": "1.png",
"bucket": "avatars",
"content_type": "image/png",
"size": 3
}
]
})))
.mount(&server)
.await;
let client = test_client(&server.uri());
let res = client
.storage()
.list("avatars", None, None, None)
.await
.expect("list() a échoué");
let files = res.data.expect("aucune donnée renvoyée par list()");
assert_eq!(files[0].name, "1.png");
}
mod n7_avertissement_url_publique {
const SOURCE: &str = include_str!("../src/services/storage.rs");
fn doc_de_get_public_url() -> String {
let idx = SOURCE
.find("pub fn get_public_url")
.expect("get_public_url doit exister");
let avant: Vec<&str> = SOURCE[..idx].lines().collect();
avant
.iter()
.rev()
.skip(1)
.take_while(|l| l.trim_start().starts_with("///"))
.copied()
.collect::<Vec<_>>()
.join("\n")
.to_lowercase()
}
#[test]
fn get_public_url_est_documentee() {
assert!(
!doc_de_get_public_url().is_empty(),
"une méthode qui fabrique une URL publique ne peut pas rester sans documentation"
);
}
#[test]
fn la_doc_avertit_que_lurl_est_reellement_publique() {
let doc = doc_de_get_public_url();
assert!(doc.contains("public"), "{doc}");
assert!(
doc.contains("sans aucune clé api"),
"l'avertissement doit dire que l'URL répond SANS AUCUNE clé API : {doc}"
);
assert!(
!doc.contains("exige quand même une clé api"),
"ne jamais réaffirmer qu'une clé API est exigée : {doc}"
);
}
#[test]
fn la_doc_oriente_vers_lurl_signee_pour_un_bucket_prive() {
let doc = doc_de_get_public_url();
assert!(
doc.contains("create_signed_url") || doc.contains("signée"),
"l'alternative pour un partage temporaire doit être nommée : {doc}"
);
}
}