Skip to content

Commit

Permalink
Create smoke test for CORS headers (#19)
Browse files Browse the repository at this point in the history
When rolling out Fastly, rust-lang/crates.io#6164 reported an issue with
missing CORS headers. A new smoke test has been implemented that checks
that the correct header is set for both CloudFront and Fastly.
  • Loading branch information
jdno authored Apr 10, 2024
1 parent 755c7e9 commit 151e161
Show file tree
Hide file tree
Showing 6 changed files with 460 additions and 3 deletions.
108 changes: 108 additions & 0 deletions src/crates/crates_6164/cloudfront.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Test the CORS headers on CloudFront
use async_trait::async_trait;

use crate::crates::utils::crate_url;
use crate::test::{Test, TestResult};

use super::config::Config;
use super::request_url_and_expect_cors_header;

/// The name of the test
const NAME: &str = "CloudFront";

/// Test the CORS headers on CloudFront
///
/// This test requests a crate from CloudFront and expects the response to have the correct CORS
/// headers.
pub struct CloudFront<'a> {
/// Configuration for this test
config: &'a Config,
}

impl<'a> CloudFront<'a> {
/// Create a new instance of the test
pub fn new(config: &'a Config) -> Self {
Self { config }
}
}

#[async_trait]
impl<'a> Test for CloudFront<'a> {
async fn run(&self) -> TestResult {
let url = crate_url(
self.config.cloudfront_url(),
self.config.krate(),
self.config.version(),
);

request_url_and_expect_cors_header(NAME, &url).await
}
}

#[cfg(test)]
mod tests {
use crate::crates::crates_6164::tests::setup;
use crate::test_utils::*;

use super::*;

const KRATE: &str = "crates-6164";
const VERSION: &str = "1.0.0";

#[tokio::test]
async fn succeeds_with_cors_header() {
let (mut server, config) = setup(KRATE, VERSION).await;

let mock = server
.mock(
"GET",
format!("/crates/{KRATE}/{KRATE}-{VERSION}.crate").as_str(),
)
.with_status(200)
.with_header("Access-Control-Allow-Origin", "*")
.create();

let result = CloudFront::new(&config).run().await;

// Assert that the mock was called
mock.assert();

assert!(result.success());
}

#[tokio::test]
async fn fails_without_cors_header() {
let (mut server, config) = setup(KRATE, VERSION).await;

let mock = server
.mock(
"GET",
format!("/crates/{KRATE}/{KRATE}-{VERSION}.crate").as_str(),
)
.with_status(200)
.create();

let result = CloudFront::new(&config).run().await;

// Assert that the mock was called
mock.assert();

assert!(!result.success());
}

#[test]
fn trait_send() {
assert_send::<CloudFront>();
}

#[test]
fn trait_sync() {
assert_sync::<CloudFront>();
}

#[test]
fn trait_unpin() {
assert_unpin::<CloudFront>();
}
}
73 changes: 73 additions & 0 deletions src/crates/crates_6164/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
//! Configuration to test rust-lang/crates.io#6164
use getset::Getters;
#[cfg(test)]
use typed_builder::TypedBuilder;

use crate::environment::Environment;

/// Configuration to test rust-lang/crates.io#6164
///
/// The smoke tests try to download a crate from the different CDNs and check if the CORS headers
/// are set correctly. This requires knowing the respective base URLs, the crate, and its version.
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Getters)]
#[cfg_attr(test, derive(TypedBuilder))]
pub struct Config {
/// The name of the crate
#[getset(get = "pub")]
krate: String,

/// The version with the `+` character
#[getset(get = "pub")]
version: String,

/// The URL for the CloudFront CDN
#[getset(get = "pub")]
cloudfront_url: String,

/// The URL for the Fastly CDN
#[getset(get = "pub")]
fastly_url: String,
}

impl Config {
/// Return the configuration for the given environment
pub fn for_env(env: Environment) -> Self {
match env {
Environment::Staging => Self {
krate: "crossbeam".into(),
version: "0.2.10".into(),
cloudfront_url: "https://cloudfront-static.staging.crates.io".into(),
fastly_url: "https://fastly-static.staging.crates.io".into(),
},
Environment::Production => Self {
krate: "axum".into(),
version: "0.6.10".into(),
cloudfront_url: "https://cloudfront-static.crates.io".into(),
fastly_url: "https://fastly-static.crates.io".into(),
},
}
}
}

#[cfg(test)]
mod tests {
use crate::test_utils::*;

use super::*;

#[test]
fn trait_send() {
assert_send::<Config>();
}

#[test]
fn trait_sync() {
assert_sync::<Config>();
}

#[test]
fn trait_unpin() {
assert_unpin::<Config>();
}
}
108 changes: 108 additions & 0 deletions src/crates/crates_6164/fastly.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
//! Test the CORS headers on Fastly
use async_trait::async_trait;

use crate::crates::utils::crate_url;
use crate::test::{Test, TestResult};

use super::config::Config;
use super::request_url_and_expect_cors_header;

/// The name of the test
const NAME: &str = "Fastly";

/// Test the CORS headers on Fastly
///
/// This test requests a crate from Fastly and expects the response to have the correct CORS
/// headers.
pub struct Fastly<'a> {
/// Configuration for this test
config: &'a Config,
}

impl<'a> Fastly<'a> {
/// Create a new instance of the test
pub fn new(config: &'a Config) -> Self {
Self { config }
}
}

#[async_trait]
impl<'a> Test for Fastly<'a> {
async fn run(&self) -> TestResult {
let url = crate_url(
self.config.fastly_url(),
self.config.krate(),
self.config.version(),
);

request_url_and_expect_cors_header(NAME, &url).await
}
}

#[cfg(test)]
mod tests {
use crate::crates::crates_6164::tests::setup;
use crate::test_utils::*;

use super::*;

const KRATE: &str = "crates-6164";
const VERSION: &str = "1.0.0";

#[tokio::test]
async fn succeeds_with_cors_header() {
let (mut server, config) = setup(KRATE, VERSION).await;

let mock = server
.mock(
"GET",
format!("/crates/{KRATE}/{KRATE}-{VERSION}.crate").as_str(),
)
.with_status(200)
.with_header("Access-Control-Allow-Origin", "*")
.create();

let result = Fastly::new(&config).run().await;

// Assert that the mock was called
mock.assert();

assert!(result.success());
}

#[tokio::test]
async fn fails_without_cors_header() {
let (mut server, config) = setup(KRATE, VERSION).await;

let mock = server
.mock(
"GET",
format!("/crates/{KRATE}/{KRATE}-{VERSION}.crate").as_str(),
)
.with_status(200)
.create();

let result = Fastly::new(&config).run().await;

// Assert that the mock was called
mock.assert();

assert!(!result.success());
}

#[test]
fn trait_send() {
assert_send::<Fastly>();
}

#[test]
fn trait_sync() {
assert_sync::<Fastly>();
}

#[test]
fn trait_unpin() {
assert_unpin::<Fastly>();
}
}
Loading

0 comments on commit 151e161

Please sign in to comment.