mirror of
https://github.com/deuxfleurs-org/garage.git
synced 2026-08-08 22:03:13 +00:00
Functional tests for website endpoints
This commit is contained in:
@@ -55,5 +55,6 @@ netapp = "0.3.0"
|
||||
[dev-dependencies]
|
||||
aws-sdk-s3 = "0.6"
|
||||
http = "0.2"
|
||||
hyper = { version = "0.14", features = ["client", "http1", "runtime"] }
|
||||
|
||||
static_init = "1.0"
|
||||
|
||||
@@ -6,7 +6,7 @@ use std::sync::Once;
|
||||
use super::ext::*;
|
||||
|
||||
// https://xkcd.com/221/
|
||||
const DEFAULT_PORT: u16 = 49995;
|
||||
pub const DEFAULT_PORT: u16 = 49995;
|
||||
|
||||
static GARAGE_TEST_SECRET: &str =
|
||||
"c3ea8cb80333d04e208d136698b1a01ae370d463f0d435ab2177510b3478bf44";
|
||||
|
||||
@@ -1 +1,342 @@
|
||||
use crate::common;
|
||||
use crate::common::ext::*;
|
||||
use aws_sdk_s3::{
|
||||
model::{CorsConfiguration, CorsRule, ErrorDocument, IndexDocument, WebsiteConfiguration},
|
||||
ByteStream,
|
||||
};
|
||||
use http::Request;
|
||||
use hyper::{
|
||||
body::{to_bytes, Body},
|
||||
Client,
|
||||
};
|
||||
|
||||
const BODY: &[u8; 16] = b"<h1>bonjour</h1>";
|
||||
const BODY_ERR: &[u8; 6] = b"erreur";
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_website() {
|
||||
const BCKT_NAME: &str = "my-website";
|
||||
let ctx = common::context();
|
||||
let bucket = ctx.create_bucket(BCKT_NAME);
|
||||
|
||||
let data = ByteStream::from_static(BODY);
|
||||
|
||||
ctx.client
|
||||
.put_object()
|
||||
.bucket(&bucket)
|
||||
.key("index.html")
|
||||
.body(data)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
let req = || {
|
||||
Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.body(Body::empty())
|
||||
.unwrap()
|
||||
};
|
||||
|
||||
let mut resp = client.request(req()).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 404);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
); /* check that we do not leak body */
|
||||
|
||||
ctx.garage
|
||||
.command()
|
||||
.args(["bucket", "website", "--allow", BCKT_NAME])
|
||||
.quiet()
|
||||
.expect_success_status("Could not allow website on bucket");
|
||||
|
||||
resp = client.request(req()).await.unwrap();
|
||||
assert_eq!(resp.status(), 200);
|
||||
assert_eq!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
|
||||
ctx.garage
|
||||
.command()
|
||||
.args(["bucket", "website", "--deny", BCKT_NAME])
|
||||
.quiet()
|
||||
.expect_success_status("Could not deny website on bucket");
|
||||
|
||||
resp = client.request(req()).await.unwrap();
|
||||
assert_eq!(resp.status(), 404);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
); /* check that we do not leak body */
|
||||
}
|
||||
|
||||
#[tokio::test]
|
||||
async fn test_website_s3_api() {
|
||||
const BCKT_NAME: &str = "my-cors";
|
||||
let ctx = common::context();
|
||||
let bucket = ctx.create_bucket(BCKT_NAME);
|
||||
|
||||
let data = ByteStream::from_static(BODY);
|
||||
|
||||
ctx.client
|
||||
.put_object()
|
||||
.bucket(&bucket)
|
||||
.key("site/home.html")
|
||||
.body(data)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
ctx.client
|
||||
.put_object()
|
||||
.bucket(&bucket)
|
||||
.key("err/error.html")
|
||||
.body(ByteStream::from_static(BODY_ERR))
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let conf = WebsiteConfiguration::builder()
|
||||
.index_document(IndexDocument::builder().suffix("home.html").build())
|
||||
.error_document(ErrorDocument::builder().key("err/error.html").build())
|
||||
.build();
|
||||
|
||||
ctx.client
|
||||
.put_bucket_website()
|
||||
.bucket(&bucket)
|
||||
.website_configuration(conf)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let cors = CorsConfiguration::builder()
|
||||
.cors_rules(
|
||||
CorsRule::builder()
|
||||
.id("main-rule")
|
||||
.allowed_headers("*")
|
||||
.allowed_methods("GET")
|
||||
.allowed_methods("PUT")
|
||||
.allowed_origins("*")
|
||||
.build(),
|
||||
)
|
||||
.build();
|
||||
|
||||
ctx.client
|
||||
.put_bucket_cors()
|
||||
.bucket(&bucket)
|
||||
.cors_configuration(cors)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
{
|
||||
let cors_res = ctx
|
||||
.client
|
||||
.get_bucket_cors()
|
||||
.bucket(&bucket)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
let main_rule = cors_res.cors_rules().unwrap().iter().next().unwrap();
|
||||
|
||||
assert_eq!(main_rule.id.as_ref().unwrap(), "main-rule");
|
||||
assert_eq!(
|
||||
main_rule.allowed_headers.as_ref().unwrap(),
|
||||
&vec!["*".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
main_rule.allowed_origins.as_ref().unwrap(),
|
||||
&vec!["*".to_string()]
|
||||
);
|
||||
assert_eq!(
|
||||
main_rule.allowed_methods.as_ref().unwrap(),
|
||||
&vec!["GET".to_string(), "PUT".to_string()]
|
||||
);
|
||||
}
|
||||
|
||||
let client = Client::new();
|
||||
|
||||
// Test direct requests with CORS
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/site/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.header("Origin", "https://example.com")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
assert_eq!(
|
||||
resp.headers().get("access-control-allow-origin").unwrap(),
|
||||
"*"
|
||||
);
|
||||
assert_eq!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
}
|
||||
|
||||
// Test ErrorDocument on 404
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/wrong.html",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 404);
|
||||
assert_eq!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY_ERR.as_ref()
|
||||
);
|
||||
}
|
||||
|
||||
// Test CORS with an allowed preflight request
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("OPTIONS")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/site/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.header("Origin", "https://example.com")
|
||||
.header("Access-Control-Request-Method", "PUT")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 200);
|
||||
assert_eq!(
|
||||
resp.headers().get("access-control-allow-origin").unwrap(),
|
||||
"*"
|
||||
);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
}
|
||||
|
||||
// Test CORS with a forbidden preflight request
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("OPTIONS")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/site/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.header("Origin", "https://example.com")
|
||||
.header("Access-Control-Request-Method", "DELETE")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 403);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
}
|
||||
|
||||
//@TODO test CORS on the S3 endpoint. We need to handle auth manually to check it.
|
||||
|
||||
// Delete cors
|
||||
ctx.client
|
||||
.delete_bucket_cors()
|
||||
.bucket(&bucket)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check CORS are deleted from the API
|
||||
// @FIXME check what is the expected behavior when GetBucketCors is called on a bucket without
|
||||
// any CORS.
|
||||
assert!(ctx
|
||||
.client
|
||||
.get_bucket_cors()
|
||||
.bucket(&bucket)
|
||||
.send()
|
||||
.await
|
||||
.is_err());
|
||||
|
||||
// Test CORS are not sent anymore on a previously allowed request
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("OPTIONS")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/site/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.header("Origin", "https://example.com")
|
||||
.header("Access-Control-Request-Method", "PUT")
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 403);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
}
|
||||
|
||||
// Disallow website from the API
|
||||
ctx.client
|
||||
.delete_bucket_website()
|
||||
.bucket(&bucket)
|
||||
.send()
|
||||
.await
|
||||
.unwrap();
|
||||
|
||||
// Check that the website is not served anymore
|
||||
{
|
||||
let req = Request::builder()
|
||||
.method("GET")
|
||||
.uri(format!(
|
||||
"http://127.0.0.1:{}/site/",
|
||||
common::garage::DEFAULT_PORT + 2
|
||||
))
|
||||
.header("Host", format!("{}.web.garage", BCKT_NAME))
|
||||
.body(Body::empty())
|
||||
.unwrap();
|
||||
|
||||
let mut resp = client.request(req).await.unwrap();
|
||||
|
||||
assert_eq!(resp.status(), 404);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY_ERR.as_ref()
|
||||
);
|
||||
assert_ne!(
|
||||
to_bytes(resp.body_mut()).await.unwrap().as_ref(),
|
||||
BODY.as_ref()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user