Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 33 additions & 4 deletions crates/empack-lib/src/application/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3327,7 +3327,7 @@ async fn handle_build(session: &dyn Session, args: &BuildArgs) -> Result<()> {
let restricted_entries = collect_restricted_entries(&results);

if !restricted_entries.is_empty() {
let pending = crate::empack::restricted_build::save_pending_build(
let mut pending = crate::empack::restricted_build::save_pending_build(
session.filesystem(),
&manager.workdir,
&build_targets,
Expand All @@ -3338,6 +3338,17 @@ async fn handle_build(session: &dyn Session, args: &BuildArgs) -> Result<()> {

let download_dirs =
restricted_download_dirs(args.downloads_dir.as_deref(), &pending, &pending.entries);
pending.candidate_baseline = crate::empack::restricted_build::capture_candidate_baseline(
session.filesystem(),
&download_dirs,
)
.context("Failed to capture restricted download baseline")?;
crate::empack::restricted_build::persist_pending_build(
session.filesystem(),
&manager.workdir,
&pending,
)
.context("Failed to persist restricted download baseline")?;
crate::empack::restricted_build::import_matching_downloads_into_cache(
session.filesystem(),
&manager.workdir,
Expand Down Expand Up @@ -3856,17 +3867,35 @@ async fn maybe_open_and_wait_for_restricted_downloads(
.status()
.info("Waiting up to 5 minutes for restricted downloads to appear...");

let mut pending_for_polling = pending.clone();
if pending_for_polling.candidate_baseline.is_empty() {
pending_for_polling.candidate_baseline =
crate::empack::restricted_build::capture_candidate_baseline(
session.filesystem(),
search_dirs,
)
.context("Failed to capture restricted download baseline")?;
crate::empack::restricted_build::persist_pending_build(
session.filesystem(),
workdir,
&pending_for_polling,
)
.context("Failed to persist restricted download baseline")?;
}

for _ in 0..300 {
crate::empack::restricted_build::import_matching_downloads_into_cache(
session.filesystem(),
workdir,
pending,
&pending_for_polling,
search_dirs,
)
.context("Failed to import matching restricted downloads into cache")?;

let remaining =
crate::empack::restricted_build::missing_cached_entries(session.filesystem(), pending);
let remaining = crate::empack::restricted_build::missing_cached_entries(
session.filesystem(),
&pending_for_polling,
);
if remaining.is_empty() {
return Ok(true);
}
Expand Down
260 changes: 260 additions & 0 deletions crates/empack-lib/src/application/commands.test.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4880,6 +4880,173 @@ mod handle_build_continue_tests {
);
}

#[tokio::test]
async fn build_continue_detects_exact_deceasedcraft_section_sign_filename() {
let _guard = crate::test_support::env_lock().lock_async().await;
let cache_root = TempDir::new().expect("cache root tempdir");
let _cache_dir = unsafe { EnvVarGuard::set("EMPACK_CACHE_DIR", cache_root.path()) };

let workdir = mock_root().join("continue-import-exact-deceasedcraft-filename");
let downloads_dir = workdir.join("manual-downloads");
let exact_name = "§6No Enchant Glint 1.20.1.zip";
let download_bytes = b"manual deceasedcraft bytes".to_vec();
let filesystem =
cached_full_build_filesystem(workdir.clone()).with_binary_file_and_metadata(
downloads_dir.join(exact_name),
download_bytes.clone(),
recent_file_metadata(download_bytes.len()),
);
let session = MockCommandSession::new()
.with_filesystem(filesystem)
.with_process(MockProcessProvider::new().with_mrpack_export_side_effects());

let pending = crate::empack::restricted_build::save_pending_build(
session.filesystem(),
&workdir,
&[BuildTarget::Mrpack],
crate::empack::archive::ArchiveFormat::Zip,
&[crate::empack::RestrictedModInfo {
name: "No Enchant Glint".to_string(),
url: "https://www.curseforge.com/minecraft/texture-packs/no-enchant-glint/download/4660358"
.to_string(),
dest_path: workdir
.join("packwiz-cache")
.join("import")
.join(exact_name)
.to_string_lossy()
.to_string(),
}],
)
.expect("save pending build");

let result = handle_build(
&session,
&BuildArgs {
continue_build: true,
downloads_dir: Some(downloads_dir.to_string_lossy().to_string()),
..Default::default()
},
)
.await;

assert!(
result.is_ok(),
"exact deceasedcraft filename should be detected and continued: {result:?}"
);
assert!(
session
.filesystem()
.exists(&pending.restricted_cache_path().join(exact_name)),
"exact filename should be imported into the managed restricted cache"
);
assert!(
session
.filesystem()
.exists(&PathBuf::from(&pending.entries[0].dest_path)),
"exact cached file should be restored into the packwiz import destination"
);
}

#[tokio::test]
async fn build_continue_ignores_preexisting_recent_zip_noise_when_baseline_exists() {
let _guard = crate::test_support::env_lock().lock_async().await;
let cache_root = TempDir::new().expect("cache root tempdir");
let _cache_dir = unsafe { EnvVarGuard::set("EMPACK_CACHE_DIR", cache_root.path()) };

let workdir = mock_root().join("continue-import-baseline-noise");
let downloads_dir = workdir.join("manual-downloads");
let noise_a = downloads_dir.join("noise-a.zip");
let noise_b = downloads_dir.join("noise-b.zip");
let noise_c = downloads_dir.join("noise-c.zip");
let exact_variant = downloads_dir.join("§6No Enchant Glint 1.20.1.zip");
let noise_a_meta = recent_file_metadata("noise-a".len());
let noise_b_meta = recent_file_metadata("noise-b".len());
let noise_c_meta = recent_file_metadata("noise-c".len());
let filesystem = cached_full_build_filesystem(workdir.clone())
.with_binary_file_and_metadata(noise_a.clone(), b"noise-a".to_vec(), noise_a_meta.clone())
.with_binary_file_and_metadata(noise_b.clone(), b"noise-b".to_vec(), noise_b_meta.clone())
.with_binary_file_and_metadata(noise_c.clone(), b"noise-c".to_vec(), noise_c_meta.clone());
let session = MockCommandSession::new()
.with_filesystem(filesystem)
.with_process(MockProcessProvider::new().with_mrpack_export_side_effects());

let mut pending = crate::empack::restricted_build::save_pending_build(
session.filesystem(),
&workdir,
&[BuildTarget::Mrpack],
crate::empack::archive::ArchiveFormat::Zip,
&[crate::empack::RestrictedModInfo {
name: "No Enchant Glint".to_string(),
url: "https://www.curseforge.com/minecraft/texture-packs/no-enchant-glint/download/4660358"
.to_string(),
dest_path: workdir
.join("packwiz-cache")
.join("import")
.join("No_Enchant_Glint.zip")
.to_string_lossy()
.to_string(),
}],
)
.expect("save pending build");
pending.candidate_baseline = vec![
crate::empack::restricted_build::PendingRestrictedCandidateSnapshot {
path: noise_a.to_string_lossy().to_string(),
len: noise_a_meta.len,
modified_unix_ms: noise_a_meta.modified_unix_ms,
created_unix_ms: noise_a_meta.created_unix_ms,
},
crate::empack::restricted_build::PendingRestrictedCandidateSnapshot {
path: noise_b.to_string_lossy().to_string(),
len: noise_b_meta.len,
modified_unix_ms: noise_b_meta.modified_unix_ms,
created_unix_ms: noise_b_meta.created_unix_ms,
},
crate::empack::restricted_build::PendingRestrictedCandidateSnapshot {
path: noise_c.to_string_lossy().to_string(),
len: noise_c_meta.len,
modified_unix_ms: noise_c_meta.modified_unix_ms,
created_unix_ms: noise_c_meta.created_unix_ms,
},
];
crate::empack::restricted_build::persist_pending_build(
session.filesystem(),
&workdir,
&pending,
)
.expect("persist baseline-aware pending build");

session
.filesystem()
.write_bytes(&exact_variant, b"manual bytes")
.expect("write exact variant");
session
.filesystem_provider
.set_file_metadata(exact_variant, recent_file_metadata("manual bytes".len()));

let result = handle_build(
&session,
&BuildArgs {
continue_build: true,
downloads_dir: Some(downloads_dir.to_string_lossy().to_string()),
..Default::default()
},
)
.await;

assert!(
result.is_ok(),
"baseline-aware continue should ignore preexisting zip noise: {result:?}"
);
let restricted_cache_dir =
crate::empack::restricted_build::restricted_cache_dir(&workdir).expect("cache dir");
assert!(
session
.filesystem()
.exists(&restricted_cache_dir.join("No_Enchant_Glint.zip")),
"the new variant should still be imported into the expected cache filename"
);
}

#[tokio::test]
async fn fresh_restricted_build_non_tty_does_not_prompt_or_wait() {
let _guard = crate::test_support::env_lock().lock_async().await;
Expand Down Expand Up @@ -5183,6 +5350,99 @@ mod handle_build_continue_tests {
assert_eq!(browser_calls[0].args, expected_args);
}

#[tokio::test]
async fn interactive_wait_loop_captures_baseline_for_legacy_pending_before_polling() {
let _guard = crate::test_support::env_lock().lock_async().await;
let cache_root = TempDir::new().expect("cache root tempdir");
let _cache_dir = unsafe { EnvVarGuard::set("EMPACK_CACHE_DIR", cache_root.path()) };

let workdir = mock_root().join("continue-browser-legacy-baseline");
let import_dir = workdir.join("packwiz-cache").join("import");
let noise_a = import_dir.join("noise-a.zip");
let noise_b = import_dir.join("noise-b.zip");
let noise_c = import_dir.join("noise-c.zip");
let exact_variant = import_dir.join("§6No Enchant Glint 1.20.1.zip");
let filesystem = cached_full_build_filesystem(workdir.clone())
.with_binary_file_and_metadata(
noise_a.clone(),
b"noise-a".to_vec(),
recent_file_metadata("noise-a".len()),
)
.with_binary_file_and_metadata(
noise_b.clone(),
b"noise-b".to_vec(),
recent_file_metadata("noise-b".len()),
)
.with_binary_file_and_metadata(
noise_c.clone(),
b"noise-c".to_vec(),
recent_file_metadata("noise-c".len()),
);
let session = MockCommandSession::new()
.with_filesystem(filesystem)
.with_process(MockProcessProvider::new().with_mrpack_export_side_effects())
.with_interactive(MockInteractiveProvider::new().with_confirm(true))
.with_terminal_capabilities(tty_capabilities());

let pending = crate::empack::restricted_build::save_pending_build(
session.filesystem(),
&workdir,
&[BuildTarget::Mrpack],
crate::empack::archive::ArchiveFormat::Zip,
&[crate::empack::RestrictedModInfo {
name: "No Enchant Glint".to_string(),
url: "https://www.curseforge.com/minecraft/texture-packs/no-enchant-glint/download/4660358"
.to_string(),
dest_path: import_dir
.join("No_Enchant_Glint.zip")
.to_string_lossy()
.to_string(),
}],
)
.expect("save pending build");

let binary_files = session.filesystem_provider.binary_files.clone();
let metadata = session.filesystem_provider.metadata.clone();
let writer = std::thread::spawn(move || {
std::thread::sleep(std::time::Duration::from_millis(50));
binary_files
.lock()
.unwrap()
.insert(exact_variant.clone(), b"manual exact variant".to_vec());
metadata.lock().unwrap().insert(
exact_variant,
recent_file_metadata("manual exact variant".len()),
);
});

let result = handle_build(
&session,
&BuildArgs {
continue_build: true,
..Default::default()
},
)
.await;
writer.join().expect("join delayed variant writer");

assert!(
result.is_ok(),
"interactive wait loop should capture a baseline for legacy pending state: {result:?}"
);
assert!(
session
.filesystem()
.exists(&pending.restricted_cache_path().join("No_Enchant_Glint.zip")),
"new variant should be imported into the managed restricted cache after baseline capture"
);
assert!(
crate::empack::restricted_build::load_pending_build(session.filesystem(), &workdir)
.expect("load pending build")
.is_none(),
"pending state should clear after interactive recovery succeeds"
);
}

#[tokio::test]
async fn build_continue_yes_mode_does_not_prompt_or_wait() {
let _guard = crate::test_support::env_lock().lock_async().await;
Expand Down
Loading
Loading