@@ -926,6 +926,101 @@ mod tests {
926926 let _ = std:: fs:: remove_dir_all ( & ws_root) ;
927927 }
928928
929+ #[ test]
930+ #[ serial]
931+ fn workspace_deploy_all_libraries_error_test ( ) {
932+ // Deploying a workspace where every member is a library should error.
933+ let temp_dir = temp_dir ( ) ;
934+ let ws_root = test_helpers:: sample_workspace_all_libraries ( & temp_dir, "deploy_all_libs" ) ;
935+
936+ let deploy = CLI {
937+ debug : false ,
938+ quiet : false ,
939+ json_output : None ,
940+ disable_update_check : false ,
941+ command : Commands :: Deploy {
942+ command : crate :: cli:: commands:: LeoDeploy {
943+ fee_options : Default :: default ( ) ,
944+ action : crate :: cli:: commands:: TransactionAction { print : false , broadcast : false , save : None } ,
945+ env_override : crate :: cli:: commands:: EnvOptions {
946+ network : Some ( NetworkName :: TestnetV0 ) ,
947+ private_key : Some ( "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH" . to_string ( ) ) ,
948+ endpoint : Some ( "http://localhost:1" . to_string ( ) ) ,
949+ consensus_heights : Some ( vec ! [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 ] ) ,
950+ ..Default :: default ( )
951+ } ,
952+ extra : crate :: cli:: commands:: ExtraOptions { yes : true , ..Default :: default ( ) } ,
953+ skip : vec ! [ ] ,
954+ build_options : Default :: default ( ) ,
955+ skip_deploy_certificate : true ,
956+ } ,
957+ } ,
958+ path : Some ( ws_root. clone ( ) ) ,
959+ home : None ,
960+ package : None ,
961+ } ;
962+
963+ create_session_if_not_set_then ( |_| {
964+ let result = run_with_args ( deploy) ;
965+ assert ! ( result. is_err( ) , "deploy of all-library workspace should fail" ) ;
966+ let err = result. unwrap_err ( ) . to_string ( ) ;
967+ assert ! (
968+ err. contains( "No deployable workspace members found" ) ,
969+ "expected 'No deployable workspace members found', got: {err}"
970+ ) ;
971+ } ) ;
972+
973+ let _ = std:: fs:: remove_dir_all ( & ws_root) ;
974+ }
975+
976+ #[ test]
977+ #[ serial]
978+ fn workspace_deploy_mixed_library_program_test ( ) {
979+ // Workspace with one library and one program member. Only the program
980+ // member should be deployed (library is skipped).
981+ let temp_dir = temp_dir ( ) ;
982+ let ws_root = test_helpers:: sample_workspace_mixed ( & temp_dir, "deploy_mixed" ) ;
983+
984+ let deploy = CLI {
985+ debug : false ,
986+ quiet : false ,
987+ json_output : None ,
988+ disable_update_check : false ,
989+ command : Commands :: Deploy {
990+ command : crate :: cli:: commands:: LeoDeploy {
991+ fee_options : Default :: default ( ) ,
992+ action : crate :: cli:: commands:: TransactionAction { print : false , broadcast : false , save : None } ,
993+ env_override : crate :: cli:: commands:: EnvOptions {
994+ network : Some ( NetworkName :: TestnetV0 ) ,
995+ private_key : Some ( "APrivateKey1zkp8CZNn3yeCseEtxuVPbDCwSyhGW6yZKUYKfgXmcpoGPWH" . to_string ( ) ) ,
996+ endpoint : Some ( "http://localhost:1" . to_string ( ) ) ,
997+ consensus_heights : Some ( vec ! [ 0 , 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 , 11 , 12 , 13 ] ) ,
998+ ..Default :: default ( )
999+ } ,
1000+ extra : crate :: cli:: commands:: ExtraOptions { yes : true , ..Default :: default ( ) } ,
1001+ skip : vec ! [ ] ,
1002+ build_options : Default :: default ( ) ,
1003+ skip_deploy_certificate : true ,
1004+ } ,
1005+ } ,
1006+ path : Some ( ws_root. clone ( ) ) ,
1007+ home : None ,
1008+ package : None ,
1009+ } ;
1010+
1011+ create_session_if_not_set_then ( |_| {
1012+ // Deploy will fail at network, but build phase should succeed.
1013+ let _ = run_with_args ( deploy) ;
1014+ } ) ;
1015+
1016+ // The app program should be built.
1017+ assert ! ( ws_root. join( "app/build/main.aleo" ) . exists( ) , "app should be built" ) ;
1018+ // utils is a library - no main.aleo output.
1019+ assert ! ( !ws_root. join( "utils/build/main.aleo" ) . exists( ) , "utils library should not produce main.aleo" ) ;
1020+
1021+ let _ = std:: fs:: remove_dir_all ( & ws_root) ;
1022+ }
1023+
9291024 #[ test]
9301025 #[ serial]
9311026 fn workspace_backward_compat_test ( ) {
@@ -1268,6 +1363,151 @@ program swap.aleo {
12681363 ws_root
12691364 }
12701365
1366+ /// Workspace where every member is a library (no `.aleo` programs).
1367+ pub ( crate ) fn sample_workspace_all_libraries ( temp_dir : & Path , name : & str ) -> PathBuf {
1368+ let ws_root = temp_dir. join ( format ! ( "ws_{name}" ) ) ;
1369+
1370+ if ws_root. exists ( ) {
1371+ std:: fs:: remove_dir_all ( & ws_root) . unwrap ( ) ;
1372+ }
1373+ std:: fs:: create_dir_all ( & ws_root) . unwrap ( ) ;
1374+
1375+ let lib_a = ws_root. join ( "lib_a" ) ;
1376+ let lib_b = ws_root. join ( "lib_b" ) ;
1377+
1378+ // Create lib_a.
1379+ std:: fs:: create_dir_all ( lib_a. join ( "src" ) ) . unwrap ( ) ;
1380+ std:: fs:: write ( lib_a. join ( "src/lib.leo" ) , "" ) . unwrap ( ) ;
1381+ std:: fs:: write (
1382+ lib_a. join ( leo_package:: MANIFEST_FILENAME ) ,
1383+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1384+ "program" : "lib_a" ,
1385+ "version" : "0.1.0" ,
1386+ "description" : "" ,
1387+ "license" : "MIT" ,
1388+ "leo" : env!( "CARGO_PKG_VERSION" ) ,
1389+ "dependencies" : null,
1390+ "dev_dependencies" : null
1391+ } ) )
1392+ . unwrap ( ) ,
1393+ )
1394+ . unwrap ( ) ;
1395+
1396+ // Create lib_b.
1397+ std:: fs:: create_dir_all ( lib_b. join ( "src" ) ) . unwrap ( ) ;
1398+ std:: fs:: write ( lib_b. join ( "src/lib.leo" ) , "" ) . unwrap ( ) ;
1399+ std:: fs:: write (
1400+ lib_b. join ( leo_package:: MANIFEST_FILENAME ) ,
1401+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1402+ "program" : "lib_b" ,
1403+ "version" : "0.1.0" ,
1404+ "description" : "" ,
1405+ "license" : "MIT" ,
1406+ "leo" : env!( "CARGO_PKG_VERSION" ) ,
1407+ "dependencies" : null,
1408+ "dev_dependencies" : null
1409+ } ) )
1410+ . unwrap ( ) ,
1411+ )
1412+ . unwrap ( ) ;
1413+
1414+ // Write workspace.json.
1415+ std:: fs:: write (
1416+ ws_root. join ( leo_package:: WORKSPACE_MANIFEST_FILENAME ) ,
1417+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1418+ "members" : [ "lib_a" , "lib_b" ]
1419+ } ) )
1420+ . unwrap ( ) ,
1421+ )
1422+ . unwrap ( ) ;
1423+
1424+ ws_root
1425+ }
1426+
1427+ /// Workspace with one library member (`utils`) and one program member
1428+ /// (`app`) that imports it.
1429+ pub ( crate ) fn sample_workspace_mixed ( temp_dir : & Path , name : & str ) -> PathBuf {
1430+ let ws_root = temp_dir. join ( format ! ( "ws_{name}" ) ) ;
1431+
1432+ if ws_root. exists ( ) {
1433+ std:: fs:: remove_dir_all ( & ws_root) . unwrap ( ) ;
1434+ }
1435+ std:: fs:: create_dir_all ( & ws_root) . unwrap ( ) ;
1436+
1437+ let utils_dir = ws_root. join ( "utils" ) ;
1438+ let app_dir = ws_root. join ( "app" ) ;
1439+
1440+ // Create utils library.
1441+ std:: fs:: create_dir_all ( utils_dir. join ( "src" ) ) . unwrap ( ) ;
1442+ std:: fs:: write (
1443+ utils_dir. join ( "src/lib.leo" ) ,
1444+ "\
1445+ const FACTOR: u32 = 2u32;
1446+ " ,
1447+ )
1448+ . unwrap ( ) ;
1449+ std:: fs:: write (
1450+ utils_dir. join ( leo_package:: MANIFEST_FILENAME ) ,
1451+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1452+ "program" : "utils" ,
1453+ "version" : "0.1.0" ,
1454+ "description" : "" ,
1455+ "license" : "MIT" ,
1456+ "leo" : env!( "CARGO_PKG_VERSION" ) ,
1457+ "dependencies" : null,
1458+ "dev_dependencies" : null
1459+ } ) )
1460+ . unwrap ( ) ,
1461+ )
1462+ . unwrap ( ) ;
1463+
1464+ // Create app program (depends on utils via workspace).
1465+ std:: fs:: create_dir_all ( app_dir. join ( "src" ) ) . unwrap ( ) ;
1466+ std:: fs:: write (
1467+ app_dir. join ( "src/main.leo" ) ,
1468+ "\
1469+ program app.aleo {
1470+ fn run(x: u32) -> u32 {
1471+ return x * utils::FACTOR;
1472+ }
1473+
1474+ @noupgrade
1475+ constructor() {}
1476+ }
1477+ " ,
1478+ )
1479+ . unwrap ( ) ;
1480+ std:: fs:: write (
1481+ app_dir. join ( leo_package:: MANIFEST_FILENAME ) ,
1482+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1483+ "program" : "app.aleo" ,
1484+ "version" : "0.1.0" ,
1485+ "description" : "" ,
1486+ "license" : "MIT" ,
1487+ "leo" : env!( "CARGO_PKG_VERSION" ) ,
1488+ "dependencies" : [ {
1489+ "name" : "utils" ,
1490+ "location" : "workspace"
1491+ } ] ,
1492+ "dev_dependencies" : null
1493+ } ) )
1494+ . unwrap ( ) ,
1495+ )
1496+ . unwrap ( ) ;
1497+
1498+ // Write workspace.json (utils first so it builds before app).
1499+ std:: fs:: write (
1500+ ws_root. join ( leo_package:: WORKSPACE_MANIFEST_FILENAME ) ,
1501+ serde_json:: to_string_pretty ( & serde_json:: json!( {
1502+ "members" : [ "utils" , "app" ]
1503+ } ) )
1504+ . unwrap ( ) ,
1505+ )
1506+ . unwrap ( ) ;
1507+
1508+ ws_root
1509+ }
1510+
12711511 pub ( crate ) fn sample_nested_package ( temp_dir : & Path ) {
12721512 let name = "nested" ;
12731513
0 commit comments