platform: add mockito HTTP tests to modrinth
Signed-off-by: NotAShelf <raf@notashelf.dev> Change-Id: I880c11195559fcfb9701e945a10fe87b6a6a6964
This commit is contained in:
parent
4fc05e71e7
commit
0cc72e9916
2 changed files with 182 additions and 49 deletions
|
|
@ -198,11 +198,11 @@ impl Project {
|
||||||
// "mod_v1.0.0"
|
// "mod_v1.0.0"
|
||||||
let version_str = name
|
let version_str = name
|
||||||
.rsplit_once('-')
|
.rsplit_once('-')
|
||||||
.and_then(|(_, v)| v.strip_suffix("jar"))
|
.and_then(|(_, v)| v.strip_suffix(".jar"))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
name
|
name
|
||||||
.rsplit_once('_')
|
.rsplit_once('_')
|
||||||
.and_then(|(_, v)| v.strip_suffix("jar"))
|
.and_then(|(_, v)| v.strip_suffix(".jar"))
|
||||||
})
|
})
|
||||||
.unwrap_or(name);
|
.unwrap_or(name);
|
||||||
semver::Version::parse(version_str).ok()
|
semver::Version::parse(version_str).ok()
|
||||||
|
|
|
||||||
|
|
@ -24,6 +24,62 @@ impl ModrinthPlatform {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn request_project_url(&self, url: &str) -> Result<Project> {
|
||||||
|
let response = self.client.get(url).send().await?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
return Err(PakkerError::ProjectNotFound(url.to_string()));
|
||||||
|
}
|
||||||
|
let mr_project: ModrinthProject = response.json().await?;
|
||||||
|
Ok(self.convert_project(mr_project))
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn request_project_files_url(
|
||||||
|
&self,
|
||||||
|
url: &str,
|
||||||
|
) -> Result<Vec<ProjectFile>> {
|
||||||
|
let response = self.client.get(url).send().await?;
|
||||||
|
if !response.status().is_success() {
|
||||||
|
return Err(PakkerError::ProjectNotFound(url.to_string()));
|
||||||
|
}
|
||||||
|
let mr_versions: Vec<ModrinthVersion> = response.json().await?;
|
||||||
|
let project_id = url
|
||||||
|
.split('/')
|
||||||
|
.nth(4)
|
||||||
|
.ok_or_else(|| {
|
||||||
|
PakkerError::InvalidResponse(
|
||||||
|
"Cannot parse project ID from URL".to_string(),
|
||||||
|
)
|
||||||
|
})?
|
||||||
|
.to_string();
|
||||||
|
Ok(
|
||||||
|
mr_versions
|
||||||
|
.into_iter()
|
||||||
|
.map(|v| self.convert_version(v, &project_id))
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn lookup_by_hash_url(&self, url: &str) -> Result<Option<Project>> {
|
||||||
|
let response = self.client.get(url).send().await?;
|
||||||
|
if response.status().as_u16() == 404 {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
if !response.status().is_success() {
|
||||||
|
return Err(PakkerError::PlatformApiError(format!(
|
||||||
|
"Modrinth API error: {}",
|
||||||
|
response.status()
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
let version_data: serde_json::Value = response.json().await?;
|
||||||
|
let project_id = version_data["project_id"].as_str().ok_or_else(|| {
|
||||||
|
PakkerError::InvalidResponse("Missing project_id".to_string())
|
||||||
|
})?;
|
||||||
|
self
|
||||||
|
.request_project_with_files(project_id, &[], &[])
|
||||||
|
.await
|
||||||
|
.map(Some)
|
||||||
|
}
|
||||||
|
|
||||||
fn map_project_type(type_str: &str) -> ProjectType {
|
fn map_project_type(type_str: &str) -> ProjectType {
|
||||||
match type_str {
|
match type_str {
|
||||||
"mod" => ProjectType::Mod,
|
"mod" => ProjectType::Mod,
|
||||||
|
|
@ -123,15 +179,7 @@ impl PlatformClient for ModrinthPlatform {
|
||||||
_loaders: &[String],
|
_loaders: &[String],
|
||||||
) -> Result<Project> {
|
) -> Result<Project> {
|
||||||
let url = format!("{MODRINTH_API_BASE}/project/{identifier}");
|
let url = format!("{MODRINTH_API_BASE}/project/{identifier}");
|
||||||
|
self.request_project_url(&url).await
|
||||||
let response = self.client.get(&url).send().await?;
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
return Err(PakkerError::ProjectNotFound(identifier.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mr_project: ModrinthProject = response.json().await?;
|
|
||||||
Ok(self.convert_project(mr_project))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn request_project_files(
|
async fn request_project_files(
|
||||||
|
|
@ -170,20 +218,7 @@ impl PlatformClient for ModrinthPlatform {
|
||||||
url.push_str(¶ms.join("&"));
|
url.push_str(¶ms.join("&"));
|
||||||
}
|
}
|
||||||
|
|
||||||
let response = self.client.get(&url).send().await?;
|
self.request_project_files_url(&url).await
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
return Err(PakkerError::ProjectNotFound(project_id.to_string()));
|
|
||||||
}
|
|
||||||
|
|
||||||
let mr_versions: Vec<ModrinthVersion> = response.json().await?;
|
|
||||||
|
|
||||||
Ok(
|
|
||||||
mr_versions
|
|
||||||
.into_iter()
|
|
||||||
.map(|v| self.convert_version(v, project_id))
|
|
||||||
.collect(),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
async fn request_project_with_files(
|
async fn request_project_with_files(
|
||||||
|
|
@ -213,30 +248,7 @@ impl PlatformClient for ModrinthPlatform {
|
||||||
async fn lookup_by_hash(&self, hash: &str) -> Result<Option<Project>> {
|
async fn lookup_by_hash(&self, hash: &str) -> Result<Option<Project>> {
|
||||||
// Modrinth uses SHA-1 hash for file lookups
|
// Modrinth uses SHA-1 hash for file lookups
|
||||||
let url = format!("{MODRINTH_API_BASE}/version_file/{hash}");
|
let url = format!("{MODRINTH_API_BASE}/version_file/{hash}");
|
||||||
|
self.lookup_by_hash_url(&url).await
|
||||||
let response = self.client.get(&url).send().await?;
|
|
||||||
|
|
||||||
if response.status().as_u16() == 404 {
|
|
||||||
return Ok(None);
|
|
||||||
}
|
|
||||||
|
|
||||||
if !response.status().is_success() {
|
|
||||||
return Err(PakkerError::PlatformApiError(format!(
|
|
||||||
"Modrinth API error: {}",
|
|
||||||
response.status()
|
|
||||||
)));
|
|
||||||
}
|
|
||||||
|
|
||||||
let version_data: serde_json::Value = response.json().await?;
|
|
||||||
|
|
||||||
let project_id = version_data["project_id"].as_str().ok_or_else(|| {
|
|
||||||
PakkerError::InvalidResponse("Missing project_id".to_string())
|
|
||||||
})?;
|
|
||||||
|
|
||||||
self
|
|
||||||
.request_project_with_files(project_id, &[], &[])
|
|
||||||
.await
|
|
||||||
.map(Some)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -280,3 +292,124 @@ struct ModrinthDependency {
|
||||||
project_id: Option<String>,
|
project_id: Option<String>,
|
||||||
dependency_type: String,
|
dependency_type: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use reqwest::Client;
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
impl ModrinthPlatform {
|
||||||
|
fn with_client(client: Client) -> Self {
|
||||||
|
Self { client }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn create_platform_with_mock()
|
||||||
|
-> (ModrinthPlatform, mockito::ServerGuard) {
|
||||||
|
let server = mockito::Server::new_async().await;
|
||||||
|
let client = Client::new();
|
||||||
|
let platform = ModrinthPlatform::with_client(client);
|
||||||
|
(platform, server)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_request_project_success() {
|
||||||
|
let (platform, mut server) = create_platform_with_mock().await;
|
||||||
|
let url = format!("{}/project/test-mod", server.url());
|
||||||
|
|
||||||
|
let _mock = server
|
||||||
|
.mock("GET", "/project/test-mod")
|
||||||
|
.with_status(200)
|
||||||
|
.with_header("content-type", "application/json")
|
||||||
|
.with_body(
|
||||||
|
r#"{
|
||||||
|
"id": "abc123",
|
||||||
|
"slug": "test-mod",
|
||||||
|
"title": "Test Mod",
|
||||||
|
"project_type": "mod",
|
||||||
|
"client_side": "required",
|
||||||
|
"server_side": "required"
|
||||||
|
}"#,
|
||||||
|
)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let result = platform.request_project_url(&url).await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let project = result.unwrap();
|
||||||
|
assert!(project.get_platform_id("modrinth").is_some());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_request_project_not_found() {
|
||||||
|
let (platform, mut server) = create_platform_with_mock().await;
|
||||||
|
let url = format!("{}/project/nonexistent", server.url());
|
||||||
|
|
||||||
|
let _mock = server
|
||||||
|
.mock("GET", "/project/nonexistent")
|
||||||
|
.with_status(404)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let result = platform.request_project_url(&url).await;
|
||||||
|
|
||||||
|
assert!(result.is_err());
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_request_project_files() {
|
||||||
|
let (platform, mut server) = create_platform_with_mock().await;
|
||||||
|
let url = format!("{}/project/abc123/version", server.url());
|
||||||
|
|
||||||
|
let _mock = server
|
||||||
|
.mock("GET", "/project/abc123/version")
|
||||||
|
.with_status(200)
|
||||||
|
.with_header("content-type", "application/json")
|
||||||
|
.with_body(
|
||||||
|
r#"[
|
||||||
|
{
|
||||||
|
"id": "v1",
|
||||||
|
"project_id": "abc123",
|
||||||
|
"name": "Test Mod v1.0.0",
|
||||||
|
"version_number": "1.0.0",
|
||||||
|
"game_versions": ["1.20.1"],
|
||||||
|
"version_type": "release",
|
||||||
|
"loaders": ["fabric"],
|
||||||
|
"date_published": "2024-01-01T00:00:00Z",
|
||||||
|
"files": [{
|
||||||
|
"hashes": {"sha1": "abc123def456"},
|
||||||
|
"url": "https://example.com/mod.jar",
|
||||||
|
"filename": "test-mod-1.0.0.jar",
|
||||||
|
"primary": true,
|
||||||
|
"size": 1024
|
||||||
|
}],
|
||||||
|
"dependencies": []
|
||||||
|
}
|
||||||
|
]"#,
|
||||||
|
)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let result = platform.request_project_files_url(&url).await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
let files = result.unwrap();
|
||||||
|
assert_eq!(files.len(), 1);
|
||||||
|
assert_eq!(files[0].file_name, "test-mod-1.0.0.jar");
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tokio::test]
|
||||||
|
async fn test_lookup_by_hash_not_found() {
|
||||||
|
let (platform, mut server) = create_platform_with_mock().await;
|
||||||
|
let url = format!("{}/version_file/unknownhash123", server.url());
|
||||||
|
|
||||||
|
let _mock = server
|
||||||
|
.mock("GET", "/version_file/unknownhash123")
|
||||||
|
.with_status(404)
|
||||||
|
.create();
|
||||||
|
|
||||||
|
let result = platform.lookup_by_hash_url(&url).await;
|
||||||
|
|
||||||
|
assert!(result.is_ok());
|
||||||
|
assert!(result.unwrap().is_none());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue