|
12 | 12 |
|
13 | 13 | use turbopath::{AnchoredSystemPathBuf, RelativeUnixPathBuf}; |
14 | 14 |
|
15 | | -use crate::{GitHashes, RepoGitIndex, SCM, test_utils}; |
| 15 | +use crate::{GitHashes, RepoGitIndex, SCM, test_utils, walk_candidate_files}; |
16 | 16 |
|
17 | 17 | fn path(s: &str) -> RelativeUnixPathBuf { |
18 | 18 | RelativeUnixPathBuf::new(s).unwrap() |
@@ -61,6 +61,23 @@ impl TestRepo { |
61 | 61 | .expect("failed to build repo index") |
62 | 62 | } |
63 | 63 |
|
| 64 | + fn build_split_repo_index(&self, prefixes: &[&str]) -> RepoGitIndex { |
| 65 | + let scm = self.scm(); |
| 66 | + let prefix_paths = prefixes |
| 67 | + .iter() |
| 68 | + .map(|prefix| path(prefix)) |
| 69 | + .collect::<Vec<_>>(); |
| 70 | + |
| 71 | + let candidates = walk_candidate_files(self.root.as_std_path(), Some(&prefix_paths)) |
| 72 | + .expect("walk candidates failed"); |
| 73 | + |
| 74 | + let mut index = scm |
| 75 | + .build_tracked_repo_index_eager() |
| 76 | + .expect("failed to build tracked repo index"); |
| 77 | + index.populate_untracked_from_candidates(candidates); |
| 78 | + index |
| 79 | + } |
| 80 | + |
64 | 81 | fn build_scoped_repo_index(&self, prefixes: &[&str]) -> RepoGitIndex { |
65 | 82 | let scm = self.scm(); |
66 | 83 | let mut index = scm |
@@ -1421,6 +1438,157 @@ fn test_superset_walk_untracked_in_other_pkg_does_not_affect_queried_pkg() { |
1421 | 1438 | assert!(pkg_b_hashes.contains_key(&path("extra5.ts"))); |
1422 | 1439 | } |
1423 | 1440 |
|
| 1441 | +// Category 6: Split walk/filter regression tests |
| 1442 | +// |
| 1443 | +// These tests validate that the two-phase approach (walk_candidate_files |
| 1444 | +// followed by populate_untracked_from_candidates) produces identical |
| 1445 | +// results to the original single-pass find_untracked_files. |
| 1446 | + |
| 1447 | +#[test] |
| 1448 | +fn test_split_walk_matches_original_path() { |
| 1449 | + let repo = TestRepo::new(); |
| 1450 | + |
| 1451 | + repo.create_file("pkg-a/src/index.ts", "a code"); |
| 1452 | + repo.create_file("pkg-a/package.json", "{}"); |
| 1453 | + repo.create_file("pkg-b/src/index.ts", "b code"); |
| 1454 | + repo.create_file("pkg-b/package.json", "{}"); |
| 1455 | + repo.create_file("package.json", "{}"); |
| 1456 | + repo.commit_all(); |
| 1457 | + |
| 1458 | + repo.create_file("pkg-a/untracked.ts", "new a"); |
| 1459 | + repo.create_file("pkg-b/untracked.ts", "new b"); |
| 1460 | + |
| 1461 | + let original = repo.build_scoped_repo_index(&["pkg-a", "pkg-b"]); |
| 1462 | + let split = repo.build_split_repo_index(&["pkg-a", "pkg-b"]); |
| 1463 | + |
| 1464 | + let orig_a = repo.get_hashes_with_index("pkg-a", &original); |
| 1465 | + let split_a = repo.get_hashes_with_index("pkg-a", &split); |
| 1466 | + assert_eq!(orig_a, split_a, "pkg-a: split walk must match original"); |
| 1467 | + |
| 1468 | + let orig_b = repo.get_hashes_with_index("pkg-b", &original); |
| 1469 | + let split_b = repo.get_hashes_with_index("pkg-b", &split); |
| 1470 | + assert_eq!(orig_b, split_b, "pkg-b: split walk must match original"); |
| 1471 | + |
| 1472 | + let no_idx_a = repo.get_hashes_no_index("pkg-a"); |
| 1473 | + let no_idx_b = repo.get_hashes_no_index("pkg-b"); |
| 1474 | + assert_eq!(split_a, no_idx_a, "pkg-a: split vs no-index"); |
| 1475 | + assert_eq!(split_b, no_idx_b, "pkg-b: split vs no-index"); |
| 1476 | +} |
| 1477 | + |
| 1478 | +#[test] |
| 1479 | +fn test_split_walk_respects_gitignore() { |
| 1480 | + let repo = TestRepo::new(); |
| 1481 | + |
| 1482 | + repo.create_gitignore(".gitignore", "*.log\nbuild/\nnode_modules/\n"); |
| 1483 | + repo.create_gitignore("pkg-b/.gitignore", "tmp/\n"); |
| 1484 | + repo.create_file("pkg-a/src/index.ts", "a"); |
| 1485 | + repo.create_file("pkg-a/package.json", "{}"); |
| 1486 | + repo.create_file("pkg-b/src/index.ts", "b"); |
| 1487 | + repo.create_file("pkg-b/package.json", "{}"); |
| 1488 | + repo.create_file("package.json", "{}"); |
| 1489 | + repo.commit_all(); |
| 1490 | + |
| 1491 | + repo.create_file("pkg-a/debug.log", "log"); |
| 1492 | + repo.create_file("pkg-a/build/out.js", "out"); |
| 1493 | + repo.create_file("pkg-a/node_modules/dep/index.js", "dep"); |
| 1494 | + repo.create_file("pkg-b/tmp/cache.dat", "cache"); |
| 1495 | + repo.create_file("pkg-b/build/out.js", "out"); |
| 1496 | + repo.create_file("pkg-a/new.ts", "new"); |
| 1497 | + repo.create_file("pkg-b/new.ts", "new"); |
| 1498 | + |
| 1499 | + let original = repo.build_scoped_repo_index(&["pkg-a", "pkg-b"]); |
| 1500 | + let split = repo.build_split_repo_index(&["pkg-a", "pkg-b"]); |
| 1501 | + |
| 1502 | + let orig_a = repo.get_hashes_with_index("pkg-a", &original); |
| 1503 | + let split_a = repo.get_hashes_with_index("pkg-a", &split); |
| 1504 | + assert_eq!( |
| 1505 | + orig_a, split_a, |
| 1506 | + "pkg-a: split must match original with gitignore" |
| 1507 | + ); |
| 1508 | + assert!(split_a.contains_key(&path("new.ts"))); |
| 1509 | + assert!(!split_a.contains_key(&path("debug.log"))); |
| 1510 | + assert!(!split_a.contains_key(&path("build/out.js"))); |
| 1511 | + assert!(!split_a.contains_key(&path("node_modules/dep/index.js"))); |
| 1512 | + |
| 1513 | + let orig_b = repo.get_hashes_with_index("pkg-b", &original); |
| 1514 | + let split_b = repo.get_hashes_with_index("pkg-b", &split); |
| 1515 | + assert_eq!( |
| 1516 | + orig_b, split_b, |
| 1517 | + "pkg-b: split must match original with gitignore" |
| 1518 | + ); |
| 1519 | + assert!(split_b.contains_key(&path("new.ts"))); |
| 1520 | + assert!(!split_b.contains_key(&path("tmp/cache.dat"))); |
| 1521 | +} |
| 1522 | + |
| 1523 | +#[test] |
| 1524 | +fn test_split_walk_with_untracked_gitignore() { |
| 1525 | + let repo = TestRepo::new(); |
| 1526 | + |
| 1527 | + repo.create_file("pkg-a/src/index.ts", "a"); |
| 1528 | + repo.create_file("pkg-a/package.json", "{}"); |
| 1529 | + repo.create_file("package.json", "{}"); |
| 1530 | + repo.commit_all(); |
| 1531 | + |
| 1532 | + repo.create_gitignore("pkg-a/.gitignore", "generated/\n"); |
| 1533 | + repo.create_file("pkg-a/generated/output.js", "out"); |
| 1534 | + repo.create_file("pkg-a/new.ts", "new"); |
| 1535 | + |
| 1536 | + let original = repo.build_scoped_repo_index(&["pkg-a"]); |
| 1537 | + let split = repo.build_split_repo_index(&["pkg-a"]); |
| 1538 | + |
| 1539 | + let orig_a = repo.get_hashes_with_index("pkg-a", &original); |
| 1540 | + let split_a = repo.get_hashes_with_index("pkg-a", &split); |
| 1541 | + |
| 1542 | + assert!(split_a.contains_key(&path("new.ts"))); |
| 1543 | + assert!(orig_a.contains_key(&path("new.ts"))); |
| 1544 | + assert!( |
| 1545 | + !split_a.contains_key(&path("generated/output.js")), |
| 1546 | + "split walk should respect untracked .gitignore" |
| 1547 | + ); |
| 1548 | + assert!( |
| 1549 | + !orig_a.contains_key(&path("generated/output.js")), |
| 1550 | + "original walk should respect untracked .gitignore" |
| 1551 | + ); |
| 1552 | + assert!(split_a.contains_key(&path(".gitignore"))); |
| 1553 | + assert!(orig_a.contains_key(&path(".gitignore"))); |
| 1554 | +} |
| 1555 | + |
| 1556 | +#[test] |
| 1557 | +fn test_split_walk_nested_gitignore_scoping() { |
| 1558 | + let repo = TestRepo::new(); |
| 1559 | + |
| 1560 | + repo.create_gitignore(".gitignore", "*.log\n"); |
| 1561 | + repo.create_gitignore("pkg-a/.gitignore", "output/\n"); |
| 1562 | + repo.create_file("pkg-a/src/index.ts", "a"); |
| 1563 | + repo.create_file("pkg-a/package.json", "{}"); |
| 1564 | + repo.create_file("pkg-b/src/index.ts", "b"); |
| 1565 | + repo.create_file("pkg-b/package.json", "{}"); |
| 1566 | + repo.create_file("package.json", "{}"); |
| 1567 | + repo.commit_all(); |
| 1568 | + |
| 1569 | + repo.create_file("pkg-a/output/bundle.js", "a bundle"); |
| 1570 | + repo.create_file("pkg-b/output/bundle.js", "b bundle"); |
| 1571 | + |
| 1572 | + let split = repo.build_split_repo_index(&["pkg-a", "pkg-b"]); |
| 1573 | + |
| 1574 | + let split_a = repo.get_hashes_with_index("pkg-a", &split); |
| 1575 | + assert!( |
| 1576 | + !split_a.contains_key(&path("output/bundle.js")), |
| 1577 | + "pkg-a output/ should be ignored by pkg-a/.gitignore" |
| 1578 | + ); |
| 1579 | + |
| 1580 | + let split_b = repo.get_hashes_with_index("pkg-b", &split); |
| 1581 | + assert!( |
| 1582 | + split_b.contains_key(&path("output/bundle.js")), |
| 1583 | + "pkg-b output/ should NOT be ignored — the output/ rule is scoped to pkg-a" |
| 1584 | + ); |
| 1585 | + |
| 1586 | + let no_idx_a = repo.get_hashes_no_index("pkg-a"); |
| 1587 | + let no_idx_b = repo.get_hashes_no_index("pkg-b"); |
| 1588 | + assert_eq!(split_a, no_idx_a, "pkg-a split vs no-index"); |
| 1589 | + assert_eq!(split_b, no_idx_b, "pkg-b split vs no-index"); |
| 1590 | +} |
| 1591 | + |
1424 | 1592 | #[test] |
1425 | 1593 | fn test_nested_gitignore_scoping() { |
1426 | 1594 | // Gitignore rules in a nested .gitignore should only apply to that |
|
0 commit comments