From 89e105691cc71e4095de64614bd5f27de8d82dab Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 17:59:05 +0200 Subject: [PATCH 1/7] Convert `tests/builders.rs` into directory module --- src/tests/{builders.rs => builders/mod.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/tests/{builders.rs => builders/mod.rs} (100%) diff --git a/src/tests/builders.rs b/src/tests/builders/mod.rs similarity index 100% rename from src/tests/builders.rs rename to src/tests/builders/mod.rs From 4b2d26ae8621c1dbb01d049fa5f9d80a5a1ba523 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 18:10:27 +0200 Subject: [PATCH 2/7] tests/builders: Split into dedicated files --- src/tests/builders/dependency.rs | 63 ++++ src/tests/builders/krate.rs | 156 ++++++++ src/tests/builders/mod.rs | 592 +------------------------------ src/tests/builders/publish.rs | 236 ++++++++++++ src/tests/builders/version.rs | 142 ++++++++ 5 files changed, 606 insertions(+), 583 deletions(-) create mode 100644 src/tests/builders/dependency.rs create mode 100644 src/tests/builders/krate.rs create mode 100644 src/tests/builders/publish.rs create mode 100644 src/tests/builders/version.rs diff --git a/src/tests/builders/dependency.rs b/src/tests/builders/dependency.rs new file mode 100644 index 00000000000..e1c87151879 --- /dev/null +++ b/src/tests/builders/dependency.rs @@ -0,0 +1,63 @@ +use cargo_registry::views::krate_publish as u; + +/// A builder for constructing a dependency of another crate. +pub struct DependencyBuilder { + name: String, + registry: Option, + explicit_name_in_toml: Option, + version_req: u::EncodableCrateVersionReq, +} + +impl DependencyBuilder { + /// Create a dependency on the crate with the given name. + pub fn new(name: &str) -> Self { + DependencyBuilder { + name: name.to_string(), + registry: None, + explicit_name_in_toml: None, + version_req: u::EncodableCrateVersionReq(semver::VersionReq::parse(">= 0").unwrap()), + } + } + + /// Rename this dependency. + pub fn rename(mut self, new_name: &str) -> Self { + self.explicit_name_in_toml = Some(u::EncodableCrateName(new_name.to_string())); + self + } + + /// Set an alternative registry for this dependency. + pub fn registry(mut self, registry: &str) -> Self { + self.registry = Some(registry.to_string()); + self + } + + /// Set the version requirement for this dependency. + /// + /// # Panics + /// + /// Panics if the `version_req` string specified isn't a valid `semver::VersionReq`. + #[track_caller] + pub fn version_req(mut self, version_req: &str) -> Self { + self.version_req = u::EncodableCrateVersionReq( + semver::VersionReq::parse(version_req) + .expect("version req isn't a valid semver::VersionReq"), + ); + self + } + + /// Consume this builder to create a `u::CrateDependency`. If the dependent crate doesn't + /// already exist, publishing a crate with this dependency will fail. + pub fn build(self) -> u::EncodableCrateDependency { + u::EncodableCrateDependency { + name: u::EncodableCrateName(self.name), + optional: false, + default_features: true, + features: Vec::new(), + version_req: self.version_req, + target: None, + kind: None, + explicit_name_in_toml: self.explicit_name_in_toml, + registry: self.registry, + } + } +} diff --git a/src/tests/builders/krate.rs b/src/tests/builders/krate.rs new file mode 100644 index 00000000000..e33e7fba343 --- /dev/null +++ b/src/tests/builders/krate.rs @@ -0,0 +1,156 @@ +use cargo_registry::{ + models::{Crate, Keyword, NewCrate}, + schema::{crates, version_downloads}, + util::errors::AppResult, +}; + +use diesel::prelude::*; + +use super::VersionBuilder; + +/// A builder to create crate records for the purpose of inserting directly into the database. +/// If you want to test logic that happens as part of a publish request, use `PublishBuilder` +/// instead. +pub struct CrateBuilder<'a> { + owner_id: i32, + krate: NewCrate<'a>, + downloads: Option, + recent_downloads: Option, + versions: Vec>, + keywords: Vec<&'a str>, +} + +impl<'a> CrateBuilder<'a> { + /// Create a new instance with the given crate name and owner. If the owner with the given ID + /// doesn't exist in the database, `expect_build` will fail. + pub fn new(name: &str, owner_id: i32) -> CrateBuilder<'_> { + CrateBuilder { + owner_id, + krate: NewCrate { + name, + ..NewCrate::default() + }, + downloads: None, + recent_downloads: None, + versions: Vec::new(), + keywords: Vec::new(), + } + } + + /// Sets the crate's `description` value. + pub fn description(mut self, description: &'a str) -> Self { + self.krate.description = Some(description); + self + } + + /// Sets the crate's `documentation` URL. + pub fn documentation(mut self, documentation: &'a str) -> Self { + self.krate.documentation = Some(documentation); + self + } + + /// Sets the crate's `homepage` URL. + pub fn homepage(mut self, homepage: &'a str) -> Self { + self.krate.homepage = Some(homepage); + self + } + + /// Sets the crate's `readme` content. + pub fn readme(mut self, readme: &'a str) -> Self { + self.krate.readme = Some(readme); + self + } + + /// Sets the crate's `max_upload_size` override value. + pub fn max_upload_size(mut self, max_upload_size: i32) -> Self { + self.krate.max_upload_size = Some(max_upload_size); + self + } + + /// Sets the crate's number of downloads that happened more than 90 days ago. The total + /// number of downloads for this crate will be this plus the number of recent downloads. + pub fn downloads(mut self, downloads: i32) -> Self { + self.downloads = Some(downloads); + self + } + + /// Sets the crate's number of downloads in the last 90 days. The total number of downloads + /// for this crate will be this plus the number of downloads set with the `downloads` method. + pub fn recent_downloads(mut self, recent_downloads: i32) -> Self { + self.recent_downloads = Some(recent_downloads); + self + } + + /// Adds a version record to be associated with the crate record when the crate record is + /// built. + pub fn version>>(mut self, version: T) -> Self { + self.versions.push(version.into()); + self + } + + /// Adds a keyword to the crate. + pub fn keyword(mut self, keyword: &'a str) -> Self { + self.keywords.push(keyword); + self + } + + pub fn build(mut self, connection: &PgConnection) -> AppResult { + use diesel::{insert_into, select, update}; + + let mut krate = self + .krate + .create_or_update(connection, self.owner_id, None)?; + + // Since we are using `NewCrate`, we can't set all the + // crate properties in a single DB call. + + if let Some(downloads) = self.downloads { + krate = update(&krate) + .set(crates::downloads.eq(downloads)) + .returning(cargo_registry::models::krate::ALL_COLUMNS) + .get_result(connection)?; + } + + if self.versions.is_empty() { + self.versions.push(VersionBuilder::new("0.99.0")); + } + + let mut last_version_id = 0; + for version_builder in self.versions { + last_version_id = version_builder + .build(krate.id, self.owner_id, connection)? + .id; + } + + if let Some(downloads) = self.recent_downloads { + insert_into(version_downloads::table) + .values(( + version_downloads::version_id.eq(last_version_id), + version_downloads::downloads.eq(downloads), + )) + .execute(connection)?; + + no_arg_sql_function!(refresh_recent_crate_downloads, ()); + select(refresh_recent_crate_downloads).execute(connection)?; + } + + if !self.keywords.is_empty() { + Keyword::update_crate(connection, &krate, &self.keywords)?; + } + + Ok(krate) + } + + /// Consumes the builder and creates the crate record in the database. + /// + /// # Panics + /// + /// Panics (and fails the test) if any part of inserting the crate record fails. + #[track_caller] + pub fn expect_build(self, connection: &PgConnection) -> Crate { + let name = self.krate.name; + self.build(connection).unwrap_or_else(|e| { + panic!("Unable to create crate {}: {:?}", name, e); + }) + } +} diff --git a/src/tests/builders/mod.rs b/src/tests/builders/mod.rs index 5a688b764bd..dd5312be3d1 100644 --- a/src/tests/builders/mod.rs +++ b/src/tests/builders/mod.rs @@ -1,585 +1,11 @@ //! Structs using the builder pattern that make it easier to create records in tests. -use cargo_registry::{ - models::{Crate, Keyword, NewCrate, NewVersion, Version}, - schema::{crates, dependencies, version_downloads, versions}, - util::errors::AppResult, - views::krate_publish as u, -}; -use std::{collections::HashMap, io::Read}; - -use diesel::prelude::*; -use flate2::{write::GzEncoder, Compression}; - -/// A builder to create version records for the purpose of inserting directly into the database. -pub struct VersionBuilder<'a> { - num: semver::Version, - license: Option<&'a str>, - license_file: Option<&'a str>, - features: HashMap>, - dependencies: Vec<(i32, Option<&'static str>)>, - yanked: bool, - size: i32, -} - -impl<'a> VersionBuilder<'a> { - /// Creates a VersionBuilder from a string slice `num` representing the version's number. - /// - /// # Panics - /// - /// Panics if `num` cannot be parsed as a valid `semver::Version`. - #[track_caller] - pub fn new(num: &str) -> Self { - let num = semver::Version::parse(num).unwrap_or_else(|e| { - panic!("The version {} is not valid: {}", num, e); - }); - - VersionBuilder { - num, - license: None, - license_file: None, - features: HashMap::new(), - dependencies: Vec::new(), - yanked: false, - size: 0, - } - } - - /// Sets the version's `license` value. - pub fn license(mut self, license: Option<&'a str>) -> Self { - self.license = license; - self - } - - /// Adds a dependency to this version. - pub fn dependency(mut self, dependency: &Crate, target: Option<&'static str>) -> Self { - self.dependencies.push((dependency.id, target)); - self - } - - /// Sets the version's `yanked` value. - pub fn yanked(self, yanked: bool) -> Self { - Self { yanked, ..self } - } - - /// Sets the version's size. - pub fn size(mut self, size: i32) -> Self { - self.size = size; - self - } - - fn build( - self, - crate_id: i32, - published_by: i32, - connection: &PgConnection, - ) -> AppResult { - use diesel::{insert_into, update}; - - let license = match self.license { - Some(license) => Some(license.to_owned()), - None => None, - }; - - let mut vers = NewVersion::new( - crate_id, - &self.num, - &self.features, - license, - self.license_file, - self.size, - published_by, - )? - .save(connection, &[], "someone@example.com")?; - - if self.yanked { - vers = update(&vers) - .set(versions::yanked.eq(true)) - .get_result(connection)?; - } - - let new_deps = self - .dependencies - .into_iter() - .map(|(crate_id, target)| { - ( - dependencies::version_id.eq(vers.id), - dependencies::req.eq(">= 0"), - dependencies::crate_id.eq(crate_id), - dependencies::target.eq(target), - dependencies::optional.eq(false), - dependencies::default_features.eq(false), - dependencies::features.eq(Vec::::new()), - ) - }) - .collect::>(); - insert_into(dependencies::table) - .values(&new_deps) - .execute(connection)?; - - Ok(vers) - } - - /// Consumes the builder and creates the version record in the database. - /// - /// # Panics - /// - /// Panics (and fails the test) if any part of inserting the version record fails. - #[track_caller] - pub fn expect_build( - self, - crate_id: i32, - published_by: i32, - connection: &PgConnection, - ) -> Version { - self.build(crate_id, published_by, connection) - .unwrap_or_else(|e| { - panic!("Unable to create version: {:?}", e); - }) - } -} - -impl<'a> From<&'a str> for VersionBuilder<'a> { - fn from(num: &'a str) -> Self { - VersionBuilder::new(num) - } -} - -/// A builder to create crate records for the purpose of inserting directly into the database. -/// If you want to test logic that happens as part of a publish request, use `PublishBuilder` -/// instead. -pub struct CrateBuilder<'a> { - owner_id: i32, - krate: NewCrate<'a>, - downloads: Option, - recent_downloads: Option, - versions: Vec>, - keywords: Vec<&'a str>, -} - -impl<'a> CrateBuilder<'a> { - /// Create a new instance with the given crate name and owner. If the owner with the given ID - /// doesn't exist in the database, `expect_build` will fail. - pub fn new(name: &str, owner_id: i32) -> CrateBuilder<'_> { - CrateBuilder { - owner_id, - krate: NewCrate { - name, - ..NewCrate::default() - }, - downloads: None, - recent_downloads: None, - versions: Vec::new(), - keywords: Vec::new(), - } - } - - /// Sets the crate's `description` value. - pub fn description(mut self, description: &'a str) -> Self { - self.krate.description = Some(description); - self - } - - /// Sets the crate's `documentation` URL. - pub fn documentation(mut self, documentation: &'a str) -> Self { - self.krate.documentation = Some(documentation); - self - } - - /// Sets the crate's `homepage` URL. - pub fn homepage(mut self, homepage: &'a str) -> Self { - self.krate.homepage = Some(homepage); - self - } - - /// Sets the crate's `readme` content. - pub fn readme(mut self, readme: &'a str) -> Self { - self.krate.readme = Some(readme); - self - } - - /// Sets the crate's `max_upload_size` override value. - pub fn max_upload_size(mut self, max_upload_size: i32) -> Self { - self.krate.max_upload_size = Some(max_upload_size); - self - } - - /// Sets the crate's number of downloads that happened more than 90 days ago. The total - /// number of downloads for this crate will be this plus the number of recent downloads. - pub fn downloads(mut self, downloads: i32) -> Self { - self.downloads = Some(downloads); - self - } - - /// Sets the crate's number of downloads in the last 90 days. The total number of downloads - /// for this crate will be this plus the number of downloads set with the `downloads` method. - pub fn recent_downloads(mut self, recent_downloads: i32) -> Self { - self.recent_downloads = Some(recent_downloads); - self - } - - /// Adds a version record to be associated with the crate record when the crate record is - /// built. - pub fn version>>(mut self, version: T) -> Self { - self.versions.push(version.into()); - self - } - - /// Adds a keyword to the crate. - pub fn keyword(mut self, keyword: &'a str) -> Self { - self.keywords.push(keyword); - self - } - - fn build(mut self, connection: &PgConnection) -> AppResult { - use diesel::{insert_into, select, update}; - - let mut krate = self - .krate - .create_or_update(connection, self.owner_id, None)?; - - // Since we are using `NewCrate`, we can't set all the - // crate properties in a single DB call. - - if let Some(downloads) = self.downloads { - krate = update(&krate) - .set(crates::downloads.eq(downloads)) - .returning(cargo_registry::models::krate::ALL_COLUMNS) - .get_result(connection)?; - } - - if self.versions.is_empty() { - self.versions.push(VersionBuilder::new("0.99.0")); - } - - let mut last_version_id = 0; - for version_builder in self.versions { - last_version_id = version_builder - .build(krate.id, self.owner_id, connection)? - .id; - } - - if let Some(downloads) = self.recent_downloads { - insert_into(version_downloads::table) - .values(( - version_downloads::version_id.eq(last_version_id), - version_downloads::downloads.eq(downloads), - )) - .execute(connection)?; - - no_arg_sql_function!(refresh_recent_crate_downloads, ()); - select(refresh_recent_crate_downloads).execute(connection)?; - } - - if !self.keywords.is_empty() { - Keyword::update_crate(connection, &krate, &self.keywords)?; - } - - Ok(krate) - } - - /// Consumes the builder and creates the crate record in the database. - /// - /// # Panics - /// - /// Panics (and fails the test) if any part of inserting the crate record fails. - #[track_caller] - pub fn expect_build(self, connection: &PgConnection) -> Crate { - let name = self.krate.name; - self.build(connection).unwrap_or_else(|e| { - panic!("Unable to create crate {}: {:?}", name, e); - }) - } -} - -lazy_static! { - // The bytes of an empty tarball is not an empty vector of bytes because of tarball headers. - // Unless files are added to a PublishBuilder, the `.crate` tarball that gets uploaded - // will be empty, so precompute the empty tarball bytes to use as a default. - static ref EMPTY_TARBALL_BYTES: Vec = { - let mut empty_tarball = vec![]; - { - let mut ar = - tar::Builder::new(GzEncoder::new(&mut empty_tarball, Compression::default())); - t!(ar.finish()); - } - empty_tarball - }; -} - -/// A builder for constructing a crate for the purposes of testing publishing. If you only need -/// a crate to exist and don't need to test behavior caused by the publish request, inserting -/// a crate into the database directly by using CrateBuilder will be faster. -pub struct PublishBuilder { - pub krate_name: String, - version: semver::Version, - tarball: Vec, - deps: Vec, - desc: Option, - readme: Option, - doc_url: Option, - keywords: Vec, - categories: Vec, - badges: HashMap>, - license: Option, - license_file: Option, - authors: Vec, -} - -impl PublishBuilder { - /// Create a request to publish a crate with the given name, version 1.0.0, and no files - /// in its tarball. - pub fn new(krate_name: &str) -> Self { - PublishBuilder { - krate_name: krate_name.into(), - version: semver::Version::parse("1.0.0").unwrap(), - tarball: EMPTY_TARBALL_BYTES.to_vec(), - deps: vec![], - desc: Some("description".to_string()), - readme: None, - doc_url: None, - keywords: vec![], - categories: vec![], - badges: HashMap::new(), - license: Some("MIT".to_string()), - license_file: None, - authors: vec!["foo".to_string()], - } - } - - /// Set the version of the crate being published to something other than the default of 1.0.0. - pub fn version(mut self, version: &str) -> Self { - self.version = semver::Version::parse(version).unwrap(); - self - } - - /// Set the files in the crate's tarball. - pub fn files(self, files: &[(&str, &[u8])]) -> Self { - let mut slices = files.iter().map(|p| p.1).collect::>(); - let mut files = files - .iter() - .zip(&mut slices) - .map(|(&(name, _), data)| { - let len = data.len() as u64; - (name, data as &mut dyn Read, len) - }) - .collect::>(); - - self.files_with_io(&mut files) - } - - /// Set the tarball from a Read trait object - pub fn files_with_io(mut self, files: &mut [(&str, &mut dyn Read, u64)]) -> Self { - let mut tarball = Vec::new(); - { - let mut ar = tar::Builder::new(GzEncoder::new(&mut tarball, Compression::default())); - for &mut (name, ref mut data, size) in files { - let mut header = tar::Header::new_gnu(); - t!(header.set_path(name)); - header.set_size(size); - header.set_cksum(); - t!(ar.append(&header, data)); - } - t!(ar.finish()); - } - - self.tarball = tarball; - self - } - - /// Set the tarball directly to the given Vec of bytes - pub fn tarball(mut self, tarball: Vec) -> Self { - self.tarball = tarball; - self - } - - /// Add a dependency to this crate. Make sure the dependency already exists in the - /// database or publish will fail. - pub fn dependency(mut self, dep: DependencyBuilder) -> Self { - self.deps.push(dep.build()); - self - } - - /// Set the description of this crate - pub fn description(mut self, description: &str) -> Self { - self.desc = Some(description.to_string()); - self - } - - /// Unset the description of this crate. Publish will fail unless description is reset. - pub fn unset_description(mut self) -> Self { - self.desc = None; - self - } - - /// Set the readme of this crate - pub fn readme(mut self, readme: &str) -> Self { - self.readme = Some(readme.to_string()); - self - } - - /// Set the documentation URL of this crate - pub fn documentation(mut self, documentation: &str) -> Self { - self.doc_url = Some(documentation.to_string()); - self - } - - /// Add a keyword to this crate. - pub fn keyword(mut self, keyword: &str) -> Self { - self.keywords.push(keyword.into()); - self - } - - /// Add a category to this crate. Make sure the category already exists in the - /// database or it will be ignored. - pub fn category(mut self, slug: &str) -> Self { - self.categories.push(slug.into()); - self - } - - /// Add badges to this crate. - pub fn badges(mut self, badges: HashMap>) -> Self { - self.badges = badges; - self - } - - /// Remove the license from this crate. Publish will fail unless license or license file is set. - pub fn unset_license(mut self) -> Self { - self.license = None; - self - } - - /// Set the license file for this crate - pub fn license_file(mut self, license_file: &str) -> Self { - self.license_file = Some(license_file.into()); - self - } - - /// Add an author to this crate - pub fn author(mut self, author: &str) -> Self { - self.authors.push(author.into()); - self - } - - /// Remove the authors from this crate. Publish will fail unless authors are reset. - pub fn unset_authors(mut self) -> Self { - self.authors = vec![]; - self - } - - /// Consume this builder to make the Put request body - pub fn body(self) -> Vec { - let new_crate = u::EncodableCrateUpload { - name: u::EncodableCrateName(self.krate_name.clone()), - vers: u::EncodableCrateVersion(self.version), - features: HashMap::new(), - deps: self.deps, - authors: self.authors, - description: self.desc, - homepage: None, - documentation: self.doc_url, - readme: self.readme, - readme_file: None, - keywords: u::EncodableKeywordList( - self.keywords.into_iter().map(u::EncodableKeyword).collect(), - ), - categories: u::EncodableCategoryList( - self.categories - .into_iter() - .map(u::EncodableCategory) - .collect(), - ), - license: self.license, - license_file: self.license_file, - repository: None, - badges: Some(self.badges), - links: None, - }; - - let json = serde_json::to_string(&new_crate).unwrap(); - let mut body = Vec::new(); - body.extend( - [ - json.len() as u8, - (json.len() >> 8) as u8, - (json.len() >> 16) as u8, - (json.len() >> 24) as u8, - ] - .iter() - .cloned(), - ); - body.extend(json.as_bytes().iter().cloned()); - - let tarball = &self.tarball; - body.extend(&[ - tarball.len() as u8, - (tarball.len() >> 8) as u8, - (tarball.len() >> 16) as u8, - (tarball.len() >> 24) as u8, - ]); - body.extend(tarball); - body - } -} - -/// A builder for constructing a dependency of another crate. -pub struct DependencyBuilder { - name: String, - registry: Option, - explicit_name_in_toml: Option, - version_req: u::EncodableCrateVersionReq, -} - -impl DependencyBuilder { - /// Create a dependency on the crate with the given name. - pub fn new(name: &str) -> Self { - DependencyBuilder { - name: name.to_string(), - registry: None, - explicit_name_in_toml: None, - version_req: u::EncodableCrateVersionReq(semver::VersionReq::parse(">= 0").unwrap()), - } - } - - /// Rename this dependency. - pub fn rename(mut self, new_name: &str) -> Self { - self.explicit_name_in_toml = Some(u::EncodableCrateName(new_name.to_string())); - self - } - - /// Set an alternative registry for this dependency. - pub fn registry(mut self, registry: &str) -> Self { - self.registry = Some(registry.to_string()); - self - } - - /// Set the version requirement for this dependency. - /// - /// # Panics - /// - /// Panics if the `version_req` string specified isn't a valid `semver::VersionReq`. - #[track_caller] - pub fn version_req(mut self, version_req: &str) -> Self { - self.version_req = u::EncodableCrateVersionReq( - semver::VersionReq::parse(version_req) - .expect("version req isn't a valid semver::VersionReq"), - ); - self - } - - /// Consume this builder to create a `u::CrateDependency`. If the dependent crate doesn't - /// already exist, publishing a crate with this dependency will fail. - fn build(self) -> u::EncodableCrateDependency { - u::EncodableCrateDependency { - name: u::EncodableCrateName(self.name), - optional: false, - default_features: true, - features: Vec::new(), - version_req: self.version_req, - target: None, - kind: None, - explicit_name_in_toml: self.explicit_name_in_toml, - registry: self.registry, - } - } -} +mod dependency; +mod krate; +mod publish; +mod version; + +pub use dependency::DependencyBuilder; +pub use krate::CrateBuilder; +pub use publish::PublishBuilder; +pub use version::VersionBuilder; diff --git a/src/tests/builders/publish.rs b/src/tests/builders/publish.rs new file mode 100644 index 00000000000..48bbcd9d6fe --- /dev/null +++ b/src/tests/builders/publish.rs @@ -0,0 +1,236 @@ +use cargo_registry::views::krate_publish as u; +use std::{collections::HashMap, io::Read}; + +use flate2::{write::GzEncoder, Compression}; + +use super::DependencyBuilder; + +lazy_static! { + // The bytes of an empty tarball is not an empty vector of bytes because of tarball headers. + // Unless files are added to a PublishBuilder, the `.crate` tarball that gets uploaded + // will be empty, so precompute the empty tarball bytes to use as a default. + static ref EMPTY_TARBALL_BYTES: Vec = { + let mut empty_tarball = vec![]; + { + let mut ar = + tar::Builder::new(GzEncoder::new(&mut empty_tarball, Compression::default())); + t!(ar.finish()); + } + empty_tarball + }; +} + +/// A builder for constructing a crate for the purposes of testing publishing. If you only need +/// a crate to exist and don't need to test behavior caused by the publish request, inserting +/// a crate into the database directly by using CrateBuilder will be faster. +pub struct PublishBuilder { + pub krate_name: String, + version: semver::Version, + tarball: Vec, + deps: Vec, + desc: Option, + readme: Option, + doc_url: Option, + keywords: Vec, + categories: Vec, + badges: HashMap>, + license: Option, + license_file: Option, + authors: Vec, +} + +impl PublishBuilder { + /// Create a request to publish a crate with the given name, version 1.0.0, and no files + /// in its tarball. + pub fn new(krate_name: &str) -> Self { + PublishBuilder { + krate_name: krate_name.into(), + version: semver::Version::parse("1.0.0").unwrap(), + tarball: EMPTY_TARBALL_BYTES.to_vec(), + deps: vec![], + desc: Some("description".to_string()), + readme: None, + doc_url: None, + keywords: vec![], + categories: vec![], + badges: HashMap::new(), + license: Some("MIT".to_string()), + license_file: None, + authors: vec!["foo".to_string()], + } + } + + /// Set the version of the crate being published to something other than the default of 1.0.0. + pub fn version(mut self, version: &str) -> Self { + self.version = semver::Version::parse(version).unwrap(); + self + } + + /// Set the files in the crate's tarball. + pub fn files(self, files: &[(&str, &[u8])]) -> Self { + let mut slices = files.iter().map(|p| p.1).collect::>(); + let mut files = files + .iter() + .zip(&mut slices) + .map(|(&(name, _), data)| { + let len = data.len() as u64; + (name, data as &mut dyn Read, len) + }) + .collect::>(); + + self.files_with_io(&mut files) + } + + /// Set the tarball from a Read trait object + pub fn files_with_io(mut self, files: &mut [(&str, &mut dyn Read, u64)]) -> Self { + let mut tarball = Vec::new(); + { + let mut ar = tar::Builder::new(GzEncoder::new(&mut tarball, Compression::default())); + for &mut (name, ref mut data, size) in files { + let mut header = tar::Header::new_gnu(); + t!(header.set_path(name)); + header.set_size(size); + header.set_cksum(); + t!(ar.append(&header, data)); + } + t!(ar.finish()); + } + + self.tarball = tarball; + self + } + + /// Set the tarball directly to the given Vec of bytes + pub fn tarball(mut self, tarball: Vec) -> Self { + self.tarball = tarball; + self + } + + /// Add a dependency to this crate. Make sure the dependency already exists in the + /// database or publish will fail. + pub fn dependency(mut self, dep: DependencyBuilder) -> Self { + self.deps.push(dep.build()); + self + } + + /// Set the description of this crate + pub fn description(mut self, description: &str) -> Self { + self.desc = Some(description.to_string()); + self + } + + /// Unset the description of this crate. Publish will fail unless description is reset. + pub fn unset_description(mut self) -> Self { + self.desc = None; + self + } + + /// Set the readme of this crate + pub fn readme(mut self, readme: &str) -> Self { + self.readme = Some(readme.to_string()); + self + } + + /// Set the documentation URL of this crate + pub fn documentation(mut self, documentation: &str) -> Self { + self.doc_url = Some(documentation.to_string()); + self + } + + /// Add a keyword to this crate. + pub fn keyword(mut self, keyword: &str) -> Self { + self.keywords.push(keyword.into()); + self + } + + /// Add a category to this crate. Make sure the category already exists in the + /// database or it will be ignored. + pub fn category(mut self, slug: &str) -> Self { + self.categories.push(slug.into()); + self + } + + /// Add badges to this crate. + pub fn badges(mut self, badges: HashMap>) -> Self { + self.badges = badges; + self + } + + /// Remove the license from this crate. Publish will fail unless license or license file is set. + pub fn unset_license(mut self) -> Self { + self.license = None; + self + } + + /// Set the license file for this crate + pub fn license_file(mut self, license_file: &str) -> Self { + self.license_file = Some(license_file.into()); + self + } + + /// Add an author to this crate + pub fn author(mut self, author: &str) -> Self { + self.authors.push(author.into()); + self + } + + /// Remove the authors from this crate. Publish will fail unless authors are reset. + pub fn unset_authors(mut self) -> Self { + self.authors = vec![]; + self + } + + /// Consume this builder to make the Put request body + pub fn body(self) -> Vec { + let new_crate = u::EncodableCrateUpload { + name: u::EncodableCrateName(self.krate_name.clone()), + vers: u::EncodableCrateVersion(self.version), + features: HashMap::new(), + deps: self.deps, + authors: self.authors, + description: self.desc, + homepage: None, + documentation: self.doc_url, + readme: self.readme, + readme_file: None, + keywords: u::EncodableKeywordList( + self.keywords.into_iter().map(u::EncodableKeyword).collect(), + ), + categories: u::EncodableCategoryList( + self.categories + .into_iter() + .map(u::EncodableCategory) + .collect(), + ), + license: self.license, + license_file: self.license_file, + repository: None, + badges: Some(self.badges), + links: None, + }; + + let json = serde_json::to_string(&new_crate).unwrap(); + let mut body = Vec::new(); + body.extend( + [ + json.len() as u8, + (json.len() >> 8) as u8, + (json.len() >> 16) as u8, + (json.len() >> 24) as u8, + ] + .iter() + .cloned(), + ); + body.extend(json.as_bytes().iter().cloned()); + + let tarball = &self.tarball; + body.extend(&[ + tarball.len() as u8, + (tarball.len() >> 8) as u8, + (tarball.len() >> 16) as u8, + (tarball.len() >> 24) as u8, + ]); + body.extend(tarball); + body + } +} diff --git a/src/tests/builders/version.rs b/src/tests/builders/version.rs new file mode 100644 index 00000000000..07c1922cbe5 --- /dev/null +++ b/src/tests/builders/version.rs @@ -0,0 +1,142 @@ +use cargo_registry::{ + models::{Crate, NewVersion, Version}, + schema::{dependencies, versions}, + util::errors::AppResult, +}; +use std::collections::HashMap; + +use diesel::prelude::*; + +/// A builder to create version records for the purpose of inserting directly into the database. +pub struct VersionBuilder<'a> { + num: semver::Version, + license: Option<&'a str>, + license_file: Option<&'a str>, + features: HashMap>, + dependencies: Vec<(i32, Option<&'static str>)>, + yanked: bool, + size: i32, +} + +impl<'a> VersionBuilder<'a> { + /// Creates a VersionBuilder from a string slice `num` representing the version's number. + /// + /// # Panics + /// + /// Panics if `num` cannot be parsed as a valid `semver::Version`. + #[track_caller] + pub fn new(num: &str) -> Self { + let num = semver::Version::parse(num).unwrap_or_else(|e| { + panic!("The version {} is not valid: {}", num, e); + }); + + VersionBuilder { + num, + license: None, + license_file: None, + features: HashMap::new(), + dependencies: Vec::new(), + yanked: false, + size: 0, + } + } + + /// Sets the version's `license` value. + pub fn license(mut self, license: Option<&'a str>) -> Self { + self.license = license; + self + } + + /// Adds a dependency to this version. + pub fn dependency(mut self, dependency: &Crate, target: Option<&'static str>) -> Self { + self.dependencies.push((dependency.id, target)); + self + } + + /// Sets the version's `yanked` value. + pub fn yanked(self, yanked: bool) -> Self { + Self { yanked, ..self } + } + + /// Sets the version's size. + pub fn size(mut self, size: i32) -> Self { + self.size = size; + self + } + + pub fn build( + self, + crate_id: i32, + published_by: i32, + connection: &PgConnection, + ) -> AppResult { + use diesel::{insert_into, update}; + + let license = match self.license { + Some(license) => Some(license.to_owned()), + None => None, + }; + + let mut vers = NewVersion::new( + crate_id, + &self.num, + &self.features, + license, + self.license_file, + self.size, + published_by, + )? + .save(connection, &[], "someone@example.com")?; + + if self.yanked { + vers = update(&vers) + .set(versions::yanked.eq(true)) + .get_result(connection)?; + } + + let new_deps = self + .dependencies + .into_iter() + .map(|(crate_id, target)| { + ( + dependencies::version_id.eq(vers.id), + dependencies::req.eq(">= 0"), + dependencies::crate_id.eq(crate_id), + dependencies::target.eq(target), + dependencies::optional.eq(false), + dependencies::default_features.eq(false), + dependencies::features.eq(Vec::::new()), + ) + }) + .collect::>(); + insert_into(dependencies::table) + .values(&new_deps) + .execute(connection)?; + + Ok(vers) + } + + /// Consumes the builder and creates the version record in the database. + /// + /// # Panics + /// + /// Panics (and fails the test) if any part of inserting the version record fails. + #[track_caller] + pub fn expect_build( + self, + crate_id: i32, + published_by: i32, + connection: &PgConnection, + ) -> Version { + self.build(crate_id, published_by, connection) + .unwrap_or_else(|e| { + panic!("Unable to create version: {:?}", e); + }) + } +} + +impl<'a> From<&'a str> for VersionBuilder<'a> { + fn from(num: &'a str) -> Self { + VersionBuilder::new(num) + } +} From b0e305a8560cc74cac5338e2d4f1c714c1c15f42 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 18:23:09 +0200 Subject: [PATCH 3/7] builders/version: Add `created_at` option --- src/tests/builders/version.rs | 15 +++++++++++++++ src/tests/krate.rs | 10 ++++------ 2 files changed, 19 insertions(+), 6 deletions(-) diff --git a/src/tests/builders/version.rs b/src/tests/builders/version.rs index 07c1922cbe5..82d20156bf7 100644 --- a/src/tests/builders/version.rs +++ b/src/tests/builders/version.rs @@ -5,10 +5,12 @@ use cargo_registry::{ }; use std::collections::HashMap; +use chrono::NaiveDateTime; use diesel::prelude::*; /// A builder to create version records for the purpose of inserting directly into the database. pub struct VersionBuilder<'a> { + created_at: Option, num: semver::Version, license: Option<&'a str>, license_file: Option<&'a str>, @@ -31,6 +33,7 @@ impl<'a> VersionBuilder<'a> { }); VersionBuilder { + created_at: None, num, license: None, license_file: None, @@ -41,6 +44,12 @@ impl<'a> VersionBuilder<'a> { } } + /// Sets the version's `created_at` value. + pub fn created_at(mut self, created_at: NaiveDateTime) -> Self { + self.created_at = Some(created_at); + self + } + /// Sets the version's `license` value. pub fn license(mut self, license: Option<&'a str>) -> Self { self.license = license; @@ -94,6 +103,12 @@ impl<'a> VersionBuilder<'a> { .get_result(connection)?; } + if let Some(created_at) = self.created_at { + vers = update(&vers) + .set(versions::created_at.eq(created_at)) + .get_result(connection)?; + } + let new_deps = self .dependencies .into_iter() diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 516b901dd5f..eba027a4b0d 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -1389,13 +1389,11 @@ fn summary_new_crates() { .unwrap(); let plus_two = Utc::now().naive_utc() + chrono::Duration::seconds(2); - let newer = VersionBuilder::new("0.1.1").expect_build(krate4.id, user.id, conn); - // Update the patch version to be newer than the other versions, including the higher one. - update(&newer) - .set(versions::created_at.eq(plus_two)) - .execute(&*conn) - .unwrap(); + // Add a patch version be newer than the other versions, including the higher one. + VersionBuilder::new("0.1.1") + .created_at(plus_two) + .expect_build(krate4.id, user.id, conn); update(&krate4) .set(crates::updated_at.eq(plus_two)) From 658cdeae59877ebb072906232745b61253f5d788 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 18:29:07 +0200 Subject: [PATCH 4/7] builders: Sort properties alphabetically --- src/tests/builders/dependency.rs | 4 ++-- src/tests/builders/krate.rs | 12 ++++++------ src/tests/builders/publish.rs | 28 ++++++++++++++-------------- src/tests/builders/version.rs | 16 ++++++++-------- 4 files changed, 30 insertions(+), 30 deletions(-) diff --git a/src/tests/builders/dependency.rs b/src/tests/builders/dependency.rs index e1c87151879..8757e499845 100644 --- a/src/tests/builders/dependency.rs +++ b/src/tests/builders/dependency.rs @@ -2,9 +2,9 @@ use cargo_registry::views::krate_publish as u; /// A builder for constructing a dependency of another crate. pub struct DependencyBuilder { + explicit_name_in_toml: Option, name: String, registry: Option, - explicit_name_in_toml: Option, version_req: u::EncodableCrateVersionReq, } @@ -12,9 +12,9 @@ impl DependencyBuilder { /// Create a dependency on the crate with the given name. pub fn new(name: &str) -> Self { DependencyBuilder { + explicit_name_in_toml: None, name: name.to_string(), registry: None, - explicit_name_in_toml: None, version_req: u::EncodableCrateVersionReq(semver::VersionReq::parse(">= 0").unwrap()), } } diff --git a/src/tests/builders/krate.rs b/src/tests/builders/krate.rs index e33e7fba343..73772e98553 100644 --- a/src/tests/builders/krate.rs +++ b/src/tests/builders/krate.rs @@ -12,12 +12,12 @@ use super::VersionBuilder; /// If you want to test logic that happens as part of a publish request, use `PublishBuilder` /// instead. pub struct CrateBuilder<'a> { - owner_id: i32, - krate: NewCrate<'a>, downloads: Option, + keywords: Vec<&'a str>, + krate: NewCrate<'a>, + owner_id: i32, recent_downloads: Option, versions: Vec>, - keywords: Vec<&'a str>, } impl<'a> CrateBuilder<'a> { @@ -25,15 +25,15 @@ impl<'a> CrateBuilder<'a> { /// doesn't exist in the database, `expect_build` will fail. pub fn new(name: &str, owner_id: i32) -> CrateBuilder<'_> { CrateBuilder { - owner_id, + downloads: None, + keywords: Vec::new(), krate: NewCrate { name, ..NewCrate::default() }, - downloads: None, + owner_id, recent_downloads: None, versions: Vec::new(), - keywords: Vec::new(), } } diff --git a/src/tests/builders/publish.rs b/src/tests/builders/publish.rs index 48bbcd9d6fe..c9665ed3fd6 100644 --- a/src/tests/builders/publish.rs +++ b/src/tests/builders/publish.rs @@ -24,19 +24,19 @@ lazy_static! { /// a crate to exist and don't need to test behavior caused by the publish request, inserting /// a crate into the database directly by using CrateBuilder will be faster. pub struct PublishBuilder { - pub krate_name: String, - version: semver::Version, - tarball: Vec, + authors: Vec, + badges: HashMap>, + categories: Vec, deps: Vec, desc: Option, - readme: Option, doc_url: Option, keywords: Vec, - categories: Vec, - badges: HashMap>, + pub krate_name: String, license: Option, license_file: Option, - authors: Vec, + readme: Option, + tarball: Vec, + version: semver::Version, } impl PublishBuilder { @@ -44,19 +44,19 @@ impl PublishBuilder { /// in its tarball. pub fn new(krate_name: &str) -> Self { PublishBuilder { - krate_name: krate_name.into(), - version: semver::Version::parse("1.0.0").unwrap(), - tarball: EMPTY_TARBALL_BYTES.to_vec(), + authors: vec!["foo".to_string()], + badges: HashMap::new(), + categories: vec![], deps: vec![], desc: Some("description".to_string()), - readme: None, doc_url: None, keywords: vec![], - categories: vec![], - badges: HashMap::new(), + krate_name: krate_name.into(), license: Some("MIT".to_string()), license_file: None, - authors: vec!["foo".to_string()], + readme: None, + tarball: EMPTY_TARBALL_BYTES.to_vec(), + version: semver::Version::parse("1.0.0").unwrap(), } } diff --git a/src/tests/builders/version.rs b/src/tests/builders/version.rs index 82d20156bf7..6a97dfbfad4 100644 --- a/src/tests/builders/version.rs +++ b/src/tests/builders/version.rs @@ -11,13 +11,13 @@ use diesel::prelude::*; /// A builder to create version records for the purpose of inserting directly into the database. pub struct VersionBuilder<'a> { created_at: Option, - num: semver::Version, + dependencies: Vec<(i32, Option<&'static str>)>, + features: HashMap>, license: Option<&'a str>, license_file: Option<&'a str>, - features: HashMap>, - dependencies: Vec<(i32, Option<&'static str>)>, - yanked: bool, + num: semver::Version, size: i32, + yanked: bool, } impl<'a> VersionBuilder<'a> { @@ -34,13 +34,13 @@ impl<'a> VersionBuilder<'a> { VersionBuilder { created_at: None, - num, + dependencies: Vec::new(), + features: HashMap::new(), license: None, license_file: None, - features: HashMap::new(), - dependencies: Vec::new(), - yanked: false, + num, size: 0, + yanked: false, } } From 4777df5042d430352874000d8505d0a2dbeac711 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 18:33:06 +0200 Subject: [PATCH 5/7] tests/krate: Move and rename `updated` and `plus_two` variables --- src/tests/krate.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/tests/krate.rs b/src/tests/krate.rs index eba027a4b0d..2d759f12c6f 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -1338,6 +1338,9 @@ fn summary_new_crates() { let (app, anon, user) = TestApp::init().with_user(); let user = user.as_model(); app.db(|conn| { + let now_ = Utc::now().naive_utc(); + let now_plus_two = now_ + chrono::Duration::seconds(2); + let krate = CrateBuilder::new("some_downloads", user.id) .version(VersionBuilder::new("0.1.0")) .description("description") @@ -1382,21 +1385,18 @@ fn summary_new_crates() { .unwrap(); // update 'just_updated' krate. Others won't appear because updated_at == created_at. - let updated = Utc::now().naive_utc(); update(&krate3) - .set(crates::updated_at.eq(updated)) + .set(crates::updated_at.eq(now_)) .execute(&*conn) .unwrap(); - let plus_two = Utc::now().naive_utc() + chrono::Duration::seconds(2); - // Add a patch version be newer than the other versions, including the higher one. VersionBuilder::new("0.1.1") - .created_at(plus_two) + .created_at(now_plus_two) .expect_build(krate4.id, user.id, conn); update(&krate4) - .set(crates::updated_at.eq(plus_two)) + .set(crates::updated_at.eq(now_plus_two)) .execute(&*conn) .unwrap(); }); From 58e67c2d71a819145fffcf96fd924537afa4a61d Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Fri, 23 Oct 2020 18:57:07 +0200 Subject: [PATCH 6/7] builders/krate: Add `updated_at` option --- src/tests/builders/krate.rs | 16 ++++++++++++++++ src/tests/krate.rs | 25 +++++++------------------ 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/src/tests/builders/krate.rs b/src/tests/builders/krate.rs index 73772e98553..0f0c3b0f417 100644 --- a/src/tests/builders/krate.rs +++ b/src/tests/builders/krate.rs @@ -4,6 +4,7 @@ use cargo_registry::{ util::errors::AppResult, }; +use chrono::NaiveDateTime; use diesel::prelude::*; use super::VersionBuilder; @@ -17,6 +18,7 @@ pub struct CrateBuilder<'a> { krate: NewCrate<'a>, owner_id: i32, recent_downloads: Option, + updated_at: Option, versions: Vec>, } @@ -33,6 +35,7 @@ impl<'a> CrateBuilder<'a> { }, owner_id, recent_downloads: None, + updated_at: None, versions: Vec::new(), } } @@ -94,6 +97,12 @@ impl<'a> CrateBuilder<'a> { self } + /// Sets the crate's `updated_at` value. + pub fn updated_at(mut self, updated_at: NaiveDateTime) -> Self { + self.updated_at = Some(updated_at); + self + } + pub fn build(mut self, connection: &PgConnection) -> AppResult { use diesel::{insert_into, select, update}; @@ -138,6 +147,13 @@ impl<'a> CrateBuilder<'a> { Keyword::update_crate(connection, &krate, &self.keywords)?; } + if let Some(updated_at) = self.updated_at { + krate = update(&krate) + .set(crates::updated_at.eq(updated_at)) + .returning(cargo_registry::models::krate::ALL_COLUMNS) + .get_result(connection)?; + } + Ok(krate) } diff --git a/src/tests/krate.rs b/src/tests/krate.rs index 2d759f12c6f..c75f857fd0c 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -1356,14 +1356,19 @@ fn summary_new_crates() { .recent_downloads(50) .expect_build(conn); - let krate3 = CrateBuilder::new("just_updated", user.id) + CrateBuilder::new("just_updated", user.id) .version(VersionBuilder::new("0.1.0")) .version(VersionBuilder::new("0.1.2")) + // update 'just_updated' krate. Others won't appear because updated_at == created_at. + .updated_at(now_) .expect_build(conn); - let krate4 = CrateBuilder::new("just_updated_patch", user.id) + CrateBuilder::new("just_updated_patch", user.id) .version(VersionBuilder::new("0.1.0")) .version(VersionBuilder::new("0.2.0")) + // Add a patch version be newer than the other versions, including the higher one. + .version(VersionBuilder::new("0.1.1").created_at(now_plus_two)) + .updated_at(now_plus_two) .expect_build(conn); CrateBuilder::new("with_downloads", user.id) @@ -1383,22 +1388,6 @@ fn summary_new_crates() { .set(metadata::total_downloads.eq(6000)) .execute(&*conn) .unwrap(); - - // update 'just_updated' krate. Others won't appear because updated_at == created_at. - update(&krate3) - .set(crates::updated_at.eq(now_)) - .execute(&*conn) - .unwrap(); - - // Add a patch version be newer than the other versions, including the higher one. - VersionBuilder::new("0.1.1") - .created_at(now_plus_two) - .expect_build(krate4.id, user.id, conn); - - update(&krate4) - .set(crates::updated_at.eq(now_plus_two)) - .execute(&*conn) - .unwrap(); }); let json: SummaryResponse = anon.get("/api/v1/summary").good(); From 8742aceccd1e5b64c6e444597f387ba7ba3db6f4 Mon Sep 17 00:00:00 2001 From: Tobias Bieniek Date: Sun, 25 Oct 2020 09:02:56 +0100 Subject: [PATCH 7/7] builders/krate: Add `category` option --- src/tests/builders/krate.rs | 14 +++++++++++++- src/tests/krate.rs | 16 ++++++++-------- 2 files changed, 21 insertions(+), 9 deletions(-) diff --git a/src/tests/builders/krate.rs b/src/tests/builders/krate.rs index 0f0c3b0f417..5669567587c 100644 --- a/src/tests/builders/krate.rs +++ b/src/tests/builders/krate.rs @@ -1,5 +1,5 @@ use cargo_registry::{ - models::{Crate, Keyword, NewCrate}, + models::{Category, Crate, Keyword, NewCrate}, schema::{crates, version_downloads}, util::errors::AppResult, }; @@ -13,6 +13,7 @@ use super::VersionBuilder; /// If you want to test logic that happens as part of a publish request, use `PublishBuilder` /// instead. pub struct CrateBuilder<'a> { + categories: Vec<&'a str>, downloads: Option, keywords: Vec<&'a str>, krate: NewCrate<'a>, @@ -27,6 +28,7 @@ impl<'a> CrateBuilder<'a> { /// doesn't exist in the database, `expect_build` will fail. pub fn new(name: &str, owner_id: i32) -> CrateBuilder<'_> { CrateBuilder { + categories: Vec::new(), downloads: None, keywords: Vec::new(), krate: NewCrate { @@ -91,6 +93,12 @@ impl<'a> CrateBuilder<'a> { self } + /// Adds a category to the crate. + pub fn category(mut self, category: &'a str) -> Self { + self.categories.push(category); + self + } + /// Adds a keyword to the crate. pub fn keyword(mut self, keyword: &'a str) -> Self { self.keywords.push(keyword); @@ -143,6 +151,10 @@ impl<'a> CrateBuilder<'a> { select(refresh_recent_crate_downloads).execute(connection)?; } + if !self.categories.is_empty() { + Category::update_crate(connection, &krate, &self.categories)?; + } + if !self.keywords.is_empty() { Keyword::update_crate(connection, &krate, &self.keywords)?; } diff --git a/src/tests/krate.rs b/src/tests/krate.rs index c75f857fd0c..4e2151ade9b 100644 --- a/src/tests/krate.rs +++ b/src/tests/krate.rs @@ -1341,17 +1341,23 @@ fn summary_new_crates() { let now_ = Utc::now().naive_utc(); let now_plus_two = now_ + chrono::Duration::seconds(2); - let krate = CrateBuilder::new("some_downloads", user.id) + new_category("Category 1", "cat1", "Category 1 crates") + .create_or_update(conn) + .unwrap(); + + CrateBuilder::new("some_downloads", user.id) .version(VersionBuilder::new("0.1.0")) .description("description") .keyword("popular") + .category("cat1") .downloads(20) .recent_downloads(10) .expect_build(conn); - let krate2 = CrateBuilder::new("most_recent_downloads", user.id) + CrateBuilder::new("most_recent_downloads", user.id) .version(VersionBuilder::new("0.2.0")) .keyword("popular") + .category("cat1") .downloads(5000) .recent_downloads(50) .expect_build(conn); @@ -1377,12 +1383,6 @@ fn summary_new_crates() { .downloads(1000) .expect_build(conn); - new_category("Category 1", "cat1", "Category 1 crates") - .create_or_update(conn) - .unwrap(); - Category::update_crate(conn, &krate, &["cat1"]).unwrap(); - Category::update_crate(conn, &krate2, &["cat1"]).unwrap(); - // set total_downloads global value for `num_downloads` prop update(metadata::table) .set(metadata::total_downloads.eq(6000))