use std::{convert::TryFrom, io::Cursor};
use super::*;
use mockito::Matcher;
use serde_json::json;
async fn http_send<Body: Into<reqwest::Body>>(
request: http::Request<Body>,
) -> Result<http::Response<Vec<u8>>, reqwest::Error> {
let mut response = reqwest::Client::new()
.execute(request.try_into().unwrap())
.await?;
let mut builder = http::Response::builder()
.status(response.status())
.version(response.version());
std::mem::swap(builder.headers_mut().unwrap(), response.headers_mut());
Ok(builder
.body(response.bytes().await.unwrap().to_vec())
.unwrap())
}
#[tokio::test]
async fn authenticate_test_success() {
let username = "me@example.com";
let password = "password";
let name = "louis-test";
let expected_secret = "some-secret-here";
let resp_body = json!({
"authing_key": false,
"inserted_at": "2020-05-02T17:18:23.336328Z",
"name": "authenticate_test_1",
"permissions": [{"domain": "api", "resource": "write"}],
"revoked_at": null,
"secret": expected_secret,
"updated_at": "2020-05-02T17: 18: 23.336328Z",
"url": "https: //hex.pm/api/keys/authenticate_test_1"
});
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/keys")
.expect(1)
.match_header("authorization", "Basic bWVAZXhhbXBsZS5jb206cGFzc3dvcmQ=")
.match_header("content-type", "application/json")
.match_header("accept", "application/json")
.match_body(Matcher::Json(json!({
"name": name,
"permissions":[{ "domain": "api", "resource": "write" }]
})))
.with_status(201)
.with_body(resp_body.to_string())
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let secret = crate::api_create_api_key_response(
http_send(crate::api_create_api_key_request(
username, password, name, &config,
))
.await
.unwrap(),
)
.unwrap();
assert_eq!(expected_secret, secret);
mock.assert();
}
#[tokio::test]
async fn authenticate_test_rate_limted() {
let username = "me@example.com";
let password = "password";
let name = "authenticate_test_2";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/keys")
.expect(1)
.match_header("authorization", "Basic bWVAZXhhbXBsZS5jb206cGFzc3dvcmQ=")
.match_header("content-type", "application/json")
.match_header("accept", "application/json")
.match_body(Matcher::Json(json!({
"name": name,
"permissions":[{ "domain": "api", "resource": "write" }]
})))
.with_status(429)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_create_api_key_response(
http_send(crate::api_create_api_key_request(
username, password, name, &config,
))
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::RateLimited => (),
result => panic!("expected RateLimited, got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn authenticate_test_bad_creds() {
let username = "me@example.com";
let password = "password";
let name = "authenticate_test_3";
let resp_body = json!({
"message": "invalid username and password combination",
"status": 401,
});
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/keys")
.expect(1)
.match_header("authorization", "Basic bWVAZXhhbXBsZS5jb206cGFzc3dvcmQ=")
.match_header("content-type", "application/json")
.match_header("accept", "application/json")
.match_body(Matcher::Json(json!({
"name": name,
"permissions":[{ "domain": "api", "resource": "write" }]
})))
.with_status(401)
.with_body(resp_body.to_string())
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_create_api_key_response(
http_send(crate::api_create_api_key_request(
username, password, name, &config,
))
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::InvalidCredentials => (),
result => panic!("expected InvalidCredentials, got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn remove_docs_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_docs_response(
http_send(crate::api_remove_docs_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn revert_release_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_revert_release_response(
http_send(crate::api_revert_release_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn add_owner_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let owner = "lpil";
let level = OwnerLevel::Maintainer;
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"PUT",
format!("/packages/{}/owners/{}", package, owner).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.match_body(Matcher::Json(json!({
"level": "maintainer",
"transfer": false,
})))
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_add_owner_response(
http_send(crate::api_add_owner_request(
package, owner, level, key, &config,
))
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn transfer_owner_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let owner = "lpil";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"PUT",
format!("/packages/{}/owners/{}", package, owner).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.match_body(Matcher::Json(json!({
"level": "full",
"transfer": true,
})))
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_transfer_owner_response(
http_send(crate::api_transfer_owner_request(
package, owner, key, &config,
))
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn remove_owner_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let owner = "lpil";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/owners/{}", package, owner).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_owner_response(
http_send(crate::api_remove_owner_request(
package, owner, key, &config,
))
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn remove_key_success() {
let name = "some-key-name";
let key = "my-api-key-here";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("DELETE", format!("/keys/{}", name).as_str())
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(204)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_api_key_response(
http_send(crate::api_remove_api_key_request(name, key, &config))
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn remove_key_success_2() {
let name = "some-key-name";
let key = "my-api-key-here";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("DELETE", format!("/keys/{}", name).as_str())
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(200)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_api_key_response(
http_send(crate::api_remove_api_key_request(name, key, &config))
.await
.unwrap(),
)
.unwrap();
assert_eq!(result, ());
mock.assert();
}
#[tokio::test]
async fn remove_docs_unknown_package_version() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib_this_does_not_exist";
let version = "0.8.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(404)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_docs_response(
http_send(crate::api_remove_docs_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::NotFound => (),
result => panic!("expected ApiError::NotFound got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn remove_docs_rate_limted() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(429)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_docs_response(
http_send(crate::api_remove_docs_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::RateLimited => (),
result => panic!("expected ApiError::RateLimited got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn remove_docs_invalid_key() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(401)
.with_body(
json!({
"message": "invalid API key",
"status": 401,
})
.to_string(),
)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_docs_response(
http_send(crate::api_remove_docs_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::InvalidApiKey => (),
result => panic!("expected ApiError::InvalidApiKey got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn remove_docs_forbidden() {
let key = "my-api-key-here";
let package = "jason";
let version = "1.2.0";
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"DELETE",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(403)
.with_body(
json!({
"message": "account is not authorized for this action",
"status": 403,
})
.to_string(),
)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_remove_docs_response(
http_send(crate::api_remove_docs_request(package, version, key, &config).unwrap())
.await
.unwrap(),
)
.unwrap_err();
match result {
ApiError::Forbidden => (),
result => panic!("expected ApiError::Forbidden got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn remove_docs_bad_package_name() {
let key = "my-api-key-here";
let package = "not valid";
let version = "1.2.0";
let config = Config::new();
match crate::api_remove_docs_request(package, version, key, &config).unwrap_err() {
ApiError::InvalidPackageNameFormat(p) if p == package => (),
result => panic!("expected Err(ApiError::BadPackage), got {:?}", result),
}
}
#[tokio::test]
async fn publish_docs_success() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib_123";
let version = "0.8.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"POST",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(201)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_docs_response(
http_send(
crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap(),
)
.await
.unwrap(),
);
match result {
Ok(()) => (),
result => panic!("expected Ok(()), got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn publish_docs_bad_package_name() {
let key = "my-api-key-here";
let package = "not valid";
let version = "1.2.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let config = Config::new();
match crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap_err() {
ApiError::InvalidPackageNameFormat(p) if p == package => (),
result => panic!("expected Err(ApiError::BadPackage), got {:?}", result),
}
}
#[tokio::test]
async fn publish_docs_bad_package_version() {
let key = "my-api-key-here";
let package = "name";
let version = "invalid version";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let config = Config::new();
match crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap_err() {
ApiError::InvalidVersionFormat(v) if v == version => (),
result => panic!("expected ApiError::BadPackage, got {:?}", result),
}
}
#[tokio::test]
async fn publish_docs_not_found() {
let key = "my-api-key-here";
let package = "name";
let version = "1.1.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"POST",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(404)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_docs_response(
http_send(
crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap(),
)
.await
.unwrap(),
);
match result {
Err(ApiError::NotFound) => (),
result => panic!("expected ApiError::NotFound, got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn publish_docs_rate_limit() {
let key = "my-api-key-here";
let package = "name";
let version = "1.1.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"POST",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(429)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_docs_response(
http_send(
crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap(),
)
.await
.unwrap(),
);
match result {
Err(ApiError::RateLimited) => (),
result => panic!("expected ApiError::RateLimited, got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn publish_docs_invalid_api_key() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"POST",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(401)
.with_body(
json!({
"message": "invalid API key",
"status": 401,
})
.to_string(),
)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_docs_response(
http_send(
crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap(),
)
.await
.unwrap(),
);
match result {
Err(ApiError::InvalidApiKey) => (),
result => panic!("expected Err(ApiError::InvalidApiKey), got {:?}", result),
}
mock.assert();
}
#[tokio::test]
async fn publish_docs_forbidden() {
let key = "my-api-key-here";
let package = "gleam_experimental_stdlib";
let version = "0.8.0";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock(
"POST",
format!("/packages/{}/releases/{}/docs", package, version).as_str(),
)
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(403)
.with_body(
json!({
"message": "account is not authorized for this action",
"status": 403,
})
.to_string(),
)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_docs_response(
http_send(
crate::api_publish_docs_request(package, version, tarball, key, &config).unwrap(),
)
.await
.unwrap(),
);
match result {
Err(ApiError::Forbidden) => (),
result => panic!("expected Err(ApiError::Forbidden), got {:?}", result),
}
mock.assert();
}
fn expected_package_exfmt() -> Package {
Package {
name: "exfmt".to_string(),
repository: "hexpm".to_string(),
releases: vec![
Release {
version: Version::try_from("0.0.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
82, 48, 191, 145, 92, 172, 0, 108, 238, 71, 57, 23, 101, 177, 161, 83, 91, 182,
18, 232, 249, 225, 29, 12, 246, 5, 215, 165, 32, 57, 179, 110,
],
meta: (),
},
Release {
version: Version::try_from("0.1.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
111, 246, 240, 176, 118, 229, 12, 15, 164, 61, 186, 3, 89, 106, 153, 225, 247,
52, 245, 8, 216, 139, 21, 232, 200, 16, 214, 59, 241, 188, 9, 6,
],
meta: (),
},
Release {
version: Version::try_from("0.2.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
149, 9, 192, 229, 84, 162, 110, 207, 161, 43, 31, 0, 126, 168, 14, 243, 31, 43,
195, 238, 100, 91, 78, 100, 213, 181, 101, 154, 106, 168, 170, 107,
],
meta: (),
},
Release {
version: Version::try_from("0.2.1").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
157, 229, 28, 212, 92, 249, 14, 240, 235, 104, 31, 12, 160, 199, 83, 195, 154,
105, 222, 37, 221, 80, 181, 183, 113, 240, 234, 107, 144, 85, 255, 65,
],
meta: (),
},
Release {
version: Version::try_from("0.2.2").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
112, 250, 133, 189, 183, 192, 54, 218, 115, 55, 216, 97, 204, 201, 191, 168,
250, 133, 138, 252, 202, 240, 74, 197, 228, 235, 81, 18, 241, 7, 155, 38,
],
meta: (),
},
Release {
version: Version::try_from("0.2.3").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
131, 20, 29, 160, 171, 124, 7, 125, 210, 88, 17, 189, 199, 49, 191, 190, 14,
162, 38, 247, 52, 176, 189, 17, 7, 188, 151, 152, 24, 64, 170, 29,
],
meta: (),
},
Release {
version: Version::try_from("0.2.4").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
109, 162, 185, 169, 26, 4, 62, 60, 167, 54, 182, 161, 140, 197, 75, 113, 183,
117, 247, 201, 218, 228, 14, 160, 115, 157, 196, 51, 108, 16, 96, 217,
],
meta: (),
},
Release {
version: Version::try_from("0.3.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
97, 50, 95, 212, 242, 59, 245, 177, 140, 78, 79, 180, 108, 174, 119, 176, 24,
80, 218, 152, 178, 227, 152, 242, 32, 126, 72, 67, 222, 0, 173, 170,
],
meta: (),
},
Release {
version: Version::try_from("0.4.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
246, 178, 237, 214, 217, 158, 143, 52, 130, 186, 64, 50, 94, 175, 161, 81, 68,
186, 4, 73, 53, 226, 235, 144, 209, 84, 231, 136, 165, 119, 122, 126,
],
meta: (),
},
Release {
version: Version::try_from("0.5.0").unwrap(),
requirements: [].into(),
retirement_status: None,
outer_checksum: vec![
151, 86, 157, 218, 218, 131, 240, 119, 198, 216, 202, 240, 65, 17, 57, 228, 84,
252, 59, 207, 246, 49, 22, 21, 52, 47, 51, 139, 190, 9, 95, 109,
],
meta: (),
},
],
}
}
#[tokio::test]
async fn get_package_ok_test() {
let response_body = std::include_bytes!("../test/package_exfmt");
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/packages/exfmt")
.expect(1)
.with_status(200)
.with_body(&response_body[..])
.create_async()
.await;
let mut config = Config::new();
config.repository_base = http::Uri::try_from(server.url()).unwrap();
let package = crate::repository_v2_get_package_response(
http_send(crate::repository_v2_get_package_request(
"exfmt", None, &config,
))
.await
.unwrap(),
std::include_bytes!("../test/public_key"),
)
.unwrap();
assert_eq!(expected_package_exfmt(), package,);
mock.assert();
}
#[tokio::test]
async fn get_package_not_found() {
let config = Config::new();
let error = crate::repository_v2_get_package_response(
http_send(crate::repository_v2_get_package_request(
"louissaysthispackagedoesnotexist",
None,
&config,
))
.await
.unwrap(),
std::include_bytes!("../test/public_key"),
)
.unwrap_err();
assert!(error.is_not_found());
}
#[tokio::test]
async fn get_package_from_bytes_ok() {
let response_body = std::include_bytes!("../test/package_exfmt");
let mut uncompressed = Vec::new();
let mut decoder = GzDecoder::new(Cursor::new(response_body));
let _ = decoder
.read_to_end(&mut uncompressed)
.expect("failed to decompress body");
let package = crate::repository_v2_package_parse_body(
&uncompressed,
std::include_bytes!("../test/public_key"),
)
.expect("package failed to parse");
assert_eq!(expected_package_exfmt(), package,);
}
#[tokio::test]
async fn get_package_from_bytes_malformed() {
let bytes = std::include_bytes!("../test/public_key").to_vec();
let package_error = crate::repository_v2_package_parse_body(&bytes, &bytes)
.expect_err("parsing failed to fail");
assert!(package_error.is_invalid_protobuf());
}
#[tokio::test]
async fn get_repository_versions_ok_test() {
let response_body = std::include_bytes!("../test/versions");
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("GET", "/versions")
.expect(1)
.with_status(200)
.with_body(&response_body[..])
.create_async()
.await;
let mut config = Config::new();
config.repository_base = http::Uri::try_from(server.url()).unwrap();
let versions = crate::repository_v2_get_versions_response(
http_send(crate::repository_v2_get_versions_request(None, &config))
.await
.unwrap(),
std::include_bytes!("../test/public_key"),
);
assert_eq!(
&vec![
Version::parse("0.0.0").unwrap(),
Version::parse("0.1.0").unwrap(),
Version::parse("0.2.0").unwrap(),
Version::parse("0.2.1").unwrap(),
Version::parse("0.2.2").unwrap(),
Version::parse("0.2.3").unwrap(),
Version::parse("0.2.4").unwrap(),
Version::parse("0.3.0").unwrap(),
Version::parse("0.4.0").unwrap(),
Version::parse("0.5.0").unwrap(),
],
versions.unwrap().get("exfmt").unwrap(),
);
mock.assert();
}
#[tokio::test]
async fn get_repository_versions_from_bytes_ok() {
let response_body = std::include_bytes!("../test/versions");
let mut uncompressed = Vec::new();
let mut decoder = GzDecoder::new(Cursor::new(response_body));
let _ = decoder
.read_to_end(&mut uncompressed)
.expect("failed to decompress body");
let versions = crate::repository_v2_get_versions_body(
&uncompressed,
std::include_bytes!("../test/public_key"),
)
.expect("versions failed to parse");
assert_eq!(
&vec![
Version::parse("0.0.0").unwrap(),
Version::parse("0.1.0").unwrap(),
Version::parse("0.2.0").unwrap(),
Version::parse("0.2.1").unwrap(),
Version::parse("0.2.2").unwrap(),
Version::parse("0.2.3").unwrap(),
Version::parse("0.2.4").unwrap(),
Version::parse("0.3.0").unwrap(),
Version::parse("0.4.0").unwrap(),
Version::parse("0.5.0").unwrap(),
],
versions.get("exfmt").unwrap(),
);
}
#[tokio::test]
async fn get_repository_versions_from_bytes_malformed() {
let bytes = std::include_bytes!("../test/public_key").to_vec();
let versions_error =
crate::repository_v2_get_versions_body(&bytes, &bytes).expect_err("parsing failed to fail");
assert!(versions_error.is_invalid_protobuf());
}
#[tokio::test]
async fn get_repository_tarball_ok_test() {
let config = Config::new();
let checksum =
base16::decode("9107f6a859cb96945ad9a099085db028ca2bebb3c8ea42eec227b51c614cc2e0").unwrap();
let downloaded = crate::repository_get_package_tarball_response(
http_send(crate::repository_get_package_tarball_request(
"gleam_stdlib",
"0.14.0",
None,
&config,
))
.await
.unwrap(),
&checksum,
)
.unwrap();
assert_eq!(
&downloaded,
std::include_bytes!("../test/gleam_stdlib-0.14.0.tar")
);
}
#[tokio::test]
async fn get_repository_tarball_bad_checksum_test() {
let config = Config::new();
let checksum = vec![1, 2, 3, 4, 5];
let err = crate::repository_get_package_tarball_response(
http_send(crate::repository_get_package_tarball_request(
"gleam_stdlib",
"0.14.0",
None,
&config,
))
.await
.unwrap(),
&checksum,
)
.unwrap_err();
assert_eq!(
err.to_string(),
"the downloaded data did not have the expected checksum"
);
}
#[tokio::test]
async fn get_repository_tarball_not_found_test() {
let config = Config::new();
let checksum = vec![1, 2, 3, 4, 5];
let err = crate::repository_get_package_tarball_response(
http_send(crate::repository_get_package_tarball_request(
"gleam_stdlib",
"unknown-version",
None,
&config,
))
.await
.unwrap(),
&checksum,
)
.unwrap_err();
assert_eq!(err.to_string(), "resource was not found");
}
#[tokio::test]
async fn publish_package_success() {
let key = "my-api-key-here";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/publish?replace=false")
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(201)
.create_async()
.await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_package_response(
http_send(crate::api_publish_package_request(
tarball, key, &config, false,
))
.await
.unwrap(),
);
match result {
Ok(()) => (),
result => panic!("expected Ok(()), got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn modify_package_late() {
let key = "my-api-key-here";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/publish?replace=true")
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(422)
.with_body(
json!({
"errors": {"inserted_at": "can only modify a release up to one hour after publication"},
"message": "Validation error(s)",
"status": 422,
})
.to_string(),
)
.create_async().await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_package_response(
http_send(crate::api_publish_package_request(
tarball, key, &config, true,
))
.await
.unwrap(),
);
match result {
Err(ApiError::LateModification) => (),
result => panic!("expected Err(ApiError::LateModification), got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn not_replacing() {
let key = "my-api-key-here";
let tarball = std::include_bytes!("../test/example.tar.gz").to_vec();
let mut server = mockito::Server::new_async().await;
let mock = server
.mock("POST", "/publish?replace=false")
.expect(1)
.match_header("authorization", key)
.match_header("accept", "application/json")
.with_status(422)
.with_body(
json!({
"errors": {"inserted_at": "must include the --replace flag to update an existing release"},
"message": "Validation error(s)",
"status": 422,
})
.to_string(),
)
.create_async().await;
let mut config = Config::new();
config.api_base = http::Uri::try_from(server.url()).unwrap();
let result = crate::api_publish_package_response(
http_send(crate::api_publish_package_request(
tarball, key, &config, false,
))
.await
.unwrap(),
);
match result {
Err(ApiError::NotReplacing) => (),
result => panic!("expected Err(ApiError::NotReplacing), got {:?}", result),
}
mock.assert()
}
#[tokio::test]
async fn get_package_release_not_found() {
let config = Config::new();
let error = crate::api_get_package_release_response(
http_send(crate::api_get_package_release_request(
"louissaysthispackagedoesnotexist",
"1.0.1",
None,
&config,
))
.await
.unwrap(),
)
.unwrap_err();
assert!(error.is_not_found());
}
#[tokio::test]
async fn get_package_release_ok() {
let config = Config::new();
let resp = crate::api_get_package_release_response(
http_send(crate::api_get_package_release_request(
"clint", "0.0.1", None, &config,
))
.await
.unwrap(),
)
.unwrap();
assert_eq!(
resp,
Release {
version: Version::new(0, 0, 1),
requirements: [
(
"plug".into(),
Dependency {
requirement: Range::new("~>0.11.0".into()).unwrap(),
optional: false,
app: Some("plug".into()),
repository: None
}
),
(
"cowboy".into(),
Dependency {
requirement: Range::new("~>1.0.0".into()).unwrap(),
optional: false,
app: Some("cowboy".into()),
repository: None
}
)
]
.into(),
retirement_status: None,
outer_checksum: vec![
65, 198, 120, 27, 95, 75, 152, 107, 206, 20, 195, 87, 141, 57, 196, 151, 188, 184,
66, 127, 29, 54, 216, 205, 229, 252, 170, 110, 3, 202, 226, 177
],
meta: ReleaseMeta {
app: "clint".into(),
build_tools: vec!["mix".into()]
}
}
)
}
#[test]
fn make_request_base_trailing_slash_is_optional() {
let slash = http::Uri::from_static("http://host/path/");
let no_slash = http::Uri::from_static("http://host/path");
let suffix = "suffix";
let expect = "/path/suffix";
let slash = make_request(slash, http::Method::GET, suffix, None);
assert_eq!(slash.uri_ref().unwrap().path(), expect);
let no_slash = make_request(no_slash, http::Method::GET, suffix, None);
assert_eq!(no_slash.uri_ref().unwrap().path(), expect);
}