Files
banderole/tests/workspace_integration_test.rs
2025-07-28 02:11:31 +04:00

476 lines
14 KiB
Rust

mod common;
use anyhow::Result;
use common::{
BundlerTestHelper, TestAssertions, TestCacheManager, TestProject, TestProjectManager,
};
use serial_test::serial;
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_npm_workspace_dependency_bundling() -> Result<()> {
println!("Testing npm workspace dependency bundling...");
// Create a workspace project with dependencies
let project = TestProject::new("workspace-test-app")
.workspace()
.with_dependency("adm-zip", "^0.5.10")
.with_dependency("commander", "^11.0.0");
let manager = TestProjectManager::create(project)?;
// Install dependencies in the workspace root
manager.install_workspace_dependencies()?;
// Verify that dependencies are installed in workspace root
let workspace_node_modules = manager.workspace_root().unwrap().join("node_modules");
assert!(
workspace_node_modules.join("adm-zip").exists(),
"adm-zip should be installed in workspace root"
);
assert!(
workspace_node_modules.join("commander").exists(),
"commander should be installed in workspace root"
);
// Bundle the workspace project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("workspace-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Dependencies:",
"adm-zip",
"commander",
"Successfully loaded adm-zip from workspace:",
"WORKSPACE_DEPENDENCY_TEST_PASSED",
],
&[],
&[],
)?;
println!("✅ npm workspace dependency bundling test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_pnpm_workspace_dependency_bundling() -> Result<()> {
println!("Testing pnpm workspace dependency bundling...");
// Create a pnpm workspace project with dependencies
let project = TestProject::new("pnpm-workspace-test-app")
.pnpm_workspace()
.with_dependency("adm-zip", "^0.5.10")
.with_dependency("js-yaml", "^4.1.0");
let manager = TestProjectManager::create(project)?;
// Try to install dependencies using pnpm, fall back to npm if pnpm is not available
match manager.install_pnpm_dependencies() {
Ok(_) => {
println!("Successfully installed pnpm workspace dependencies");
}
Err(e) => {
println!("Pnpm installation failed, falling back to npm: {e}");
manager.install_workspace_dependencies()?;
}
}
// Bundle the pnpm workspace project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("pnpm-workspace-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from pnpm workspace project!",
"Dependencies:",
"Successfully loaded adm-zip from pnpm workspace:",
"PNPM_WORKSPACE_DEPENDENCY_TEST_PASSED",
],
&[],
&[],
)?;
println!("✅ pnpm workspace dependency bundling test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_with_typescript_project() -> Result<()> {
println!("Testing workspace with TypeScript project...");
// Create a workspace TypeScript project
let project = TestProject::new("workspace-ts-app")
.workspace()
.typescript("dist")
.with_dependency("lodash", "^4.17.21")
.with_dependency("@types/lodash", "^4.14.195");
let manager = TestProjectManager::create(project)?;
// Install dependencies
manager.install_workspace_dependencies()?;
// Bundle the TypeScript workspace project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("workspace-ts-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from TypeScript project!",
"This should come from the compiled output directory",
"Marker file found: dist",
],
&[],
&[],
)?;
println!("✅ workspace TypeScript project bundling test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_nested_dependencies() -> Result<()> {
println!("Testing workspace with nested dependencies...");
// Create a workspace project that tests transitive dependency resolution
let project = TestProject::new("nested-deps-app")
.workspace()
.with_dependency("express", "^4.18.2") // Has many transitive dependencies
.with_dependency("axios", "^1.6.0"); // Also has transitive dependencies
let manager = TestProjectManager::create(project)?;
// Install dependencies
manager.install_workspace_dependencies()?;
// Create a more complex index.js that tests nested dependencies
let complex_index_js = r#"console.log("Hello from nested dependencies test!");
try {
const express = require('express');
const axios = require('axios');
console.log("Successfully loaded express:", typeof express);
console.log("Successfully loaded axios:", typeof axios);
// Test that transitive dependencies are available
const app = express();
console.log("Express app created successfully");
// Test axios functionality
console.log("Axios version:", axios.VERSION || "unknown");
console.log("NESTED_DEPENDENCIES_TEST_PASSED");
} catch (e) {
console.error("Nested dependencies test failed:", e.message);
console.log("NESTED_DEPENDENCIES_TEST_FAILED");
process.exit(1);
}
console.log("Nested dependencies test completed!");
process.exit(0);"#;
std::fs::write(manager.project_path().join("index.js"), complex_index_js)?;
// Bundle the project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("nested-deps-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_dependency_test_passes(
&executable_path,
"NESTED_DEPENDENCIES_TEST_PASSED",
)?;
println!("✅ workspace nested dependencies test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_with_bin_scripts() -> Result<()> {
println!("Testing workspace with bin scripts...");
// Create a workspace project with dependencies that have bin scripts
let project = TestProject::new("bin-scripts-app")
.workspace()
.with_dependency("semver", "^7.5.4") // Has a bin script
.with_dependency("rimraf", "^5.0.5"); // Has a bin script
let manager = TestProjectManager::create(project)?;
// Install dependencies
manager.install_workspace_dependencies()?;
// Verify that .bin directory exists in workspace
let workspace_bin = manager.workspace_root().unwrap().join("node_modules/.bin");
assert!(
workspace_bin.exists(),
".bin directory should exist in workspace node_modules"
);
// Bundle the project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("bin-scripts-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Dependencies:",
"Workspace project test completed!",
],
&[],
&[],
)?;
println!("✅ workspace bin scripts test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_project_without_own_node_modules() -> Result<()> {
println!("Testing workspace project without its own node_modules...");
// Create a workspace project where dependencies are only in workspace root
let project = TestProject::new("no-local-deps-app")
.workspace()
.with_dependency("minimist", "^1.2.8")
.with_dependency("chalk", "^5.3.0");
let manager = TestProjectManager::create(project)?;
// Install dependencies only in workspace root
manager.install_workspace_dependencies()?;
// Ensure the project itself has no node_modules
let project_node_modules = manager.project_path().join("node_modules");
if project_node_modules.exists() {
std::fs::remove_dir_all(&project_node_modules)?;
}
// Verify workspace has the dependencies
let workspace_node_modules = manager.workspace_root().unwrap().join("node_modules");
assert!(
workspace_node_modules.join("minimist").exists(),
"minimist should be in workspace node_modules"
);
// Bundle the project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("no-local-deps-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Dependencies:",
"Workspace project test completed!",
],
&[],
&[],
)?;
println!("✅ workspace project without local node_modules test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_with_peer_dependencies() -> Result<()> {
println!("Testing workspace with peer dependencies...");
// Create a project that has peer dependencies
let project = TestProject::new("peer-deps-app")
.workspace()
.with_dependency("react", "^18.2.0")
.with_dependency("prop-types", "^15.8.1"); // Has react as peer dependency
let manager = TestProjectManager::create(project)?;
// Create a custom package.json that includes peerDependencies
let package_json_with_peers = r#"{
"name": "peer-deps-app",
"version": "1.0.0",
"main": "index.js",
"scripts": {
"start": "node index.js"
},
"dependencies": {
"react": "^18.2.0",
"prop-types": "^15.8.1"
},
"peerDependencies": {
"react": "^18.0.0"
}
}"#;
std::fs::write(
manager.project_path().join("package.json"),
package_json_with_peers,
)?;
// Install dependencies
manager.install_workspace_dependencies()?;
// Bundle the project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("peer-deps-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Dependencies:",
"Workspace project test completed!",
],
&[],
&[],
)?;
println!("✅ workspace peer dependencies test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_deep_workspace_nesting() -> Result<()> {
println!("Testing deep workspace nesting...");
// Create a deeply nested workspace structure
let project = TestProject::new("deep-nested-client")
.workspace()
.with_dependency("uuid", "^9.0.1")
.with_dependency("date-fns", "^2.30.0");
let manager = TestProjectManager::create(project)?;
// Install dependencies in workspace root
manager.install_workspace_dependencies()?;
// Bundle the deeply nested project)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("deep-nested-test"),
false,
)?;
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Dependencies:",
"Workspace project test completed!",
],
&[],
&[],
)?;
println!("✅ deep workspace nesting test passed!");
Ok(())
}
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_workspace_collision_handling() -> Result<()> {
println!("Testing workspace collision handling...");
// Create a workspace project where the executable name might collide
let project = TestProject::new("collision-test")
.workspace()
.with_dependency("fs-extra", "^11.1.1");
let manager = TestProjectManager::create(project)?;
// Create a directory with the same name as the expected executable
std::fs::create_dir_all(manager.temp_dir().join("collision-test"))?;
// Install dependencies
manager.install_workspace_dependencies()?;
// Bundle the project (should handle collision automatically, no compression for speed)
let executable_path = BundlerTestHelper::bundle_project_with_compression(
manager.project_path(),
manager.temp_dir(),
Some("collision-test"),
false,
)?;
// The executable should exist with collision avoidance
assert!(
executable_path.exists(),
"Executable should exist with collision avoidance: {}",
executable_path.display()
);
// Test the bundled executable
TestAssertions::assert_executable_works(
&executable_path,
&[
"Hello from workspace project!",
"Workspace project test completed!",
],
&[],
&[],
)?;
println!("✅ workspace collision handling test passed!");
Ok(())
}
/// Cleanup function to be called after all workspace tests
#[tokio::test(flavor = "multi_thread")]
#[serial]
async fn test_zzz_cleanup_workspace_cache() -> Result<()> {
println!("Cleaning up application cache after workspace tests...");
TestCacheManager::clear_application_cache()?;
println!("✅ Workspace cache cleanup completed!");
Ok(())
}