This guide covers how to test the list-updater CLI tool during development.
The project does not currently have automated unit tests. All testing is done manually using the CLI commands.
Pre-flight validation
Before making changes, always validate the current state:
uv run python main.py listings validate
This ensures your starting point is clean. If validation fails, fix issues first:
uv run python main.py listings fix
Testing workflow
1. Create a backup
Before testing commands that modify data:
# Backup listings.json
cp .github/scripts/listings.json .github/scripts/listings.json.bak
# Backup READMEs
cp README.md README.md.bak
cp README-Inactive.md README-Inactive.md.bak
cp README-Off-Season.md README-Off-Season.md.bak
2. Make your changes
Edit code in main.py or list_updater/ modules.
3. Test the command
Run your command with test data:
# Example: Test search command
uv run python main.py listings search --company "Test"
# Example: Test validation
uv run python main.py listings validate
# Example: Test stats
uv run python main.py listings stats
4. Check the results
For commands that modify files:
# View changes to listings.json
git diff .github/scripts/listings.json
# View changes to README
git diff README.md
5. Restore if needed
If something went wrong:
# Restore listings.json
mv .github/scripts/listings.json.bak .github/scripts/listings.json
# Restore READMEs
mv README.md.bak README.md
mv README-Inactive.md.bak README-Inactive.md
mv README-Off-Season.md.bak README-Off-Season.md
Testing specific commands
README commands
Test README generation:
Backup READMEs
cp README.md README.md.bak
cp README-Inactive.md README-Inactive.md.bak
cp README-Off-Season.md README-Off-Season.md.bak
Run update command
uv run python main.py readme update
Review changes
git diff README.md
git diff README-Inactive.md
git diff README-Off-Season.md
Check for:
- Correct category sections
- Proper emoji indicators (🔥, 🎓, 🛂, 🇺🇸)
- Accurate company links
- Correct sorting (by company name)
- File size warnings if approaching GitHub limit
Restore if needed
mv README.md.bak README.md
mv README-Inactive.md.bak README-Inactive.md
mv README-Off-Season.md.bak README-Off-Season.md
Validation commands
Test validation:
uv run python main.py listings validate
Expect output showing:
- Error count (missing fields, duplicates, empty values)
- Warning count (invalid categories, blocked companies)
- Total listings checked
Test validation with fix flag:
uv run python main.py listings validate --fix
Currently has limited auto-fix capability. Use fix command instead.
Interactive fix command
Test fix in dry-run mode:
uv run python main.py listings fix --dry-run
This shows what would be fixed without saving changes.
Test specific issue types:
# Only empty fields
uv run python main.py listings fix --type empty --dry-run
# Only duplicates
uv run python main.py listings fix --type duplicate --dry-run
# Only blocked companies
uv run python main.py listings fix --type blocked --dry-run
Test auto-fix mode:
uv run python main.py listings fix --auto --dry-run
Verify that recommended fixes are sensible:
- Empty titles → generated from category
- Invalid categories → ML classification or “Other”
- Duplicates → kept Simplify-sourced or newest
- Blocked companies → hidden
Search commands
Test basic search:
uv run python main.py listings search --company "Google"
Test with multiple filters:
uv run python main.py listings search --company "Meta" --category "Software" --active
Test limit:
uv run python main.py listings search --limit 5
Verify:
- Results match filter criteria
- Output formatting is correct (emoji status, category, dates)
- Limit is respected
Stats commands
Test formatted output:
uv run python main.py listings stats
Verify:
- Counts are accurate (active, inactive, visible, hidden)
- Category breakdown includes all categories
- Top companies list is reasonable
- Percentages are correct
Test JSON output:
uv run python main.py listings stats --json
Verify:
- Valid JSON format
- All fields present
- Can be parsed by other tools:
uv run python main.py listings stats --json | python -m json.tool
Diff commands
Test default diff (last commit):
uv run python main.py listings diff
Requires being in a git repository with commit history.
Test date-based diff:
uv run python main.py listings diff --since "2025-01-01"
Verify:
- Shows listings added since date
- Shows listings updated since date
- Dates are parsed correctly
Test commit-based diff:
uv run python main.py listings diff --commit HEAD~5
Verify:
- Shows additions, removals, status changes
- Correctly identifies what changed
Remove commands
Test mark inactive (default):
# Backup first
cp .github/scripts/listings.json .github/scripts/listings.json.bak
# Test remove
uv run python main.py listings remove --url "https://example.com/job/123"
# Verify
grep -A 10 "example.com/job/123" .github/scripts/listings.json
# Should show "active": false
# Restore
mv .github/scripts/listings.json.bak .github/scripts/listings.json
Test hide:
uv run python main.py listings remove --url "https://example.com/job/123" --hide
# Verify: should show "is_visible": false
grep -A 10 "example.com/job/123" .github/scripts/listings.json
Test permanent delete:
uv run python main.py listings remove --url "https://example.com/job/123" --permanent --confirm
# Verify: URL should not exist
grep "example.com/job/123" .github/scripts/listings.json
# Should return nothing
Permanent deletion cannot be undone. Always backup first and use a test URL.
Testing with test data
Create a minimal test listings.json for isolated testing:
# Backup production data
cp .github/scripts/listings.json .github/scripts/listings.json.prod
# Create test data
cat > .github/scripts/listings.json << 'EOF'
[
{
"company_name": "Test Corp",
"company_url": "",
"title": "Software Engineering Intern",
"date_posted": 1704067200,
"date_updated": 1704067200,
"url": "https://testcorp.com/job/1",
"terms": ["Summer 2026"],
"locations": ["San Francisco, CA"],
"active": true,
"is_visible": true,
"source": "test_user",
"id": "00000000-0000-0000-0000-000000000001",
"category": "Software Engineering",
"sponsorship": "Offers Sponsorship",
"degrees": ["Bachelor's"]
}
]
EOF
# Test commands with minimal data
uv run python main.py listings stats
uv run python main.py listings validate
uv run python main.py readme update
# Restore production data
mv .github/scripts/listings.json.prod .github/scripts/listings.json
Code quality checks
Always run before committing:
# Lint
uv run ruff check .
# Format
uv run ruff format .
# Type check
uv run mypy .
# Or run all at once
uv run task check
Common issues
Command not found
Error: No such command "listing"
Fix: Use correct command group name (listings not listing)
Module import errors
ModuleNotFoundError: No module named 'list_updater'
Fix: Ensure you’re running from repository root and dependencies are installed:
cd /path/to/Summer2026-Internships
uv sync
Type errors from mypy
error: Argument 1 has incompatible type "str | None"; expected "str"
Fix: Add type guards:
if company is not None:
filter_by_company(company)
Validation failures
❌ ERRORS (5):
- [Acme Corp] Empty title
- Duplicate URL found 2 times
Fix: Run interactive fix:
uv run python main.py listings fix
Best practices
- Always backup before testing destructive commands
- Use dry-run when available (
--dry-run flag)
- Test with small datasets to catch issues quickly
- Validate after changes to ensure data integrity
- Check git diff to see what actually changed
- Run code quality checks before committing
- Test in clean environment using fresh git clone if issues persist
Integration testing
Test the full workflow:
Start with clean state
git status # Should be clean
uv run python main.py listings validate
Make a change
Edit a listing manually in .github/scripts/listings.json or use a command
Validate changes
uv run python main.py listings validate
Update READMEs
uv run python main.py readme update
Check results
Verify README changes reflect your edit Verify stats
uv run python main.py listings stats
Counts should be consistent