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"
|
||||
let version_str = name
|
||||
.rsplit_once('-')
|
||||
.and_then(|(_, v)| v.strip_suffix("jar"))
|
||||
.and_then(|(_, v)| v.strip_suffix(".jar"))
|
||||
.or_else(|| {
|
||||
name
|
||||
.rsplit_once('_')
|
||||
.and_then(|(_, v)| v.strip_suffix("jar"))
|
||||
.and_then(|(_, v)| v.strip_suffix(".jar"))
|
||||
})
|
||||
.unwrap_or(name);
|
||||
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 {
|
||||
match type_str {
|
||||
"mod" => ProjectType::Mod,
|
||||
|
|
@ -123,15 +179,7 @@ impl PlatformClient for ModrinthPlatform {
|
|||
_loaders: &[String],
|
||||
) -> Result<Project> {
|
||||
let url = format!("{MODRINTH_API_BASE}/project/{identifier}");
|
||||
|
||||
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))
|
||||
self.request_project_url(&url).await
|
||||
}
|
||||
|
||||
async fn request_project_files(
|
||||
|
|
@ -170,20 +218,7 @@ impl PlatformClient for ModrinthPlatform {
|
|||
url.push_str(¶ms.join("&"));
|
||||
}
|
||||
|
||||
let response = self.client.get(&url).send().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(),
|
||||
)
|
||||
self.request_project_files_url(&url).await
|
||||
}
|
||||
|
||||
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>> {
|
||||
// Modrinth uses SHA-1 hash for file lookups
|
||||
let url = format!("{MODRINTH_API_BASE}/version_file/{hash}");
|
||||
|
||||
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)
|
||||
self.lookup_by_hash_url(&url).await
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -280,3 +292,124 @@ struct ModrinthDependency {
|
|||
project_id: Option<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