2025-12-22 20:10:17 +01:00
|
|
|
use libips::actions::{File as FileAction, Manifest};
|
|
|
|
|
use libips::repository::{FileBackend, RepositoryVersion, WritableRepository};
|
2025-12-08 20:50:20 +01:00
|
|
|
use pkg6depotd::config::{Config, RepositoryConfig, ServerConfig};
|
|
|
|
|
use pkg6depotd::http;
|
2025-12-22 20:10:17 +01:00
|
|
|
use pkg6depotd::repo::DepotRepo;
|
|
|
|
|
use std::fs;
|
2025-12-08 20:50:20 +01:00
|
|
|
use std::path::PathBuf;
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
use tempfile::TempDir;
|
|
|
|
|
use tokio::net::TcpListener;
|
|
|
|
|
|
|
|
|
|
// Helper to setup a repo with a published package
|
|
|
|
|
fn setup_repo(dir: &TempDir) -> PathBuf {
|
|
|
|
|
let repo_path = dir.path().join("repo");
|
|
|
|
|
let mut backend = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
|
|
|
|
let publisher = "test";
|
|
|
|
|
backend.add_publisher(publisher).unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// Create a transaction to publish a package
|
|
|
|
|
let mut tx = backend.begin_transaction().unwrap();
|
|
|
|
|
tx.set_publisher(publisher);
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// Create content
|
|
|
|
|
let content_dir = dir.path().join("content");
|
|
|
|
|
fs::create_dir_all(&content_dir).unwrap();
|
|
|
|
|
let file_path = content_dir.join("hello.txt");
|
|
|
|
|
fs::write(&file_path, "Hello IPS").unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// Add file
|
|
|
|
|
let mut fa = FileAction::read_from_path(&file_path).unwrap();
|
|
|
|
|
fa.path = "hello.txt".to_string(); // relative path in package
|
|
|
|
|
tx.add_file(fa, &file_path).unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// Update manifest
|
|
|
|
|
let mut manifest = Manifest::new();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 21:36:37 +01:00
|
|
|
use libips::actions::Attr;
|
2025-12-08 20:50:20 +01:00
|
|
|
use std::collections::HashMap;
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
manifest.attributes.push(Attr {
|
|
|
|
|
key: "pkg.fmri".to_string(),
|
|
|
|
|
values: vec!["pkg://test/example@1.0.0".to_string()],
|
|
|
|
|
properties: HashMap::new(),
|
|
|
|
|
});
|
2025-12-22 20:10:17 +01:00
|
|
|
manifest.attributes.push(Attr {
|
2025-12-08 20:50:20 +01:00
|
|
|
key: "pkg.summary".to_string(),
|
|
|
|
|
values: vec!["Test Package".to_string()],
|
|
|
|
|
properties: HashMap::new(),
|
|
|
|
|
});
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
tx.update_manifest(manifest);
|
|
|
|
|
tx.commit().unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
backend.rebuild(Some(publisher), false, false).unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
repo_path
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_depot_server() {
|
|
|
|
|
// Setup
|
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
|
|
|
let repo_path = setup_repo(&temp_dir);
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
let config = Config {
|
|
|
|
|
server: ServerConfig {
|
|
|
|
|
bind: vec!["127.0.0.1:0".to_string()],
|
|
|
|
|
workers: None,
|
|
|
|
|
max_connections: None,
|
|
|
|
|
reuseport: None,
|
2025-12-09 20:23:00 +01:00
|
|
|
cache_max_age: Some(3600),
|
2025-12-08 20:50:20 +01:00
|
|
|
tls_cert: None,
|
|
|
|
|
tls_key: None,
|
|
|
|
|
},
|
|
|
|
|
repository: RepositoryConfig {
|
|
|
|
|
root: repo_path.clone(),
|
|
|
|
|
mode: Some("readonly".to_string()),
|
|
|
|
|
},
|
|
|
|
|
telemetry: None,
|
|
|
|
|
publishers: None,
|
|
|
|
|
admin: None,
|
|
|
|
|
oauth2: None,
|
|
|
|
|
};
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
let repo = DepotRepo::new(&config).unwrap();
|
|
|
|
|
let state = Arc::new(repo);
|
|
|
|
|
let router = http::routes::app_router(state);
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
|
|
|
let addr = listener.local_addr().unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// Spawn server
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
http::server::run(router, listener).await.unwrap();
|
|
|
|
|
});
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
let base_url = format!("http://{}", addr);
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// 1. Test Versions
|
2025-12-22 20:10:17 +01:00
|
|
|
let resp = client
|
|
|
|
|
.get(format!("{}/versions/0/", base_url))
|
|
|
|
|
.send()
|
|
|
|
|
.await
|
|
|
|
|
.unwrap();
|
2025-12-08 20:50:20 +01:00
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
let text = resp.text().await.unwrap();
|
2026-01-20 17:44:36 +01:00
|
|
|
assert!(text.contains("pkg-server pkg6depotd-"));
|
2025-12-08 22:10:11 +01:00
|
|
|
assert!(text.contains("catalog 1"));
|
2025-12-08 21:36:37 +01:00
|
|
|
assert!(text.contains("manifest 0 1"));
|
|
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// 2. Test Catalog
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 21:36:37 +01:00
|
|
|
// Test Catalog v1
|
|
|
|
|
let catalog_v1_url = format!("{}/test/catalog/1/catalog.attrs", base_url);
|
|
|
|
|
let resp = client.get(&catalog_v1_url).send().await.unwrap();
|
|
|
|
|
if !resp.status().is_success() {
|
2025-12-22 20:10:17 +01:00
|
|
|
println!("Catalog v1 failed: {:?}", resp);
|
2025-12-08 21:36:37 +01:00
|
|
|
}
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
let catalog_attrs = resp.text().await.unwrap();
|
|
|
|
|
// Verify it looks like JSON catalog attrs (contains signature)
|
|
|
|
|
assert!(catalog_attrs.contains("package-count"));
|
|
|
|
|
assert!(catalog_attrs.contains("parts"));
|
|
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// 3. Test Manifest
|
|
|
|
|
let fmri_arg = "example%401.0.0";
|
2025-12-08 21:36:37 +01:00
|
|
|
// v0
|
|
|
|
|
let manifest_url = format!("{}/test/manifest/0/{}", base_url, fmri_arg);
|
|
|
|
|
let resp = client.get(&manifest_url).send().await.unwrap();
|
2025-12-08 20:50:20 +01:00
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
let manifest_text = resp.text().await.unwrap();
|
|
|
|
|
assert!(manifest_text.contains("pkg.fmri"));
|
|
|
|
|
assert!(manifest_text.contains("example@1.0.0"));
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 21:36:37 +01:00
|
|
|
// v1
|
|
|
|
|
let manifest_v1_url = format!("{}/test/manifest/1/{}", base_url, fmri_arg);
|
|
|
|
|
let resp = client.get(&manifest_v1_url).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
let manifest_text_v1 = resp.text().await.unwrap();
|
|
|
|
|
assert_eq!(manifest_text, manifest_text_v1);
|
|
|
|
|
|
2025-12-08 20:50:20 +01:00
|
|
|
// 4. Test Info
|
2025-12-08 21:36:37 +01:00
|
|
|
let info_url = format!("{}/test/info/0/{}", base_url, fmri_arg);
|
|
|
|
|
let resp = client.get(&info_url).send().await.unwrap();
|
2025-12-08 20:50:20 +01:00
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
let info_text = resp.text().await.unwrap();
|
|
|
|
|
assert!(info_text.contains("Name: example"));
|
|
|
|
|
assert!(info_text.contains("Summary: Test Package"));
|
2025-12-09 16:42:21 +01:00
|
|
|
// Ensure FMRI format is correct: pkg://<publisher>/<name>@<version>
|
2025-12-22 20:10:17 +01:00
|
|
|
assert!(
|
|
|
|
|
info_text.contains("FMRI: pkg://test/example@1.0.0"),
|
|
|
|
|
"Info FMRI was: {}",
|
|
|
|
|
info_text
|
|
|
|
|
);
|
|
|
|
|
|
2025-12-08 21:36:37 +01:00
|
|
|
// 5. Test Publisher v1
|
|
|
|
|
let pub_url = format!("{}/test/publisher/1", base_url);
|
|
|
|
|
let resp = client.get(&pub_url).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
2026-01-20 17:44:36 +01:00
|
|
|
|
|
|
|
|
// Test Publisher v1 with trailing slash
|
|
|
|
|
let pub_url_slash = format!("{}/test/publisher/1/", base_url);
|
|
|
|
|
let resp = client.get(&pub_url_slash).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
2025-12-22 20:10:17 +01:00
|
|
|
assert!(
|
|
|
|
|
resp.headers()
|
|
|
|
|
.get("content-type")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_str()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.contains("application/vnd.pkg5.info")
|
|
|
|
|
);
|
2025-12-08 21:36:37 +01:00
|
|
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
|
|
|
|
assert_eq!(pub_json["version"], 1);
|
|
|
|
|
assert_eq!(pub_json["publishers"][0]["name"], "test");
|
2026-01-25 23:17:49 +01:00
|
|
|
|
2026-01-20 17:44:36 +01:00
|
|
|
// Test Default Publisher Route v1
|
|
|
|
|
let def_pub_url = format!("{}/publisher/1", base_url);
|
|
|
|
|
let resp = client.get(&def_pub_url).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
|
|
|
|
// Test Default Publisher Route v1 with trailing slash
|
|
|
|
|
let def_pub_url_slash = format!("{}/publisher/1/", base_url);
|
|
|
|
|
let resp = client.get(&def_pub_url_slash).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
|
|
|
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
|
|
|
|
// In current implementation it returns one publisher.
|
|
|
|
|
// We want it to return all publishers.
|
|
|
|
|
assert_eq!(pub_json["publishers"].as_array().unwrap().len(), 1);
|
|
|
|
|
assert_eq!(pub_json["publishers"][0]["name"], "test");
|
|
|
|
|
|
|
|
|
|
// Test Default Publisher Route v0
|
|
|
|
|
let def_pub_url_v0 = format!("{}/publisher/0", base_url);
|
|
|
|
|
let resp = client.get(&def_pub_url_v0).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
|
|
|
|
// Test Default Publisher Route v0 with trailing slash
|
|
|
|
|
let def_pub_url_v0_slash = format!("{}/publisher/0/", base_url);
|
|
|
|
|
let resp = client.get(&def_pub_url_v0_slash).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
|
|
|
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
|
|
|
|
assert_eq!(pub_json["publishers"].as_array().unwrap().len(), 1);
|
|
|
|
|
assert_eq!(pub_json["publishers"][0]["name"], "test");
|
2025-12-22 20:10:17 +01:00
|
|
|
|
2025-12-08 21:36:37 +01:00
|
|
|
// 6. Test File
|
|
|
|
|
// We assume file exists if manifest works.
|
2025-12-08 20:50:20 +01:00
|
|
|
}
|
2025-12-09 14:23:55 +01:00
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_ini_only_repo_serving_catalog() {
|
|
|
|
|
use libips::repository::BatchOptions;
|
2026-01-20 22:19:25 +01:00
|
|
|
use libips::repository::WritableRepository;
|
2025-12-09 14:23:55 +01:00
|
|
|
use std::io::Write as _;
|
|
|
|
|
|
|
|
|
|
// Setup temp repo
|
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
|
|
|
let repo_path = temp_dir.path().join("repo_ini");
|
|
|
|
|
|
|
|
|
|
// Create repo using FileBackend to get layout, publish one pkg
|
|
|
|
|
let mut backend = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
|
|
|
|
let publisher = "ini-test";
|
|
|
|
|
backend.add_publisher(publisher).unwrap();
|
|
|
|
|
|
|
|
|
|
// Create content
|
|
|
|
|
let content_dir = temp_dir.path().join("content_ini");
|
|
|
|
|
fs::create_dir_all(&content_dir).unwrap();
|
|
|
|
|
let file_path = content_dir.join("hello.txt");
|
|
|
|
|
fs::write(&file_path, "Hello INI Repo").unwrap();
|
|
|
|
|
|
|
|
|
|
// Publish one manifest
|
|
|
|
|
let mut tx = backend.begin_transaction().unwrap();
|
|
|
|
|
tx.set_publisher(publisher);
|
|
|
|
|
let mut fa = FileAction::read_from_path(&file_path).unwrap();
|
|
|
|
|
fa.path = "hello.txt".to_string();
|
|
|
|
|
tx.add_file(fa, &file_path).unwrap();
|
|
|
|
|
|
|
|
|
|
let mut manifest = Manifest::new();
|
|
|
|
|
use libips::actions::Attr;
|
|
|
|
|
use std::collections::HashMap;
|
2025-12-22 20:10:17 +01:00
|
|
|
manifest.attributes.push(Attr {
|
|
|
|
|
key: "pkg.fmri".to_string(),
|
|
|
|
|
values: vec![format!("pkg://{}/example@1.0.0", publisher)],
|
|
|
|
|
properties: HashMap::new(),
|
|
|
|
|
});
|
|
|
|
|
manifest.attributes.push(Attr {
|
|
|
|
|
key: "pkg.summary".to_string(),
|
|
|
|
|
values: vec!["INI Repo Test Package".to_string()],
|
|
|
|
|
properties: HashMap::new(),
|
|
|
|
|
});
|
2025-12-09 14:23:55 +01:00
|
|
|
tx.update_manifest(manifest);
|
|
|
|
|
tx.commit().unwrap();
|
|
|
|
|
|
|
|
|
|
// Rebuild catalog using batched API explicitly with small batch to exercise code path
|
2025-12-22 20:10:17 +01:00
|
|
|
let opts = BatchOptions {
|
|
|
|
|
batch_size: 1,
|
|
|
|
|
flush_every_n: 1,
|
|
|
|
|
};
|
|
|
|
|
backend
|
|
|
|
|
.rebuild_catalog_batched(publisher, true, opts)
|
|
|
|
|
.unwrap();
|
2025-12-09 14:23:55 +01:00
|
|
|
|
|
|
|
|
// Replace pkg6.repository with legacy pkg5.repository so FileBackend::open uses INI
|
|
|
|
|
let pkg6_cfg = repo_path.join("pkg6.repository");
|
2025-12-22 20:10:17 +01:00
|
|
|
if pkg6_cfg.exists() {
|
|
|
|
|
fs::remove_file(&pkg6_cfg).unwrap();
|
|
|
|
|
}
|
2025-12-09 14:23:55 +01:00
|
|
|
let mut ini = String::new();
|
|
|
|
|
ini.push_str("[publisher]\n");
|
|
|
|
|
ini.push_str(&format!("prefix = {}\n", publisher));
|
|
|
|
|
ini.push_str("[repository]\nversion = 4\n");
|
|
|
|
|
let mut f = std::fs::File::create(repo_path.join("pkg5.repository")).unwrap();
|
|
|
|
|
f.write_all(ini.as_bytes()).unwrap();
|
|
|
|
|
|
|
|
|
|
// Start depot server
|
|
|
|
|
let config = Config {
|
2025-12-22 20:10:17 +01:00
|
|
|
server: ServerConfig {
|
|
|
|
|
bind: vec!["127.0.0.1:0".to_string()],
|
|
|
|
|
workers: None,
|
|
|
|
|
max_connections: None,
|
|
|
|
|
reuseport: None,
|
|
|
|
|
cache_max_age: Some(3600),
|
|
|
|
|
tls_cert: None,
|
|
|
|
|
tls_key: None,
|
|
|
|
|
},
|
|
|
|
|
repository: RepositoryConfig {
|
|
|
|
|
root: repo_path.clone(),
|
|
|
|
|
mode: Some("readonly".to_string()),
|
|
|
|
|
},
|
|
|
|
|
telemetry: None,
|
|
|
|
|
publishers: None,
|
|
|
|
|
admin: None,
|
|
|
|
|
oauth2: None,
|
2025-12-09 14:23:55 +01:00
|
|
|
};
|
|
|
|
|
let repo = DepotRepo::new(&config).unwrap();
|
|
|
|
|
let state = Arc::new(repo);
|
|
|
|
|
let router = http::routes::app_router(state);
|
|
|
|
|
|
|
|
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
|
|
|
let addr = listener.local_addr().unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
tokio::spawn(async move {
|
|
|
|
|
http::server::run(router, listener).await.unwrap();
|
|
|
|
|
});
|
2025-12-09 14:23:55 +01:00
|
|
|
|
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
let base_url = format!("http://{}", addr);
|
|
|
|
|
|
|
|
|
|
// Fetch catalog attrs via v1 endpoint
|
|
|
|
|
let url = format!("{}/{}/catalog/1/catalog.attrs", base_url, publisher);
|
|
|
|
|
let resp = client.get(&url).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success(), "status: {:?}", resp.status());
|
|
|
|
|
let body = resp.text().await.unwrap();
|
|
|
|
|
assert!(body.contains("package-count"));
|
|
|
|
|
assert!(body.contains("parts"));
|
2025-12-09 16:02:02 +01:00
|
|
|
|
|
|
|
|
// Also fetch individual catalog parts
|
2025-12-22 20:10:17 +01:00
|
|
|
for part in [
|
|
|
|
|
"catalog.base.C",
|
|
|
|
|
"catalog.dependency.C",
|
|
|
|
|
"catalog.summary.C",
|
|
|
|
|
]
|
|
|
|
|
.iter()
|
|
|
|
|
{
|
2025-12-09 16:02:02 +01:00
|
|
|
let url = format!("{}/{}/catalog/1/{}", base_url, publisher, part);
|
|
|
|
|
let resp = client.get(&url).send().await.unwrap();
|
2025-12-22 20:10:17 +01:00
|
|
|
assert!(
|
|
|
|
|
resp.status().is_success(),
|
|
|
|
|
"{} status: {:?}",
|
|
|
|
|
part,
|
|
|
|
|
resp.status()
|
|
|
|
|
);
|
|
|
|
|
let ct = resp
|
|
|
|
|
.headers()
|
|
|
|
|
.get("content-type")
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_str()
|
|
|
|
|
.unwrap()
|
|
|
|
|
.to_string();
|
|
|
|
|
assert!(
|
|
|
|
|
ct.contains("application/json"),
|
|
|
|
|
"content-type for {} was {}",
|
|
|
|
|
part,
|
|
|
|
|
ct
|
|
|
|
|
);
|
2025-12-09 16:02:02 +01:00
|
|
|
let txt = resp.text().await.unwrap();
|
|
|
|
|
assert!(!txt.is_empty(), "{} should not be empty", part);
|
|
|
|
|
if *part == "catalog.base.C" {
|
2025-12-22 20:10:17 +01:00
|
|
|
assert!(
|
|
|
|
|
txt.contains(&publisher) && txt.contains("version"),
|
|
|
|
|
"base part should contain publisher and version"
|
|
|
|
|
);
|
2025-12-09 16:02:02 +01:00
|
|
|
} else {
|
|
|
|
|
// dependency/summary may be empty for this test package; at least ensure signature is present
|
2025-12-22 20:10:17 +01:00
|
|
|
assert!(
|
|
|
|
|
txt.contains("_SIGNATURE"),
|
|
|
|
|
"{} should contain a signature field",
|
|
|
|
|
part
|
|
|
|
|
);
|
2025-12-09 16:02:02 +01:00
|
|
|
}
|
|
|
|
|
}
|
2025-12-09 14:23:55 +01:00
|
|
|
}
|
2025-12-22 22:42:56 +01:00
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_file_url_without_algo() {
|
|
|
|
|
// Setup
|
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
|
|
|
let repo_path = setup_repo(&temp_dir);
|
|
|
|
|
|
|
|
|
|
let config = Config {
|
|
|
|
|
server: ServerConfig {
|
|
|
|
|
bind: vec!["127.0.0.1:0".to_string()],
|
|
|
|
|
workers: None,
|
|
|
|
|
max_connections: None,
|
|
|
|
|
reuseport: None,
|
|
|
|
|
cache_max_age: Some(3600),
|
|
|
|
|
tls_cert: None,
|
|
|
|
|
tls_key: None,
|
|
|
|
|
},
|
|
|
|
|
repository: RepositoryConfig {
|
|
|
|
|
root: repo_path.clone(),
|
|
|
|
|
mode: Some("readonly".to_string()),
|
|
|
|
|
},
|
|
|
|
|
telemetry: None,
|
|
|
|
|
publishers: None,
|
|
|
|
|
admin: None,
|
|
|
|
|
oauth2: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let repo = DepotRepo::new(&config).unwrap();
|
|
|
|
|
let state = Arc::new(repo);
|
|
|
|
|
let router = http::routes::app_router(state);
|
|
|
|
|
|
|
|
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
|
|
|
let addr = listener.local_addr().unwrap();
|
|
|
|
|
|
|
|
|
|
// Spawn server
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
http::server::run(router, listener).await.unwrap();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
let base_url = format!("http://{}", addr);
|
|
|
|
|
|
|
|
|
|
// Hash found in repo (SHA256 of compressed content likely)
|
|
|
|
|
let hash = "40dafd2319edb9b7c930958f7b8d2d59198f88c906d50811b21436008ef0746f";
|
|
|
|
|
|
|
|
|
|
// Test URL without algo
|
|
|
|
|
// Expected format: /{publisher}/file/1/{hash}
|
|
|
|
|
let url = format!("{}/test/file/1/{}", base_url, hash);
|
|
|
|
|
println!("Requesting: {}", url);
|
2026-01-18 12:51:55 +01:00
|
|
|
|
2025-12-22 22:42:56 +01:00
|
|
|
let resp = client.get(&url).send().await.unwrap();
|
2026-01-18 12:51:55 +01:00
|
|
|
|
|
|
|
|
assert_eq!(
|
|
|
|
|
resp.status(),
|
|
|
|
|
200,
|
|
|
|
|
"Should handle file URL without algorithm"
|
|
|
|
|
);
|
2025-12-22 22:42:56 +01:00
|
|
|
let _content = resp.text().await.unwrap();
|
|
|
|
|
}
|
2026-01-20 17:44:36 +01:00
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn test_multiple_publishers_default_route() {
|
|
|
|
|
let temp_dir = TempDir::new().unwrap();
|
|
|
|
|
let repo_path = temp_dir.path().join("repo_multi");
|
|
|
|
|
let mut backend = FileBackend::create(&repo_path, RepositoryVersion::V4).unwrap();
|
2026-01-25 23:17:49 +01:00
|
|
|
|
2026-01-20 17:44:36 +01:00
|
|
|
backend.add_publisher("pub1").unwrap();
|
|
|
|
|
backend.add_publisher("pub2").unwrap();
|
2026-01-25 23:17:49 +01:00
|
|
|
|
2026-01-20 17:44:36 +01:00
|
|
|
let config = Config {
|
|
|
|
|
server: ServerConfig {
|
|
|
|
|
bind: vec!["127.0.0.1:0".to_string()],
|
|
|
|
|
workers: None,
|
|
|
|
|
max_connections: None,
|
|
|
|
|
reuseport: None,
|
|
|
|
|
cache_max_age: Some(3600),
|
|
|
|
|
tls_cert: None,
|
|
|
|
|
tls_key: None,
|
|
|
|
|
},
|
|
|
|
|
repository: RepositoryConfig {
|
|
|
|
|
root: repo_path.clone(),
|
|
|
|
|
mode: Some("readonly".to_string()),
|
|
|
|
|
},
|
|
|
|
|
telemetry: None,
|
|
|
|
|
publishers: None,
|
|
|
|
|
admin: None,
|
|
|
|
|
oauth2: None,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
let repo = DepotRepo::new(&config).unwrap();
|
|
|
|
|
let state = Arc::new(repo);
|
|
|
|
|
let router = http::routes::app_router(state);
|
|
|
|
|
|
|
|
|
|
let listener = TcpListener::bind("127.0.0.1:0").await.unwrap();
|
|
|
|
|
let addr = listener.local_addr().unwrap();
|
|
|
|
|
|
|
|
|
|
tokio::spawn(async move {
|
|
|
|
|
http::server::run(router, listener).await.unwrap();
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
let client = reqwest::Client::new();
|
|
|
|
|
let base_url = format!("http://{}", addr);
|
|
|
|
|
|
|
|
|
|
let def_pub_url = format!("{}/publisher/0", base_url);
|
|
|
|
|
let resp = client.get(&def_pub_url).send().await.unwrap();
|
|
|
|
|
assert!(resp.status().is_success());
|
|
|
|
|
|
|
|
|
|
let pub_json: serde_json::Value = resp.json().await.unwrap();
|
|
|
|
|
let pubs = pub_json["publishers"].as_array().unwrap();
|
2026-01-25 23:17:49 +01:00
|
|
|
|
2026-01-20 17:44:36 +01:00
|
|
|
// CURRENT BEHAVIOR: returns 1
|
|
|
|
|
// DESIRED BEHAVIOR: returns 2
|
|
|
|
|
assert_eq!(pubs.len(), 2, "Should return all publishers");
|
2026-01-25 23:17:49 +01:00
|
|
|
|
|
|
|
|
let names: Vec<String> = pubs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|p| p["name"].as_str().unwrap().to_string())
|
|
|
|
|
.collect();
|
2026-01-20 17:44:36 +01:00
|
|
|
assert!(names.contains(&"pub1".to_string()));
|
|
|
|
|
assert!(names.contains(&"pub2".to_string()));
|
|
|
|
|
}
|