Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

1// SPDX-License-Identifier: Apache-2.0 2// SPDX-FileCopyrightText: 2024 The Gleam contributors 3 4use std::collections::HashMap; 5 6use camino::{Utf8Path, Utf8PathBuf}; 7use ecow::EcoString; 8use hexpm::version::Version; 9use pretty_assertions::assert_eq; 10 11use gleam_core::{ 12 Error, 13 build::Runtime, 14 config::{DenoConfig, DenoFlag, Docs, ErlangConfig, JavaScriptConfig}, 15 manifest::{Base16Checksum, Manifest, ManifestPackage, ManifestPackageSource}, 16 paths::ProjectPaths, 17 requirement::Requirement, 18}; 19 20use crate::dependencies::*; 21 22#[test] 23fn list_manifest_format() { 24 let mut buffer = vec![]; 25 let manifest = Manifest { 26 requirements: HashMap::new(), 27 packages: vec![ 28 ManifestPackage { 29 name: "root".into(), 30 version: Version::parse("1.0.0").unwrap(), 31 build_tools: ["gleam".into()].into(), 32 otp_app: None, 33 requirements: vec![], 34 source: ManifestPackageSource::Hex { 35 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 36 }, 37 }, 38 ManifestPackage { 39 name: "aaa".into(), 40 version: Version::new(0, 4, 2), 41 build_tools: ["rebar3".into(), "make".into()].into(), 42 otp_app: Some("aaa_app".into()), 43 requirements: vec!["zzz".into(), "gleam_stdlib".into()], 44 source: ManifestPackageSource::Hex { 45 outer_checksum: Base16Checksum(vec![3, 22]), 46 }, 47 }, 48 ManifestPackage { 49 name: "zzz".into(), 50 version: Version::new(0, 4, 0), 51 build_tools: ["mix".into()].into(), 52 otp_app: None, 53 requirements: vec![], 54 source: ManifestPackageSource::Hex { 55 outer_checksum: Base16Checksum(vec![3, 22]), 56 }, 57 }, 58 ], 59 }; 60 list_manifest_packages(&mut buffer, manifest).unwrap(); 61 assert_eq!( 62 std::str::from_utf8(&buffer).unwrap(), 63 "Package Version 64------- ------- 65root 1.0.0 66aaa 0.4.2 67zzz 0.4.0 68" 69 ) 70} 71 72#[test] 73fn tree_format() { 74 let mut buffer = vec![]; 75 let manifest = Manifest { 76 requirements: HashMap::new(), 77 packages: vec![ 78 ManifestPackage { 79 name: "deps_proj".into(), 80 version: Version::parse("1.0.0").unwrap(), 81 build_tools: [].into(), 82 otp_app: None, 83 requirements: vec!["gleam_regexp".into(), "gleam_stdlib".into()], 84 source: ManifestPackageSource::Hex { 85 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 86 }, 87 }, 88 ManifestPackage { 89 name: "gleam_stdlib".into(), 90 version: Version::new(0, 52, 0), 91 build_tools: ["rebar3".into(), "make".into()].into(), 92 otp_app: Some("aaa_app".into()), 93 requirements: vec![], 94 source: ManifestPackageSource::Hex { 95 outer_checksum: Base16Checksum(vec![3, 22]), 96 }, 97 }, 98 ManifestPackage { 99 name: "gleam_regexp".into(), 100 version: Version::new(1, 0, 0), 101 build_tools: ["mix".into()].into(), 102 otp_app: None, 103 requirements: vec!["gleam_stdlib".into()], 104 source: ManifestPackageSource::Hex { 105 outer_checksum: Base16Checksum(vec![3, 22]), 106 }, 107 }, 108 ], 109 }; 110 111 let options = TreeOptions { 112 package: None, 113 invert: None, 114 }; 115 116 let root_package_name = EcoString::from("deps_proj"); 117 118 list_package_and_dependencies_tree( 119 &mut buffer, 120 options, 121 manifest.packages.clone(), 122 root_package_name, 123 ) 124 .unwrap(); 125 assert_eq!( 126 std::str::from_utf8(&buffer).unwrap(), 127 r#"deps_proj v1.0.0 128├── gleam_regexp v1.0.0 129│ └── gleam_stdlib v0.52.0 130└── gleam_stdlib v0.52.0 131"# 132 ) 133} 134 135#[test] 136fn tree_package_format() { 137 let mut buffer = vec![]; 138 let manifest = Manifest { 139 requirements: HashMap::new(), 140 packages: vec![ 141 ManifestPackage { 142 name: "gleam_stdlib".into(), 143 version: Version::new(0, 52, 0), 144 build_tools: ["rebar3".into(), "make".into()].into(), 145 otp_app: Some("aaa_app".into()), 146 requirements: vec![], 147 source: ManifestPackageSource::Hex { 148 outer_checksum: Base16Checksum(vec![3, 22]), 149 }, 150 }, 151 ManifestPackage { 152 name: "deps_proj".into(), 153 version: Version::parse("1.0.0").unwrap(), 154 build_tools: [].into(), 155 otp_app: None, 156 requirements: vec!["gleam_stdlib".into(), "gleam_regexp".into()], 157 source: ManifestPackageSource::Hex { 158 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 159 }, 160 }, 161 ManifestPackage { 162 name: "gleam_regexp".into(), 163 version: Version::new(1, 0, 0), 164 build_tools: ["mix".into()].into(), 165 otp_app: None, 166 requirements: vec!["gleam_stdlib".into()], 167 source: ManifestPackageSource::Hex { 168 outer_checksum: Base16Checksum(vec![3, 22]), 169 }, 170 }, 171 ], 172 }; 173 let options = TreeOptions { 174 package: Some("gleam_regexp".to_string()), 175 invert: None, 176 }; 177 178 let root_package_name = EcoString::from("deps_proj"); 179 180 list_package_and_dependencies_tree( 181 &mut buffer, 182 options, 183 manifest.packages.clone(), 184 root_package_name, 185 ) 186 .unwrap(); 187 assert_eq!( 188 std::str::from_utf8(&buffer).unwrap(), 189 r#"gleam_regexp v1.0.0 190└── gleam_stdlib v0.52.0 191"# 192 ) 193} 194 195#[test] 196fn tree_invert_format() { 197 let mut buffer = vec![]; 198 let manifest = Manifest { 199 requirements: HashMap::new(), 200 packages: vec![ 201 ManifestPackage { 202 name: "gleam_stdlib".into(), 203 version: Version::new(0, 52, 0), 204 build_tools: ["rebar3".into(), "make".into()].into(), 205 otp_app: Some("aaa_app".into()), 206 requirements: vec![], 207 source: ManifestPackageSource::Hex { 208 outer_checksum: Base16Checksum(vec![3, 22]), 209 }, 210 }, 211 ManifestPackage { 212 name: "deps_proj".into(), 213 version: Version::parse("1.0.0").unwrap(), 214 build_tools: [].into(), 215 otp_app: None, 216 requirements: vec!["gleam_stdlib".into(), "gleam_regexp".into()], 217 source: ManifestPackageSource::Hex { 218 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 219 }, 220 }, 221 ManifestPackage { 222 name: "gleam_regexp".into(), 223 version: Version::new(1, 0, 0), 224 build_tools: ["mix".into()].into(), 225 otp_app: None, 226 requirements: vec!["gleam_stdlib".into()], 227 source: ManifestPackageSource::Hex { 228 outer_checksum: Base16Checksum(vec![3, 22]), 229 }, 230 }, 231 ], 232 }; 233 let options = TreeOptions { 234 package: None, 235 invert: Some("gleam_stdlib".to_string()), 236 }; 237 238 let root_package_name = EcoString::from("deps_proj"); 239 240 list_package_and_dependencies_tree( 241 &mut buffer, 242 options, 243 manifest.packages.clone(), 244 root_package_name, 245 ) 246 .unwrap(); 247 assert_eq!( 248 std::str::from_utf8(&buffer).unwrap(), 249 r#"gleam_stdlib v0.52.0 250├── deps_proj v1.0.0 251└── gleam_regexp v1.0.0 252 └── deps_proj v1.0.0 253"# 254 ) 255} 256 257#[test] 258fn list_tree_invalid_package_format() { 259 let mut buffer = vec![]; 260 let manifest = Manifest { 261 requirements: HashMap::new(), 262 packages: vec![ 263 ManifestPackage { 264 name: "gleam_stdlib".into(), 265 version: Version::new(0, 52, 0), 266 build_tools: ["rebar3".into(), "make".into()].into(), 267 otp_app: Some("aaa_app".into()), 268 requirements: vec![], 269 source: ManifestPackageSource::Hex { 270 outer_checksum: Base16Checksum(vec![3, 22]), 271 }, 272 }, 273 ManifestPackage { 274 name: "gleam_regexp".into(), 275 version: Version::new(1, 0, 0), 276 build_tools: ["mix".into()].into(), 277 otp_app: None, 278 requirements: vec!["gleam_stdlib".into()], 279 source: ManifestPackageSource::Hex { 280 outer_checksum: Base16Checksum(vec![3, 22]), 281 }, 282 }, 283 ManifestPackage { 284 name: "root".into(), 285 version: Version::parse("1.0.0").unwrap(), 286 build_tools: [].into(), 287 otp_app: None, 288 requirements: vec!["gleam_regexp".into(), "gleam_stdlib".into()], 289 source: ManifestPackageSource::Hex { 290 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 291 }, 292 }, 293 ], 294 }; 295 let options = TreeOptions { 296 package: Some("zzzzzz".to_string()), 297 invert: None, 298 }; 299 300 let root_package_name = EcoString::from("deps_proj"); 301 302 list_package_and_dependencies_tree( 303 &mut buffer, 304 options, 305 manifest.packages.clone(), 306 root_package_name, 307 ) 308 .unwrap(); 309 assert_eq!( 310 std::str::from_utf8(&buffer).unwrap(), 311 r#"Package not found. Please check the package name. 312"# 313 ) 314} 315 316#[test] 317fn parse_gleam_add_specifier_invalid_semver() { 318 assert!(parse_gleam_add_specifier("some_package@1.2.3.4").is_err()); 319} 320 321#[test] 322fn parse_gleam_add_specifier_non_numeric_version() { 323 assert!(parse_gleam_add_specifier("some_package@not_a_version").is_err()); 324} 325 326#[test] 327fn parse_gleam_add_specifier_default() { 328 let provided = "some_package"; 329 let expected = Requirement::hex(">= 0.0.0").unwrap(); 330 let (package, version) = parse_gleam_add_specifier(provided).unwrap(); 331 assert_eq!(version, expected); 332 assert_eq!("some_package", package); 333} 334 335#[test] 336fn parse_gleam_add_specifier_major_only() { 337 let provided = "wobble@1"; 338 let expected = Requirement::hex(">= 1.0.0 and < 2.0.0").unwrap(); 339 let (package, version) = parse_gleam_add_specifier(provided).unwrap(); 340 assert_eq!(version, expected); 341 assert_eq!("wobble", package); 342} 343 344#[test] 345fn parse_gleam_add_specifier_major_and_minor() { 346 let provided = "wibble@1.2"; 347 let expected = Requirement::hex(">= 1.2.0 and < 2.0.0").unwrap(); 348 let (package, version) = parse_gleam_add_specifier(provided).unwrap(); 349 assert_eq!(version, expected); 350 assert_eq!("wibble", package); 351} 352 353#[test] 354fn parse_gleam_add_specifier_major_minor_and_patch() { 355 let provided = "bobble@1.2.3"; 356 let expected = Requirement::hex("1.2.3").unwrap(); 357 let (package, version) = parse_gleam_add_specifier(provided).unwrap(); 358 assert_eq!(version, expected); 359 assert_eq!("bobble", package); 360} 361 362#[test] 363fn missing_local_packages() { 364 let manifest = Manifest { 365 requirements: HashMap::new(), 366 packages: vec![ 367 ManifestPackage { 368 name: "root".into(), 369 version: Version::parse("1.0.0").unwrap(), 370 build_tools: ["gleam".into()].into(), 371 otp_app: None, 372 requirements: vec![], 373 source: ManifestPackageSource::Hex { 374 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 375 }, 376 }, 377 ManifestPackage { 378 name: "local1".into(), 379 version: Version::parse("1.0.0").unwrap(), 380 build_tools: ["gleam".into()].into(), 381 otp_app: None, 382 requirements: vec![], 383 source: ManifestPackageSource::Hex { 384 outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]), 385 }, 386 }, 387 ManifestPackage { 388 name: "local2".into(), 389 version: Version::parse("3.0.0").unwrap(), 390 build_tools: ["gleam".into()].into(), 391 otp_app: None, 392 requirements: vec![], 393 source: ManifestPackageSource::Hex { 394 outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]), 395 }, 396 }, 397 ], 398 }; 399 let mut extra = LocalPackages { 400 packages: [ 401 ("local2".into(), Version::parse("2.0.0").unwrap()), 402 ("local3".into(), Version::parse("3.0.0").unwrap()), 403 ] 404 .into(), 405 } 406 .missing_local_packages(&manifest, "root"); 407 extra.sort(); 408 assert_eq!( 409 extra, 410 [ 411 &ManifestPackage { 412 name: "local1".into(), 413 version: Version::parse("1.0.0").unwrap(), 414 build_tools: ["gleam".into()].into(), 415 otp_app: None, 416 requirements: vec![], 417 source: ManifestPackageSource::Hex { 418 outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]), 419 }, 420 }, 421 &ManifestPackage { 422 name: "local2".into(), 423 version: Version::parse("3.0.0").unwrap(), 424 build_tools: ["gleam".into()].into(), 425 otp_app: None, 426 requirements: vec![], 427 source: ManifestPackageSource::Hex { 428 outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]), 429 }, 430 }, 431 ] 432 ) 433} 434 435#[test] 436fn extra_local_packages() { 437 let mut extra = LocalPackages { 438 packages: [ 439 ("local1".into(), Version::parse("1.0.0").unwrap()), 440 ("local2".into(), Version::parse("2.0.0").unwrap()), 441 ("local3".into(), Version::parse("3.0.0").unwrap()), 442 ] 443 .into(), 444 } 445 .extra_local_packages(&Manifest { 446 requirements: HashMap::new(), 447 packages: vec![ 448 ManifestPackage { 449 name: "local1".into(), 450 version: Version::parse("1.0.0").unwrap(), 451 build_tools: ["gleam".into()].into(), 452 otp_app: None, 453 requirements: vec![], 454 source: ManifestPackageSource::Hex { 455 outer_checksum: Base16Checksum(vec![1, 2, 3, 4, 5]), 456 }, 457 }, 458 ManifestPackage { 459 name: "local2".into(), 460 version: Version::parse("3.0.0").unwrap(), 461 build_tools: ["gleam".into()].into(), 462 otp_app: None, 463 requirements: vec![], 464 source: ManifestPackageSource::Hex { 465 outer_checksum: Base16Checksum(vec![4, 5]), 466 }, 467 }, 468 ], 469 }); 470 extra.sort(); 471 assert_eq!( 472 extra, 473 [ 474 ("local2".into(), Version::new(2, 0, 0)), 475 ("local3".into(), Version::new(3, 0, 0)), 476 ] 477 ) 478} 479 480#[test] 481fn provide_wrong_package() { 482 let mut provided = HashMap::new(); 483 let project_paths = crate::project_paths_at_current_directory_without_toml(); 484 let result = provide_local_package( 485 "wrong_name".into(), 486 Utf8Path::new("./test/hello_world"), 487 Utf8Path::new("./"), 488 &project_paths, 489 &mut provided, 490 &mut vec!["root".into(), "subpackage".into()], 491 ); 492 match result { 493 Err(Error::WrongDependencyProvided { 494 expected, found, .. 495 }) => { 496 assert_eq!(expected, "wrong_name"); 497 assert_eq!(found, "hello_world"); 498 } 499 _ => { 500 panic!("Expected WrongDependencyProvided error") 501 } 502 } 503} 504 505#[test] 506fn provide_existing_package() { 507 let mut provided = HashMap::new(); 508 let project_paths = crate::project_paths_at_current_directory_without_toml(); 509 510 let result = provide_local_package( 511 "hello_world".into(), 512 Utf8Path::new("./test/hello_world"), 513 Utf8Path::new("./"), 514 &project_paths, 515 &mut provided, 516 &mut vec!["root".into(), "subpackage".into()], 517 ); 518 assert_eq!( 519 result, 520 Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap()) 521 ); 522 523 let result = provide_local_package( 524 "hello_world".into(), 525 Utf8Path::new("./test/hello_world"), 526 Utf8Path::new("./"), 527 &project_paths, 528 &mut provided, 529 &mut vec!["root".into(), "subpackage".into()], 530 ); 531 assert_eq!( 532 result, 533 Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap()) 534 ); 535} 536 537#[test] 538fn provide_conflicting_package() { 539 let mut provided = HashMap::new(); 540 let project_paths = crate::project_paths_at_current_directory_without_toml(); 541 let result = provide_local_package( 542 "hello_world".into(), 543 Utf8Path::new("./test/hello_world"), 544 Utf8Path::new("./"), 545 &project_paths, 546 &mut provided, 547 &mut vec!["root".into(), "subpackage".into()], 548 ); 549 assert_eq!( 550 result, 551 Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap()) 552 ); 553 554 let result = provide_package( 555 "hello_world".into(), 556 Utf8PathBuf::from("./test/other"), 557 ProvidedPackageSource::Local { 558 path: Utf8Path::new("./test/other").to_path_buf(), 559 }, 560 &project_paths, 561 &mut provided, 562 &mut vec!["root".into(), "subpackage".into()], 563 ); 564 match result { 565 Err(Error::ProvidedDependencyConflict { package, .. }) => { 566 assert_eq!(package, "hello_world"); 567 } 568 _ => { 569 panic!("Expected ProvidedDependencyConflict error") 570 } 571 } 572} 573 574#[test] 575fn provided_is_absolute() { 576 let mut provided = HashMap::new(); 577 let project_paths = crate::project_paths_at_current_directory_without_toml(); 578 let result = provide_local_package( 579 "hello_world".into(), 580 Utf8Path::new("./test/hello_world"), 581 Utf8Path::new("./"), 582 &project_paths, 583 &mut provided, 584 &mut vec!["root".into(), "subpackage".into()], 585 ); 586 assert_eq!( 587 result, 588 Ok(hexpm::version::Range::new("== 0.1.0".into()).unwrap()) 589 ); 590 let package = provided.get("hello_world").unwrap().clone(); 591 match package.source { 592 ProvidedPackageSource::Local { path } => { 593 assert!(path.is_absolute()) 594 } 595 _ => { 596 panic!("Provide_local_package provided a package that is not local!") 597 } 598 } 599} 600 601#[test] 602fn provided_recursive() { 603 let mut provided = HashMap::new(); 604 let project_paths = crate::project_paths_at_current_directory_without_toml(); 605 let result = provide_local_package( 606 "hello_world".into(), 607 Utf8Path::new("./test/hello_world"), 608 Utf8Path::new("./"), 609 &project_paths, 610 &mut provided, 611 &mut vec!["root".into(), "hello_world".into(), "subpackage".into()], 612 ); 613 assert_eq!( 614 result, 615 Err(Error::PackageCycle { 616 packages: vec!["subpackage".into(), "hello_world".into()], 617 }) 618 ) 619} 620 621#[test] 622fn provided_local_to_hex() { 623 let provided_package = ProvidedPackage { 624 version: Version::new(1, 0, 0), 625 source: ProvidedPackageSource::Local { 626 path: "canonical/path/to/package".into(), 627 }, 628 requirements: [ 629 ( 630 "req_1".into(), 631 hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 632 ), 633 ( 634 "req_2".into(), 635 hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 636 ), 637 ] 638 .into(), 639 }; 640 641 let hex_package = hexpm::Package { 642 name: "package".into(), 643 repository: "local".into(), 644 releases: vec![hexpm::Release { 645 version: Version::new(1, 0, 0), 646 retirement_status: None, 647 outer_checksum: vec![], 648 meta: (), 649 requirements: [ 650 ( 651 "req_1".into(), 652 hexpm::Dependency { 653 requirement: hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 654 optional: false, 655 app: None, 656 repository: None, 657 }, 658 ), 659 ( 660 "req_2".into(), 661 hexpm::Dependency { 662 requirement: hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 663 optional: false, 664 app: None, 665 repository: None, 666 }, 667 ), 668 ] 669 .into(), 670 }], 671 }; 672 673 assert_eq!( 674 provided_package.to_hex_package(&"package".into()), 675 hex_package 676 ); 677} 678 679#[test] 680fn provided_git_to_hex() { 681 let provided_package = ProvidedPackage { 682 version: Version::new(1, 0, 0), 683 source: ProvidedPackageSource::Git { 684 repo: "https://github.com/gleam-lang/gleam.git".into(), 685 commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(), 686 path: None, 687 }, 688 requirements: [ 689 ( 690 "req_1".into(), 691 hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 692 ), 693 ( 694 "req_2".into(), 695 hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 696 ), 697 ] 698 .into(), 699 }; 700 701 let hex_package = hexpm::Package { 702 name: "package".into(), 703 repository: "local".into(), 704 releases: vec![hexpm::Release { 705 version: Version::new(1, 0, 0), 706 retirement_status: None, 707 outer_checksum: vec![], 708 meta: (), 709 requirements: [ 710 ( 711 "req_1".into(), 712 hexpm::Dependency { 713 requirement: hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 714 optional: false, 715 app: None, 716 repository: None, 717 }, 718 ), 719 ( 720 "req_2".into(), 721 hexpm::Dependency { 722 requirement: hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 723 optional: false, 724 app: None, 725 repository: None, 726 }, 727 ), 728 ] 729 .into(), 730 }], 731 }; 732 733 assert_eq!( 734 provided_package.to_hex_package(&"package".into()), 735 hex_package 736 ); 737} 738 739#[test] 740fn provided_local_to_manifest() { 741 let provided_package = ProvidedPackage { 742 version: Version::new(1, 0, 0), 743 source: ProvidedPackageSource::Local { 744 path: "canonical/path/to/package".into(), 745 }, 746 requirements: [ 747 ( 748 "req_1".into(), 749 hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 750 ), 751 ( 752 "req_2".into(), 753 hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 754 ), 755 ] 756 .into(), 757 }; 758 759 let manifest_package = ManifestPackage { 760 name: "package".into(), 761 version: Version::new(1, 0, 0), 762 otp_app: None, 763 build_tools: vec!["gleam".into()], 764 requirements: vec!["req_1".into(), "req_2".into()], 765 source: ManifestPackageSource::Local { 766 path: "canonical/path/to/package".into(), 767 }, 768 }; 769 770 assert_eq!( 771 provided_package.to_manifest_package("package"), 772 manifest_package 773 ); 774} 775 776#[test] 777fn provided_git_to_manifest() { 778 let provided_package = ProvidedPackage { 779 version: Version::new(1, 0, 0), 780 source: ProvidedPackageSource::Git { 781 repo: "https://github.com/gleam-lang/gleam.git".into(), 782 commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(), 783 path: None, 784 }, 785 requirements: [ 786 ( 787 "req_1".into(), 788 hexpm::version::Range::new("~> 1.0.0".into()).unwrap(), 789 ), 790 ( 791 "req_2".into(), 792 hexpm::version::Range::new("== 1.0.0".into()).unwrap(), 793 ), 794 ] 795 .into(), 796 }; 797 798 let manifest_package = ManifestPackage { 799 name: "package".into(), 800 version: Version::new(1, 0, 0), 801 otp_app: None, 802 build_tools: vec!["gleam".into()], 803 requirements: vec!["req_1".into(), "req_2".into()], 804 source: ManifestPackageSource::Git { 805 repo: "https://github.com/gleam-lang/gleam.git".into(), 806 commit: "bd9fe02f72250e6a136967917bcb1bdccaffa3c8".into(), 807 path: None, 808 }, 809 }; 810 811 assert_eq!( 812 provided_package.to_manifest_package("package"), 813 manifest_package 814 ); 815} 816 817#[test] 818fn validate_git_dependency_path_accepts_subdir() { 819 assert!( 820 validate_git_dependency_path( 821 "package", 822 Utf8Path::new("subdir"), 823 "https://github.com/gleam-lang/gleam.git" 824 ) 825 .is_ok() 826 ); 827} 828 829#[test] 830fn validate_git_dependency_path_accepts_nested_subdir() { 831 assert!( 832 validate_git_dependency_path( 833 "package", 834 Utf8Path::new("packages/subdir"), 835 "https://github.com/gleam-lang/gleam.git" 836 ) 837 .is_ok() 838 ); 839} 840 841#[test] 842fn validate_git_dependency_path_rejects_parent_traversal() { 843 let result = validate_git_dependency_path( 844 "package", 845 Utf8Path::new("../escape"), 846 "https://github.com/gleam-lang/gleam.git", 847 ); 848 assert!(matches!( 849 result, 850 Err(Error::GitDependencyPathNotFound { .. }) 851 )); 852} 853 854#[test] 855fn validate_git_dependency_path_rejects_nested_parent_traversal() { 856 let result = validate_git_dependency_path( 857 "package", 858 Utf8Path::new("packages/../../escape"), 859 "https://github.com/gleam-lang/gleam.git", 860 ); 861 assert!(matches!( 862 result, 863 Err(Error::GitDependencyPathNotFound { .. }) 864 )); 865} 866 867#[test] 868fn validate_git_dependency_path_rejects_absolute_path() { 869 let result = validate_git_dependency_path( 870 "package", 871 Utf8Path::new("/etc/passwd"), 872 "https://github.com/gleam-lang/gleam.git", 873 ); 874 assert!(matches!( 875 result, 876 Err(Error::GitDependencyPathNotFound { .. }) 877 )); 878} 879 880#[test] 881fn git_repo_dir_name_produces_expected_name() { 882 assert_eq!( 883 git_repo_dir_name("https://github.com/gleam-lang/gleam.git", "main"), 884 "https-github.com-gleam-lang-gleam-edfec3bb6bbeb6c1" 885 ); 886} 887 888#[test] 889fn git_repo_dir_name_different_refs_produce_different_names() { 890 assert_eq!( 891 git_repo_dir_name("https://github.com/gleam-lang/gleam.git", "main"), 892 "https-github.com-gleam-lang-gleam-edfec3bb6bbeb6c1" 893 ); 894 assert_eq!( 895 git_repo_dir_name("https://github.com/gleam-lang/gleam.git", "v1.0.0"), 896 "https-github.com-gleam-lang-gleam-90203c669cc91eee" 897 ); 898} 899 900#[test] 901fn verified_requirements_equality_with_canonicalized_paths() { 902 let temp_dir = tempfile::tempdir().expect("Failed to create a temp directory"); 903 let temp_path = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) 904 .expect("Path should be valid UTF-8"); 905 906 let sub_dir = temp_path.join("subdir"); 907 std::fs::create_dir(&sub_dir).expect("Failed to create a subdir"); 908 let file_path = sub_dir.join("file.txt"); 909 fs::write(&file_path, "content").expect("Failed to write to file"); 910 911 let canonical_path = std::fs::canonicalize(&file_path).expect("Failed to canonicalize path"); 912 let relative_path = temp_path.join("./subdir/../subdir/./file.txt"); 913 914 let requirements1 = HashMap::from([( 915 EcoString::from("dep1"), 916 Requirement::Path { 917 path: Utf8PathBuf::from(canonical_path.to_str().expect("Path should be valid UTF-8")), 918 }, 919 )]); 920 921 let requirements2 = HashMap::from([( 922 EcoString::from("dep1"), 923 Requirement::Path { 924 path: Utf8PathBuf::from(relative_path.to_string()), 925 }, 926 )]); 927 928 assert!( 929 is_same_requirements(&requirements1, &requirements2, &temp_path) 930 .expect("Requirements should be the same") 931 ); 932} 933 934#[test] 935fn test_path_dependency_config_updates() { 936 let temp_dir = tempfile::tempdir().expect("Failed to create a temp directory"); 937 let root_path = Utf8PathBuf::from_path_buf(temp_dir.path().to_path_buf()) 938 .expect("Path should be valid UTF-8"); 939 let paths = ProjectPaths::new(root_path.clone()); 940 let dep_path = root_path.join("dep"); 941 std::fs::create_dir_all(&dep_path).expect("Failed to create dependency directory"); 942 let build_packages_dir = root_path.join("build").join("packages"); 943 std::fs::create_dir_all(&build_packages_dir) 944 .expect("Failed to create build/packages directory"); 945 946 let config = "name = \"dep\" 947version = \"1.0.0\" 948 949[dependencies] 950"; 951 let dep_config_path = dep_path.join("gleam.toml"); 952 fs::write(&dep_config_path, config).expect("Failed to write to manifest file"); 953 954 let requirements = HashMap::from([( 955 EcoString::from("dep"), 956 Requirement::Path { 957 path: Utf8PathBuf::from("dep"), 958 }, 959 )]); 960 961 // Initial check testing 962 963 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 964 assert!(!unchanged, "fresh always needs resolution"); 965 966 let fingerprint_path = build_packages_dir.join("dep.config_fingerprint"); 967 assert!(fingerprint_path.exists(), "fingerprint must exist"); 968 969 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 970 assert!(unchanged, "is unchanged"); 971 972 // Set fingerprint mtime to some time in the past. This causes the mtime check to fail, 973 // so it moves on to checking the fingerprint itself. 974 let past = std::time::SystemTime::now() - std::time::Duration::from_secs(10); 975 let past = filetime::FileTime::from_system_time(past); 976 filetime::set_file_mtime(&fingerprint_path, past).unwrap(); 977 978 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 979 assert!(unchanged, "mtime is outdated, but content has not changed"); 980 981 // Writing new content means the mtime and the fingerprint checks will fail. 982 let config = "name = \"dep\" 983version = \"1.0.0\" 984 985[dependencies] 986blah = \">= 1.0.0\" 987"; 988 fs::write(&dep_config_path, config).unwrap(); 989 990 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 991 assert!(!unchanged, "content has changed"); 992 993 // Run again, to ensure that the fingerprint has been updated. 994 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 995 assert!(unchanged, "no changes since last run"); 996 997 // Test that mtime is checked first, and that content is not checked when the mtime 998 // is still valid. We do this by having having content that would fail the fingerprint 999 // check, but having a fresh mtime so it never gets checked. 1000 // This can never happen in reality as you can't update the content without updating 1001 // the mtime, but we create the situation in this test to verify the short-circuiting 1002 // behaviour. 1003 let config = "name = \"dep\" 1004version = \"1.0.0\" 1005 1006[dependencies] 1007blah = \">= 1.0.0\" 1008wub = \">= 1.0.0\" 1009"; 1010 fs::write(&dep_config_path, config).unwrap(); 1011 let future = std::time::SystemTime::now() + std::time::Duration::from_secs(10); 1012 let future = filetime::FileTime::from_system_time(future); 1013 filetime::set_file_mtime(&fingerprint_path, future).unwrap(); 1014 1015 let unchanged = path_dependency_configs_unchanged(&requirements, &paths).unwrap(); 1016 assert!(unchanged, "fingerprint is outdated, but mtime is not"); 1017} 1018 1019fn create_testable_unlock_manifest( 1020 packages: Vec<(EcoString, Version, Vec<EcoString>)>, 1021 requirements: Vec<(EcoString, EcoString)>, 1022) -> Manifest { 1023 let manifest_packages = packages 1024 .into_iter() 1025 .map(|(name, version, requirements)| ManifestPackage { 1026 name, 1027 version, 1028 build_tools: vec!["gleam".into()], 1029 otp_app: None, 1030 requirements, 1031 source: ManifestPackageSource::Hex { 1032 outer_checksum: Base16Checksum(vec![]), 1033 }, 1034 }) 1035 .collect(); 1036 1037 let root_requirements = requirements 1038 .into_iter() 1039 .map(|(name, range)| { 1040 ( 1041 name, 1042 Requirement::Hex { 1043 version: hexpm::version::Range::new(range.into()).unwrap(), 1044 }, 1045 ) 1046 }) 1047 .collect(); 1048 1049 Manifest { 1050 packages: manifest_packages, 1051 requirements: root_requirements, 1052 } 1053} 1054 1055#[test] 1056fn test_unlock_package() { 1057 let mut locked = HashMap::from([ 1058 ("package_a".into(), Version::new(1, 0, 0)), 1059 ("package_b".into(), Version::new(2, 0, 0)), 1060 ("package_c".into(), Version::new(3, 0, 0)), 1061 ("package_d".into(), Version::new(4, 0, 0)), 1062 ]); 1063 1064 let packages = vec![ 1065 ( 1066 "package_a".into(), 1067 Version::new(1, 0, 0), 1068 vec!["package_b".into()], 1069 ), 1070 ( 1071 "package_b".into(), 1072 Version::new(2, 0, 0), 1073 vec!["package_c".into()], 1074 ), 1075 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1076 ("package_d".into(), Version::new(4, 0, 0), vec![]), 1077 ]; 1078 1079 let manifest = create_testable_unlock_manifest(packages, Vec::new()); 1080 1081 let packages_to_unlock = vec!["package_a".into()]; 1082 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1083 1084 assert!(!locked.contains_key("package_a")); 1085 assert!(!locked.contains_key("package_b")); 1086 assert!(!locked.contains_key("package_c")); 1087 assert!(locked.contains_key("package_d")); 1088} 1089 1090#[test] 1091fn test_unlock_package_without_manifest() { 1092 let mut locked = HashMap::from([ 1093 ("package_a".into(), Version::new(1, 0, 0)), 1094 ("package_b".into(), Version::new(2, 0, 0)), 1095 ("package_c".into(), Version::new(3, 0, 0)), 1096 ]); 1097 1098 let packages_to_unlock = vec!["package_a".into()]; 1099 unlock_packages(&mut locked, &packages_to_unlock, None).unwrap(); 1100 1101 assert!(!locked.contains_key("package_a")); 1102 assert!(locked.contains_key("package_b")); 1103 assert!(locked.contains_key("package_c")); 1104} 1105 1106#[test] 1107fn test_unlock_nonexistent_package() { 1108 let initial_locked = HashMap::from([ 1109 ("package_a".into(), Version::new(1, 0, 0)), 1110 ("package_b".into(), Version::new(2, 0, 0)), 1111 ]); 1112 1113 let packages = vec![ 1114 ( 1115 "package_a".into(), 1116 Version::new(1, 0, 0), 1117 vec!["package_b".into()], 1118 ), 1119 ("package_b".into(), Version::new(2, 0, 0), vec![]), 1120 ]; 1121 1122 let manifest = create_testable_unlock_manifest(packages, Vec::new()); 1123 1124 let packages_to_unlock = vec!["nonexistent_package".into()]; 1125 let mut locked = initial_locked.clone(); 1126 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1127 1128 assert_eq!( 1129 initial_locked, locked, 1130 "Locked packages should remain unchanged" 1131 ); 1132} 1133 1134#[test] 1135fn test_unlock_multiple_packages() { 1136 let mut locked = HashMap::from([ 1137 ("package_a".into(), Version::new(1, 0, 0)), 1138 ("package_b".into(), Version::new(2, 0, 0)), 1139 ("package_c".into(), Version::new(3, 0, 0)), 1140 ("package_d".into(), Version::new(4, 0, 0)), 1141 ("package_e".into(), Version::new(5, 0, 0)), 1142 ]); 1143 1144 let packages = vec![ 1145 ( 1146 "package_a".into(), 1147 Version::new(1, 0, 0), 1148 vec!["package_b".into()], 1149 ), 1150 ( 1151 "package_b".into(), 1152 Version::new(2, 0, 0), 1153 vec!["package_c".into()], 1154 ), 1155 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1156 ( 1157 "package_d".into(), 1158 Version::new(4, 0, 0), 1159 vec!["package_e".into()], 1160 ), 1161 ("package_e".into(), Version::new(5, 0, 0), vec![]), 1162 ]; 1163 1164 let manifest = create_testable_unlock_manifest(packages, Vec::new()); 1165 1166 let packages_to_unlock = vec!["package_a".into(), "package_d".into()]; 1167 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1168 1169 assert!(!locked.contains_key("package_a")); 1170 assert!(!locked.contains_key("package_b")); 1171 assert!(!locked.contains_key("package_c")); 1172 assert!(!locked.contains_key("package_d")); 1173 assert!(!locked.contains_key("package_e")); 1174} 1175 1176#[test] 1177fn test_unlock_packages_empty_input() { 1178 let initial_locked = HashMap::from([ 1179 ("package_a".into(), Version::new(1, 0, 0)), 1180 ("package_b".into(), Version::new(2, 0, 0)), 1181 ]); 1182 1183 let packages = vec![ 1184 ( 1185 "package_a".into(), 1186 Version::new(1, 0, 0), 1187 vec!["package_b".into()], 1188 ), 1189 ("package_b".into(), Version::new(2, 0, 0), vec![]), 1190 ]; 1191 1192 let manifest = create_testable_unlock_manifest(packages, Vec::new()); 1193 1194 let packages_to_unlock: Vec<EcoString> = vec![]; 1195 let mut locked = initial_locked.clone(); 1196 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1197 1198 assert_eq!( 1199 initial_locked, locked, 1200 "Locked packages should remain unchanged when no packages are specified to unlock" 1201 ); 1202} 1203 1204#[test] 1205fn test_unlock_package_preserve_shared_deps() { 1206 let mut locked = HashMap::from([ 1207 ("package_a".into(), Version::new(1, 0, 0)), 1208 ("package_b".into(), Version::new(2, 0, 0)), 1209 ("package_c".into(), Version::new(3, 0, 0)), 1210 ]); 1211 1212 let packages = vec![ 1213 ( 1214 "package_a".into(), 1215 Version::new(1, 0, 0), 1216 vec!["package_c".into()], 1217 ), 1218 ( 1219 "package_b".into(), 1220 Version::new(2, 0, 0), 1221 vec!["package_c".into()], 1222 ), 1223 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1224 ]; 1225 1226 let manifest = create_testable_unlock_manifest(packages, Vec::new()); 1227 1228 let packages_to_unlock: Vec<EcoString> = vec!["package_a".into()]; 1229 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1230 1231 assert!(!locked.contains_key("package_a")); 1232 assert!(locked.contains_key("package_b")); 1233 assert!(locked.contains_key("package_c")); 1234} 1235 1236#[test] 1237fn test_unlock_package_with_root_dep() { 1238 let mut locked = HashMap::from([ 1239 ("package_a".into(), Version::new(1, 0, 0)), 1240 ("package_b".into(), Version::new(2, 0, 0)), 1241 ("package_c".into(), Version::new(3, 0, 0)), 1242 ]); 1243 1244 let packages = vec![ 1245 ( 1246 "package_a".into(), 1247 Version::new(1, 0, 0), 1248 vec!["package_b".into()], 1249 ), 1250 ( 1251 "package_b".into(), 1252 Version::new(2, 0, 0), 1253 vec!["package_c".into()], 1254 ), 1255 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1256 ]; 1257 1258 let requirements = vec![("package_b".into(), ">= 2.0.0".into())]; 1259 1260 let manifest = create_testable_unlock_manifest(packages, requirements); 1261 1262 let packages_to_unlock: Vec<EcoString> = vec!["package_a".into()]; 1263 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1264 1265 assert!(!locked.contains_key("package_a")); 1266 assert!(locked.contains_key("package_b")); 1267 assert!(locked.contains_key("package_c")); 1268} 1269 1270#[test] 1271fn test_unlock_root_dep_package() { 1272 let mut locked = HashMap::from([ 1273 ("package_a".into(), Version::new(1, 0, 0)), 1274 ("package_b".into(), Version::new(2, 0, 0)), 1275 ("package_c".into(), Version::new(3, 0, 0)), 1276 ]); 1277 1278 let packages = vec![ 1279 ( 1280 "package_a".into(), 1281 Version::new(1, 0, 0), 1282 vec!["package_b".into()], 1283 ), 1284 ("package_b".into(), Version::new(2, 0, 0), vec![]), 1285 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1286 ]; 1287 1288 let requirements = vec![("package_a".into(), ">= 1.0.0".into())]; 1289 1290 let manifest = create_testable_unlock_manifest(packages, requirements); 1291 1292 let packages_to_unlock: Vec<EcoString> = vec!["package_a".into()]; 1293 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1294 1295 assert!(!locked.contains_key("package_a")); 1296 assert!(!locked.contains_key("package_b")); 1297 assert!(locked.contains_key("package_c")); 1298} 1299 1300#[test] 1301fn test_unlock_package_with_and_without_root_dep() { 1302 let mut locked = HashMap::from([ 1303 ("package_a".into(), Version::new(1, 0, 0)), 1304 ("package_b".into(), Version::new(2, 0, 0)), 1305 ("package_c".into(), Version::new(3, 0, 0)), 1306 ]); 1307 1308 let packages = vec![ 1309 ( 1310 "package_a".into(), 1311 Version::new(1, 0, 0), 1312 vec!["package_b".into(), "package_c".into()], 1313 ), 1314 ("package_b".into(), Version::new(2, 0, 0), vec![]), 1315 ("package_c".into(), Version::new(3, 0, 0), vec![]), 1316 ]; 1317 1318 let requirements = vec![("package_b".into(), ">= 2.0.0".into())]; 1319 1320 let manifest = create_testable_unlock_manifest(packages, requirements); 1321 1322 let packages_to_unlock: Vec<EcoString> = vec!["package_a".into()]; 1323 unlock_packages(&mut locked, &packages_to_unlock, Some(&manifest)).unwrap(); 1324 1325 assert!(!locked.contains_key("package_a")); 1326 assert!(locked.contains_key("package_b")); 1327 assert!(!locked.contains_key("package_c")); 1328} 1329 1330fn manifest_package(name: &str, version: &str, requirements: Vec<EcoString>) -> ManifestPackage { 1331 ManifestPackage { 1332 name: name.into(), 1333 version: Version::parse(version).unwrap(), 1334 build_tools: ["gleam".into()].into(), 1335 otp_app: None, 1336 requirements, 1337 source: ManifestPackageSource::Hex { 1338 outer_checksum: Base16Checksum(vec![1, 2, 3, 4]), 1339 }, 1340 } 1341} 1342 1343fn package_config( 1344 dependencies: HashMap<EcoString, Requirement>, 1345 dev_dependencies: HashMap<EcoString, Requirement>, 1346) -> PackageConfig { 1347 PackageConfig { 1348 name: "the_package".into(), 1349 version: Version::parse("1.0.0").unwrap(), 1350 gleam_version: None, 1351 licences: vec![], 1352 description: "".into(), 1353 documentation: Docs { pages: vec![] }, 1354 dependencies, 1355 dev_dependencies, 1356 repository: None, 1357 links: vec![], 1358 erlang: ErlangConfig { 1359 application_start_module: None, 1360 application_start_argument: None, 1361 extra_applications: vec![], 1362 }, 1363 javascript: JavaScriptConfig { 1364 typescript_declarations: false, 1365 source_maps: false, 1366 runtime: Runtime::NodeJs, 1367 deno: DenoConfig { 1368 allow_env: DenoFlag::AllowAll, 1369 allow_sys: true, 1370 allow_hrtime: true, 1371 allow_net: DenoFlag::AllowAll, 1372 allow_ffi: true, 1373 allow_read: DenoFlag::AllowAll, 1374 allow_run: DenoFlag::AllowAll, 1375 allow_write: DenoFlag::AllowAll, 1376 allow_all: true, 1377 unstable: true, 1378 location: None, 1379 }, 1380 }, 1381 target: Target::Erlang, 1382 internal_modules: None, 1383 } 1384} 1385 1386#[test] 1387fn test_remove_do_nothing() { 1388 let config = package_config( 1389 HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]), 1390 HashMap::from([("b".into(), Requirement::hex("~>2.0").unwrap())]), 1391 ); 1392 1393 let mut manifest = Manifest { 1394 requirements: HashMap::from([ 1395 ("a".into(), Requirement::hex("~>1.0").unwrap()), 1396 ("b".into(), Requirement::hex("~>2.0").unwrap()), 1397 ]), 1398 packages: vec![ 1399 manifest_package("a", "1.0.0", vec![]), 1400 manifest_package("b", "2.0.8", vec![]), 1401 ], 1402 }; 1403 1404 let manifest_copy = manifest.clone(); 1405 1406 remove_extra_requirements(&config, &mut manifest).unwrap(); 1407 1408 assert_eq!(manifest.requirements, manifest_copy.requirements); 1409 assert_eq!(manifest.packages, manifest_copy.packages); 1410} 1411 1412#[test] 1413fn test_remove_simple() { 1414 let config = package_config(HashMap::new(), HashMap::new()); 1415 1416 let mut manifest = Manifest { 1417 requirements: HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]), 1418 packages: vec![manifest_package("a", "1.0.0", vec![])], 1419 }; 1420 1421 remove_extra_requirements(&config, &mut manifest).unwrap(); 1422 1423 assert_eq!(manifest.requirements, config.dependencies); 1424 assert_eq!(manifest.packages, vec![]); 1425} 1426 1427#[test] 1428fn test_remove_package_with_transitive_dependencies() { 1429 let config = package_config(HashMap::new(), HashMap::new()); 1430 1431 let mut manifest = Manifest { 1432 requirements: HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]), 1433 packages: vec![ 1434 manifest_package("a", "1.0.0", vec!["b".into()]), 1435 manifest_package("b", "1.2.3", vec!["c".into()]), 1436 manifest_package("c", "2.0.0", vec![]), 1437 ], 1438 }; 1439 1440 remove_extra_requirements(&config, &mut manifest).unwrap(); 1441 1442 assert_eq!(manifest.requirements, config.dependencies); 1443 assert_eq!(manifest.packages, vec![]); 1444} 1445 1446#[test] 1447fn test_remove_package_with_shared_transitive_dependencies() { 1448 let config = package_config( 1449 HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]), 1450 HashMap::new(), 1451 ); 1452 1453 let mut manifest = Manifest { 1454 requirements: HashMap::from([ 1455 ("a".into(), Requirement::hex("~>1.0").unwrap()), 1456 ("b".into(), Requirement::hex("~>1.0").unwrap()), 1457 ]), 1458 packages: vec![ 1459 manifest_package("a", "1.0.0", vec!["c".into()]), 1460 manifest_package("b", "1.2.3", vec!["c".into(), "d".into()]), 1461 manifest_package("c", "2.0.0", vec![]), 1462 manifest_package("d", "0.1.0", vec![]), 1463 ], 1464 }; 1465 1466 remove_extra_requirements(&config, &mut manifest).unwrap(); 1467 1468 assert_eq!(manifest.requirements, config.dependencies); 1469 assert_eq!( 1470 manifest.packages, 1471 vec![ 1472 manifest_package("a", "1.0.0", vec!["c".into()]), 1473 manifest_package("c", "2.0.0", vec![]), 1474 ] 1475 ); 1476} 1477 1478#[test] 1479fn test_remove_package_that_is_also_a_transitive_dependency() { 1480 let config = package_config( 1481 HashMap::from([("a".into(), Requirement::hex("~>1.0").unwrap())]), 1482 HashMap::new(), 1483 ); 1484 1485 let mut manifest = Manifest { 1486 requirements: HashMap::from([ 1487 ("a".into(), Requirement::hex("~>1.0").unwrap()), 1488 ("b".into(), Requirement::hex("~>1.0").unwrap()), 1489 ]), 1490 packages: vec![ 1491 manifest_package("a", "1.0.0", vec!["b".into(), "c".into()]), 1492 manifest_package("b", "1.2.3", vec!["c".into(), "d".into()]), 1493 manifest_package("c", "2.0.0", vec![]), 1494 manifest_package("d", "0.1.0", vec![]), 1495 ], 1496 }; 1497 1498 let manifest_copy = manifest.clone(); 1499 1500 remove_extra_requirements(&config, &mut manifest).unwrap(); 1501 1502 assert_eq!(manifest.requirements, config.dependencies); 1503 assert_eq!(manifest.packages, manifest_copy.packages); 1504} 1505 1506#[test] 1507fn test_pretty_print_major_versions_available() { 1508 let versions = vec![ 1509 ( 1510 "very_long_package_name".to_string(), 1511 (Version::new(18, 382, 43), Version::new(19, 0, 38)), 1512 ), 1513 ( 1514 "gleam_stdlib".to_string(), 1515 (Version::new(0, 45, 0), Version::new(1, 0, 0)), 1516 ), 1517 ( 1518 "short_name".to_string(), 1519 (Version::new(1, 0, 0), Version::new(2, 0, 0)), 1520 ), 1521 ] 1522 .into_iter() 1523 .collect(); 1524 1525 let output = pretty_print_major_versions_available(versions); 1526 1527 insta::assert_snapshot!(output); 1528} 1529 1530#[test] 1531fn test_pretty_print_version_updates() { 1532 let versions = vec![ 1533 ( 1534 "gleam_stdlib".to_string(), 1535 (Version::new(0, 45, 0), Version::new(0, 46, 0)), 1536 ), 1537 ( 1538 "wisp".to_string(), 1539 (Version::new(2, 1, 0), Version::new(2, 1, 1)), 1540 ), 1541 ( 1542 "very_long_package_name".to_string(), 1543 (Version::new(12, 12, 12), Version::new(120, 12, 12)), 1544 ), 1545 ] 1546 .into_iter() 1547 .collect(); 1548 1549 let output = pretty_print_version_updates(versions); 1550 1551 insta::assert_snapshot!(output); 1552} 1553 1554#[test] 1555fn test_pretty_print_outdated_versions() { 1556 let versions = vec![ 1557 ( 1558 "gleam_stdlib".to_string(), 1559 (Version::new(0, 45, 0), Version::new(0, 46, 0)), 1560 ), 1561 ( 1562 "wisp".to_string(), 1563 (Version::new(2, 1, 0), Version::new(2, 1, 1)), 1564 ), 1565 ( 1566 "very_long_package_name".to_string(), 1567 (Version::new(12, 12, 12), Version::new(120, 12, 12)), 1568 ), 1569 ] 1570 .into_iter() 1571 .collect(); 1572 1573 let output = pretty_print_outdated_versions(12, versions); 1574 1575 insta::assert_snapshot!(output); 1576} 1577 1578#[test] 1579fn test_pretty_print_outdated_versions_no_updates() { 1580 let versions: HashMap<String, (Version, Version)> = HashMap::new(); 1581 1582 let output = pretty_print_outdated_versions(12, versions); 1583 1584 insta::assert_snapshot!(output); 1585} 1586 1587#[test] 1588fn test_ensure_packages_exist_locally_all_present() { 1589 let manifest = Manifest { 1590 requirements: HashMap::new(), 1591 packages: vec![ 1592 manifest_package("package_a", "1.0.0", vec![]), 1593 manifest_package("package_b", "2.0.0", vec![]), 1594 manifest_package("package_c", "3.0.0", vec![]), 1595 ], 1596 }; 1597 1598 let packages_to_check = vec!["package_a".into(), "package_b".into()]; 1599 let result = dependency_manager::ensure_packages_exist_locally(&manifest, &packages_to_check); 1600 1601 assert!(result.is_ok(), "All packages exist, should return Ok"); 1602} 1603 1604#[test] 1605fn test_ensure_packages_exist_locally_some_missing() { 1606 let manifest = Manifest { 1607 requirements: HashMap::new(), 1608 packages: vec![ 1609 manifest_package("package_a", "1.0.0", vec![]), 1610 manifest_package("package_b", "2.0.0", vec![]), 1611 ], 1612 }; 1613 1614 let packages_to_check = vec![ 1615 "package_a".into(), 1616 "package_b".into(), 1617 "missing_package".into(), 1618 "another_missing".into(), 1619 ]; 1620 let result = dependency_manager::ensure_packages_exist_locally(&manifest, &packages_to_check); 1621 1622 match result { 1623 Err(Error::PackagesToUpdateNotExist { packages }) => { 1624 assert_eq!(packages.len(), 2); 1625 assert!(packages.contains(&"missing_package".into())); 1626 assert!(packages.contains(&"another_missing".into())); 1627 } 1628 _ => panic!("Expected PackagesToUpdateNotExist error"), 1629 } 1630}