Skip to content

Commit

Permalink
Create test group for issue with URL encoding (#8)
Browse files Browse the repository at this point in the history
An issue was reported in rust-lang/crates.io#4891 related to how the
`+` character is encoded in URLs. The issue was fixed by ensuring files
are uploaded with consistent file names and by rewriting URLs with the
wrong encoding in the Content Delivery Networks. The test group will
request various URLs with different encoding from the CDNs to ensure
they work correctly.
  • Loading branch information
jdno committed Mar 19, 2024
1 parent 2bfdbfe commit 3e23cde
Show file tree
Hide file tree
Showing 6 changed files with 175 additions and 6 deletions.
4 changes: 3 additions & 1 deletion src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
//! the `Cli` struct that parses the command-line arguments and options.

use clap::Parser;
use getset::CopyGetters;

use crate::environment::Environment;

Expand All @@ -12,10 +13,11 @@ use crate::environment::Environment;
/// This command-line application can be used to run smoke tests against our infrastructure. The
/// tests confirm that the infrastructure is working as expected and that no regressions have been
/// introduced.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Parser)]
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, CopyGetters, Parser)]
pub struct Cli {
/// The environment to run the smoke tests against
#[arg(long, value_enum, default_value_t)]
#[getset(get_copy = "pub")]
env: Environment,
}

Expand Down
71 changes: 71 additions & 0 deletions src/crates/issue_4891/config.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
//! Configuration to test rust-lang/crates.io#4891

use getset::Getters;

use crate::environment::Environment;

/// Configuration to test rust-lang/crates.io#4891
///
/// The smoke tests try to access a crate with a `+` character in its version on all the different
/// Content Delivery Networks. The configuration provides a crate in the different environments that
/// can be used for the tests as well as the URLs for the CDNs.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default, Getters)]
pub struct Config {
/// The name of the crate
#[get(get = "pub")]
krate: &'static str,

/// The version with the `+` character
#[get(get = "pub")]
version: &'static str,

/// The URL for the CloudFront CDN
#[get(get = "pub")]
cloudfront_url: &'static str,

/// The URL for the Fastly CDN
#[get(get = "pub")]
fastly_url: &'static str,
}

impl Config {
/// Return the configuration for the given environment
pub fn for_env(env: Environment) -> Self {
match env {
Environment::Staging => Self {
krate: "rust-cratesio-4891",
version: "0.1.0+1",
cloudfront_url: "https://cloudfront-static.staging.crates.io",
fastly_url: "https://fastly-static.staging.crates.io",
},
Environment::Production => Self {
krate: "libgit2-sys",
version: "0.12.25+1.3.0",
cloudfront_url: "https://cloudfront-static.crates.io",
fastly_url: "https://fastly-static.crates.io",
},
}
}
}

#[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>();
}
}
74 changes: 74 additions & 0 deletions src/crates/issue_4891/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//! Encoded URLs with a + sign fail

use std::fmt::{Display, Formatter};

use crate::environment::Environment;
use crate::test::{TestGroup, TestGroupResult};

use self::config::Config;

mod config;

/// Encoded URLs with a + sign fail
///
/// An issue was reported where requests that encoded the `+` character in the URL would receive an
/// HTTP 403 Forbidden response. The cause for this issue was that the `+` character has a special
/// meaning in S3, which was not considered when uploading crates in the past. The smoke tests
/// ensure that the Content Delivery Networks correctly rewrite the URL to avoid this issue.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Issue4891 {
/// Configuration for the test group
config: Config,
}

impl Issue4891 {
/// Create a new instance of the test group
pub fn new(env: Environment) -> Self {
Self {
config: Config::for_env(env),
}
}
}

impl Display for Issue4891 {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "rust-lang/crates.io#4891")
}
}

impl TestGroup for Issue4891 {
async fn run(&self) -> TestGroupResult {
todo!()
}
}

#[cfg(test)]
mod tests {
use pretty_assertions::assert_eq;

use crate::test_utils::*;

use super::*;

#[test]
fn trait_display() {
let issue_4891 = Issue4891::new(Environment::Staging);

assert_eq!("rust-lang/crates.io#4891", issue_4891.to_string());
}

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

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

#[test]
fn trait_unpin() {
assert_unpin::<Issue4891>();
}
}
27 changes: 24 additions & 3 deletions src/crates/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,29 @@

use std::fmt::{Display, Formatter};

use crate::test::{TestSuite, TestSuiteResult};
use crate::crates::issue_4891::Issue4891;
use crate::environment::Environment;
use crate::test::{TestGroup, TestSuite, TestSuiteResult};

mod issue_4891;

/// Smoke tests for crates.io
///
/// This test suite implements the smoke tests for crates.io, mostly importantly its Content
/// Delivery Network. The tests ensure that prior bugs in the configuration are not reintroduced,
/// and that CloudFront and Fastly behave the same.
#[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Debug, Default)]
pub struct Crates {}
pub struct Crates {
/// The environment to run the tests in
env: Environment,
}

impl Crates {
/// Creates a new instance of the test suite
pub fn new(env: Environment) -> Self {
Self { env }
}
}

impl Display for Crates {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
Expand All @@ -20,9 +34,16 @@ impl Display for Crates {

impl TestSuite for Crates {
async fn run(&self) -> TestSuiteResult {
let groups = [Issue4891::new(self.env)];

let mut results = Vec::with_capacity(groups.len());
for group in &groups {
results.push(group.run().await);
}

TestSuiteResult::builder()
.name("crates.io")
.results(Vec::new())
.results(results)
.build()
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ mod test_utils;

#[tokio::main]
async fn main() {
let _cli = Cli::parse();
let cli = Cli::parse();

let tests = vec![Crates::default()];
let tests = vec![Crates::new(cli.env())];

let mut results: Vec<TestSuiteResult> = Vec::with_capacity(tests.len());
for test in &tests {
Expand Down
1 change: 1 addition & 0 deletions src/test/mod.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
//! Types that represent tests and their results

pub use self::test_group::TestGroup;
pub use self::test_group_result::TestGroupResult;
pub use self::test_result::TestResult;
pub use self::test_suite::TestSuite;
Expand Down

0 comments on commit 3e23cde

Please sign in to comment.