|
4 | 4 |
|
5 | 5 | from agentready.assessors.stub_assessors import ( |
6 | 6 | DependencyPinningAssessor, |
| 7 | + FileSizeLimitsAssessor, |
7 | 8 | GitignoreAssessor, |
8 | 9 | ) |
9 | 10 | from agentready.models.repository import Repository |
@@ -513,3 +514,178 @@ def test_no_languages_detected(self, tmp_path): |
513 | 514 |
|
514 | 515 | # Should still give points if file exists with content |
515 | 516 | assert finding.score > 0 |
| 517 | + |
| 518 | + |
| 519 | +class TestFileSizeLimitsAssessor: |
| 520 | + """Tests for FileSizeLimitsAssessor - Issue #245 fix.""" |
| 521 | + |
| 522 | + def test_respects_gitignore_venv(self, tmp_path): |
| 523 | + """Verify .venv files are NOT counted (fixes issue #245).""" |
| 524 | + # Initialize git repository |
| 525 | + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) |
| 526 | + |
| 527 | + # Create .gitignore with .venv/ |
| 528 | + gitignore = tmp_path / ".gitignore" |
| 529 | + gitignore.write_text(".venv/\n") |
| 530 | + |
| 531 | + # Create .venv directory with large file (should be IGNORED) |
| 532 | + venv_dir = tmp_path / ".venv" |
| 533 | + venv_dir.mkdir() |
| 534 | + large_venv_file = venv_dir / "large_module.py" |
| 535 | + large_venv_file.write_text("x = 1\n" * 2000) # 2000 lines - huge |
| 536 | + |
| 537 | + # Create src directory with small file (should be counted) |
| 538 | + src_dir = tmp_path / "src" |
| 539 | + src_dir.mkdir() |
| 540 | + small_file = src_dir / "main.py" |
| 541 | + small_file.write_text("print('hello')\n" * 50) # 50 lines |
| 542 | + |
| 543 | + # Add only the tracked file to git |
| 544 | + subprocess.run(["git", "add", "src/main.py"], cwd=tmp_path, capture_output=True) |
| 545 | + |
| 546 | + repo = Repository( |
| 547 | + path=tmp_path, |
| 548 | + name="test-repo", |
| 549 | + url=None, |
| 550 | + branch="main", |
| 551 | + commit_hash="abc123", |
| 552 | + languages={"Python": 1}, |
| 553 | + total_files=1, |
| 554 | + total_lines=50, |
| 555 | + ) |
| 556 | + |
| 557 | + assessor = FileSizeLimitsAssessor() |
| 558 | + finding = assessor.assess(repo) |
| 559 | + |
| 560 | + # Should pass because .venv file is ignored |
| 561 | + assert finding.status == "pass" |
| 562 | + assert finding.score == 100.0 |
| 563 | + # Evidence should NOT mention the 2000-line file |
| 564 | + assert "2000" not in str(finding.evidence) |
| 565 | + |
| 566 | + def test_no_source_files_returns_not_applicable(self, tmp_path): |
| 567 | + """Test not_applicable when no source files exist.""" |
| 568 | + # Initialize git repository |
| 569 | + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) |
| 570 | + |
| 571 | + # Create only non-source files |
| 572 | + readme = tmp_path / "README.md" |
| 573 | + readme.write_text("# Test\n") |
| 574 | + subprocess.run(["git", "add", "README.md"], cwd=tmp_path, capture_output=True) |
| 575 | + |
| 576 | + repo = Repository( |
| 577 | + path=tmp_path, |
| 578 | + name="test-repo", |
| 579 | + url=None, |
| 580 | + branch="main", |
| 581 | + commit_hash="abc123", |
| 582 | + languages={"Markdown": 1}, |
| 583 | + total_files=1, |
| 584 | + total_lines=1, |
| 585 | + ) |
| 586 | + |
| 587 | + assessor = FileSizeLimitsAssessor() |
| 588 | + finding = assessor.assess(repo) |
| 589 | + |
| 590 | + assert finding.status == "not_applicable" |
| 591 | + |
| 592 | + def test_huge_files_detected(self, tmp_path): |
| 593 | + """Test that files >1000 lines are flagged.""" |
| 594 | + # Initialize git repository |
| 595 | + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) |
| 596 | + |
| 597 | + # Create a huge file |
| 598 | + huge_file = tmp_path / "huge_module.py" |
| 599 | + huge_file.write_text("x = 1\n" * 1500) # 1500 lines |
| 600 | + subprocess.run( |
| 601 | + ["git", "add", "huge_module.py"], cwd=tmp_path, capture_output=True |
| 602 | + ) |
| 603 | + |
| 604 | + repo = Repository( |
| 605 | + path=tmp_path, |
| 606 | + name="test-repo", |
| 607 | + url=None, |
| 608 | + branch="main", |
| 609 | + commit_hash="abc123", |
| 610 | + languages={"Python": 1}, |
| 611 | + total_files=1, |
| 612 | + total_lines=1500, |
| 613 | + ) |
| 614 | + |
| 615 | + assessor = FileSizeLimitsAssessor() |
| 616 | + finding = assessor.assess(repo) |
| 617 | + |
| 618 | + assert finding.status == "fail" |
| 619 | + assert finding.score < 70 |
| 620 | + assert "1500" in str(finding.evidence) or ">1000" in str(finding.evidence) |
| 621 | + |
| 622 | + def test_small_files_pass(self, tmp_path): |
| 623 | + """Test that all files <500 lines gives perfect score.""" |
| 624 | + # Initialize git repository |
| 625 | + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) |
| 626 | + |
| 627 | + # Create small files |
| 628 | + for i in range(5): |
| 629 | + small_file = tmp_path / f"module_{i}.py" |
| 630 | + small_file.write_text("x = 1\n" * 100) # 100 lines each |
| 631 | + subprocess.run( |
| 632 | + ["git", "add", f"module_{i}.py"], cwd=tmp_path, capture_output=True |
| 633 | + ) |
| 634 | + |
| 635 | + repo = Repository( |
| 636 | + path=tmp_path, |
| 637 | + name="test-repo", |
| 638 | + url=None, |
| 639 | + branch="main", |
| 640 | + commit_hash="abc123", |
| 641 | + languages={"Python": 5}, |
| 642 | + total_files=5, |
| 643 | + total_lines=500, |
| 644 | + ) |
| 645 | + |
| 646 | + assessor = FileSizeLimitsAssessor() |
| 647 | + finding = assessor.assess(repo) |
| 648 | + |
| 649 | + assert finding.status == "pass" |
| 650 | + assert finding.score == 100.0 |
| 651 | + assert "All 5 source files are <500 lines" in str(finding.evidence) |
| 652 | + |
| 653 | + def test_respects_gitignore_node_modules(self, tmp_path): |
| 654 | + """Verify node_modules files are NOT counted.""" |
| 655 | + # Initialize git repository |
| 656 | + subprocess.run(["git", "init"], cwd=tmp_path, capture_output=True, check=True) |
| 657 | + |
| 658 | + # Create .gitignore with node_modules/ |
| 659 | + gitignore = tmp_path / ".gitignore" |
| 660 | + gitignore.write_text("node_modules/\n") |
| 661 | + |
| 662 | + # Create node_modules directory with large JS file (should be IGNORED) |
| 663 | + nm_dir = tmp_path / "node_modules" |
| 664 | + nm_dir.mkdir() |
| 665 | + large_js = nm_dir / "large_lib.js" |
| 666 | + large_js.write_text("var x = 1;\n" * 3000) # 3000 lines |
| 667 | + |
| 668 | + # Create src directory with small JS file (should be counted) |
| 669 | + src_dir = tmp_path / "src" |
| 670 | + src_dir.mkdir() |
| 671 | + small_js = src_dir / "app.js" |
| 672 | + small_js.write_text("console.log('hi');\n" * 30) # 30 lines |
| 673 | + |
| 674 | + subprocess.run(["git", "add", "src/app.js"], cwd=tmp_path, capture_output=True) |
| 675 | + |
| 676 | + repo = Repository( |
| 677 | + path=tmp_path, |
| 678 | + name="test-repo", |
| 679 | + url=None, |
| 680 | + branch="main", |
| 681 | + commit_hash="abc123", |
| 682 | + languages={"JavaScript": 1}, |
| 683 | + total_files=1, |
| 684 | + total_lines=30, |
| 685 | + ) |
| 686 | + |
| 687 | + assessor = FileSizeLimitsAssessor() |
| 688 | + finding = assessor.assess(repo) |
| 689 | + |
| 690 | + assert finding.status == "pass" |
| 691 | + assert "3000" not in str(finding.evidence) |
0 commit comments