Skip to content

v0.92.0#345

Merged
dwash96 merged 118 commits intomainfrom
v0.92.0
Jan 2, 2026
Merged

v0.92.0#345
dwash96 merged 118 commits intomainfrom
v0.92.0

Conversation

@dwash96
Copy link
Copy Markdown
Collaborator

@dwash96 dwash96 commented Jan 1, 2026

No description provided.

johbo added 30 commits December 29, 2025 22:30
Phase 1 of pytest migration: Replace unittest setUp/tearDown with
function-scoped autouse fixture.

Changes:
- Add pytest import
- Create test_env autouse fixture that runs for each test
- Fixture provides same environment as setUp/tearDown via request.instance
- Remove setUp method from TestMain
- Remove tearDown method from TestMain
- Keep TestCase inheritance (will remove in Phase 2.2)
- Keep all test methods unchanged

Verification:
- All 83 tests pass (83/83)
- Tests still use self.tempdir, self.create_env_file, etc.
- Fixture handles setup/teardown automatically

Why function-scoped not module-level:
- Tests create files, repos, modify environment
- Need fresh isolation per test
- Module-level would share state and break tests

Next: Phase 2.1 - Convert assertions to plain assert (keep TestCase)
Phase 2.1 Batch 1: Convert unittest assertions to plain assert for
basic main() functionality tests while keeping TestCase base class.

Tests converted:
- test_main_with_empty_dir_no_files_on_command (no assertions)
- test_main_with_emptqy_dir_new_file
- test_main_with_empty_git_dir_new_file
- test_main_with_empty_git_dir_new_files
- test_main_with_dname_and_fname
- test_main_with_subdir_repo_fnames
- test_main_with_empty_git_dir_new_subdir_file (no assertions)
- test_setup_git
- test_check_gitignore
- test_return_coder
- test_main_with_git_config_yml (already had plain assert)

Assertions converted:
- self.assertTrue(x) → assert x
- self.assertFalse(x) → assert not x
- self.assertEqual(a, b) → assert a == b
- self.assertNotEqual(a, b) → assert a != b (or assert a is not None)
- self.assertIsInstance(x, T) → assert isinstance(x, T)

Verification:
- All 11 tests pass (11/11)
- TestCase base class still in place
- pytest assertion rewriting tested and working

Next: Batch 2 - Environment & configuration tests (20 tests)
Phase 2.1 Batch 2: Convert unittest assertions to plain assert for
environment and configuration tests while keeping TestCase base class.

Tests converted:
- test_env_file_override
- test_env_file_flag_sets_automatic_variable
- test_default_env_file_sets_automatic_variable
- test_false_vals_in_env_file
- test_true_vals_in_env_file
- test_verbose_mode_lists_env_vars
- test_yaml_config_file_loading
- test_pytest_env_vars
- test_set_env_single
- test_set_env_multiple
- test_set_env_with_spaces
- test_set_env_invalid_format
- test_api_key_single
- test_api_key_multiple
- test_api_key_invalid_format
- test_git_config_include
- test_git_config_include_directive
- test_load_dotenv_files_override

Assertions converted:
- self.assertEqual(a, b) → assert a == b
- self.assertIn(x, y) → assert x in y
- self.assertRegex(s, r) → assert re.search(r, s)
- self.assertLess(a, b) → assert a < b

Verification:
- All 18 tests pass (18/18)
- TestCase base class still in place

Progress: 29/83 tests converted (35%)
Next: Batch 3 - Model configuration tests
- test_resolve_aiderignore_path
- test_invalid_edit_format
- test_default_model_selection
- test_model_precedence
- test_model_overrides_suffix_applied
- test_model_overrides_no_match_preserves_model_name
- test_chat_language_spanish
- test_commit_language_japanese
- test_main_exit_with_git_command_not_found
- test_reasoning_effort_option
- test_thinking_tokens_option
- test_list_models_includes_metadata_models
- test_list_models_includes_all_model_sources
- test_list_models_with_direct_resource_patch
- test_stream_without_cache_no_warning
- test_cache_without_stream_no_warning

Converted unittest assertions to plain assert statements while keeping
TestCase base class. All 83 tests still passing.
- test_command_line_gitignore_files_flag
- test_add_command_gitignore_files_flag
- test_encodings_arg
- test_yes
- test_default_yes
- test_dark_mode_sets_code_theme
- test_light_mode_sets_code_theme
- test_lint_option
- test_lint_option_with_explicit_files
- test_lint_option_with_glob_pattern
- test_map_mul_option
- test_suggest_shell_commands_default/disabled/enabled
- test_detect_urls_default/disabled/enabled
- test_accepts_settings_warnings
- test_argv_file_respects_git
- test_mcp_servers_parsing

Converted all remaining unittest assertions to plain assert statements.
All 83 tests still passing. Ready to remove TestCase inheritance.
Removed TestCase base class and unittest import. All tests now use pure
pytest patterns with plain assert statements and pytest fixtures.

All 83 tests passing. Phase 2 (assertion conversion) complete!
Replace 6 separate boolean flag tests with a single parametrized test:
- test_suggest_shell_commands_* (3 tests)
- test_detect_urls_* (3 tests)

Reduces duplication while maintaining all test cases. All 83 tests pass.
Replace 3 separate API key tests with a single parametrized test:
- test_api_key_single
- test_api_key_multiple
- test_api_key_invalid_format

All 83 tests pass.
Replace 4 separate --set-env tests with a single parametrized test:
- test_set_env_single
- test_set_env_multiple
- test_set_env_with_spaces
- test_set_env_invalid_format

All 83 tests pass.
Replace 2 separate mode tests with a single parametrized test:
- test_dark_mode_sets_code_theme
- test_light_mode_sets_code_theme

All 83 tests pass.
Split large test_default_model_selection into:
- Parametrized test for 5 API key scenarios (anthropic, deepseek,
  openrouter, openai, gemini)
- Separate test for OAuth fallback when no API keys present

All 88 tests pass (83 → 88 due to test expansion).
Replace single test with 5 internal sub-tests with a parametrized test:
- test_main_args now tests 5 scenarios via parametrization
- no_auto_commits, auto_commits, defaults, no_dirty_commits, dirty_commits

All 92 tests pass.
Create dummy_io fixture to eliminate 75+ duplicate DummyInput/Output calls.
All tests now use **dummy_io instead of explicit input/output parameters.

Reduces duplication and improves test maintainability.
All 92 tests pass.
Create mock_coder fixture to eliminate duplicate Coder.create mock setup.
Uses pytest-mock's mocker fixture for cleaner mocking.

Updated 4 tests to use the new fixture:
- test_main_with_git_config_yml
- test_main_args (parametrized)
- test_false_vals_in_env_file
- test_true_vals_in_env_file

All 92 tests pass.
Created git_temp_dir fixture to provide temporary git directories:
- Added fixture yielding GitTemporaryDirectory as Path object
- Added git_temp_dir parameter to 57 test methods that need it
- Removed 36 redundant GitTemporaryDirectory() context managers
- Tests using self.tempdir (from test_env) excluded from fixture

All 92 tests passing.
Created create_env_file factory fixture:
- Converted helper method to pytest factory fixture
- Uses Path.cwd() to create env files in current test directory
- Updated 5 calls across 5 test methods to use fixture
- Removed old TestMain.create_env_file method

All 92 tests passing.
Converted all 16 @patch decorators to pytest-mock mocker.patch():
- Removed @patch decorators from test methods
- Added mocker parameter to test signatures
- Replaced unittest.mock.patch with mocker.patch calls
- Tests with multiple decorators now use multiple mocker.patch calls

All 92 tests passing.
Converted 5 additional `with patch` context managers to pytest-mock:
- test_message_file_flag
- test_encodings_arg (nested patches)
- test_mode_sets_code_theme
- test_env_file_flag_sets_automatic_variable
- test_default_env_file_sets_automatic_variable

Progress: 21/40 total patch usages converted to mocker.
All 92 tests passing.
Converted 5 additional `with patch` context managers to pytest-mock:
- test_lint_option
- test_lint_option_with_explicit_files
- test_lint_option_with_glob_pattern
- test_map_tokens_option
- test_map_tokens_option_with_non_zero_value

Progress: 27/40 total patch usages converted to mocker (67.5%).
All 92 tests passing.
Converted 2 more `with patch` context managers to pytest-mock:
- test_sonnet_and_cache_options (RepoMap)
- test_verbose_mode_lists_env_vars (sys.stdout with StringIO)

Progress: 29/40 total patch usages converted to mocker (72.5%).
All 92 tests passing.
Converted 2 more `with patch` context managers to pytest-mock:
- test_invalid_edit_format (sys.stderr with StringIO)
- test_list_models_includes_metadata_models (sys.stdout)

Progress: 31/40 total patch usages converted (77.5%).
All 92 tests passing.
Converted 4 more `with patch` context managers to pytest-mock:
- test_list_models_includes_all_model_sources (sys.stdout)
- test_check_model_accepts_settings_flag (Model.set_thinking_tokens)
- test_list_models_with_direct_resource_patch (3 patches: importlib_resources.files, sys.stdout, Model.set_reasoning_effort)

Progress: 36/40 total patch usages converted (90%).
All 92 tests passing.
Replace all unittest.mock.patch usage with pytest-mock's mocker:
- Converted test_env autouse fixture to use mocker instead of patch
- Converted 5 remaining tests using with patch() context managers:
  - test_main_exit_calls_version_check
  - test_yaml_config_file_loading
  - test_accepts_settings_warnings
  - test_model_overrides_suffix_applied
  - test_model_overrides_no_match_preserves_model_name
- Removed patch import from unittest.mock

All 92 tests passing. Code now fully uses pytest-mock for all mocking.
Remove unittest legacy patterns from test_env fixture:
- Removed request.instance pattern and all instance variable assignments
- Removed request parameter (no longer needed)
- Replaced self.tempdir with os.getcwd() in 3 locations:
  - test_setup_git (2 uses)
  - test_list_models_with_direct_resource_patch (1 use)
- Simplified fixture to only use mocker parameter

All 92 tests passing. Fixture is now more idiomatic pytest.
Replace manual environment variable manipulation with pytest's monkeypatch:
- test_check_gitignore: Use monkeypatch.setenv for GIT_CONFIG_GLOBAL
- test_env_file_override: Use monkeypatch.setenv for HOME and E
- test_yaml_config_file_loading: Use monkeypatch.setenv for HOME
- test_model_precedence: Use monkeypatch.setenv for API keys

Benefits:
- Automatic cleanup (no more del os.environ)
- More explicit and idiomatic pytest
- Cleaner code with fewer lines

All 92 tests passing. Phase 3C complete.
Complete transformation to idiomatic pytest:
- Removed TestMain class wrapper
- Converted all 74 test methods to standalone functions
- Removed 'self' parameter from all test signatures
- Added comprehensive module docstring
- Enhanced test_env fixture documentation

Code is now fully idiomatic pytest with:
- Function-based tests (no class wrapper)
- Pytest fixtures for dependency injection
- Parametrized tests for reducing duplication
- pytest-mock for all mocking
- monkeypatch for environment variables

All 92 tests passing.
Merge test_main_smoke.py into test_main.py to eliminate fixture duplication:
- Added 2 smoke tests (test_main_executes, test_main_async_executes)
- Updated smoke tests to use dummy_io fixture for consistency
- Deleted tests/basic/test_main_smoke.py
- Updated module docstring to reflect consolidated suite

Benefits:
- Single source of truth for main() tests
- No fixture duplication between files
- Simpler test organization

Total: 94 tests (92 comprehensive + 2 smoke tests)
All tests passing.
Remove test_main_executes as it duplicates existing coverage:
- test_main_with_empty_dir_no_files_on_command already tests main() execution
- 90+ other tests call main() with various arguments
- No unique value added by the redundant smoke test

Keep test_main_async_executes as it's the ONLY test for main_async().

Applying clean code principles: avoid test duplication.

Total: 93 tests (92 comprehensive + 1 async smoke test)
All tests passing.
Remove test_main_async_executes as it provides zero additional coverage:
- main() is just a thin wrapper: asyncio.run(main_async(...))
- All 92 tests calling main() already test main_async() indirectly
- All business logic lives in main_async(), tested via main()

Updated module docstring to clarify that tests cover both entry points.

Clean code principle: eliminated all test duplication.

Total: 92 comprehensive tests
All tests passing.
johbo and others added 29 commits January 1, 2026 16:53
Split 4 large test methods with multiple test cases into 21 focused tests:

1. test_confirm_ask_explicit_yes_required → 4 separate tests
   - Tests explicit_yes_required behavior with different yes flag values

2. test_confirm_ask_with_group → 5 separate tests
   - Tests ConfirmGroup behavior (all, skip, explicit_yes_required)

3. test_confirm_ask_yes_no → 1 parametrized test with 7 test cases
   - Tests various user input responses (y, n, empty, skip, all, etc.)

4. test_confirm_ask_allow_never → 5 separate tests
   - Tests "don't ask again" functionality with/without subject parameter

This improves test isolation and makes failures easier to diagnose.
Each test now validates a single behavior.

All 29 tests pass (was 4 large tests + 8 other tests).
Convert test_parse_model_with_suffix from a single test with 6 inline
assertions to a parametrized test with 6 distinct test cases:
- gpt-4o:high (valid suffix 'high')
- gpt-4o:low (valid suffix 'low')
- gpt-4o (no suffix)
- gpt-4o:unknown (unknown suffix)
- unknown-model:high (unknown model with suffix)
- empty model name

This improves test isolation and makes failures easier to diagnose,
as each test case now runs independently with descriptive test names.

All 6 test cases pass.
Standardize explanatory comments for the (return; yield) async
generator pattern across test_editblock.py and test_wholefile.py.

Updated comments now clearly explain:
- The `return` statement stops iteration immediately
- The `yield` statement makes it an async generator but is never reached
- This pattern creates an empty async generator needed for mocking

This pattern is used in mock_send functions to satisfy async generator
expectations without actually yielding any items.
- Use @pytest.mark.skipif decorator instead of manual pytest.skip()
  for Windows-specific test, following the pattern from test_repo.py:261
- Replace unittest assertion (self.assertIsNone) with pytest assertion
  (assert result is None)
- Add platform import to support skipif condition

The test now uses pytest's declarative skip marker which is more
consistent with pytest best practices and makes the skip condition
visible in test discovery.

Test properly skips on non-Windows platforms (6 passed, 1 skipped).
The TestRepoMapTypescript class (lines 331-334) contained only a
fixture setup with no actual test methods, making it dead code.
Remove incorrect '# Already had #' comment at line 53. The test input
'FFA500' did NOT have a # prefix - it was added by the code being
tested, just like all other hex color inputs in this test.
Move MockDelta and MockStreamingChunk from conftest.py fixtures to
plain classes in test_reasoning.py where they are actually used.

Changes:
- Add MockDelta and MockStreamingChunk as plain classes in test_reasoning.py
- Remove mock_delta_class and mock_streaming_chunk_class fixtures from conftest.py
- Update 3 test methods to remove fixture parameters and assignments:
  - test_send_with_reasoning_content_stream()
  - test_send_with_think_tags_stream()
  - test_send_with_reasoning_stream()

Benefits:
- Eliminates unnecessary fixture indirection
- Improves code readability and maintainability
- Follows principle of proximity (code near where it's used)
- Reduces conftest.py to only fixtures that provide real value

All 9 tests in test_reasoning.py pass.
Add blank lines before and after the yield statement in the setup
fixture to visually separate the setup phase from the teardown phase.
This improves readability by making the fixture's structure clearer.
Convert test_line_endings_validation from using a for loop to a
parametrized test for better test isolation and clarity.

Changes:
- Split into test_valid_line_endings (parametrized) and
  test_invalid_line_endings (separate test)
- Each valid line ending now runs as a separate test case
- Total: 4 parametrized test cases + 1 invalid test = 5 tests

Benefits:
- Better test isolation - each ending tested independently
- Clearer test output showing which specific ending fails
- Follows pytest best practices for parametrization

All 5 tests pass (4 parametrized + 1 invalid).
Remove 'see MR7 comment Aider-AI#683' from TODO comment as MR7 is an internal
GitLab reference that has no value in the public codebase.

The TODO still clearly indicates the bug location (io.py:970) and the
issue (UnboundLocalError) without needing the internal reference.
fix: correct pricing calculation for providers using $/token format
Add back comments that explain what each test section is validating:
- test_use_temperature_settings: Clarify three different scenarios
- test_request_timeout_from_extra_params: Explain override behavior

These comments provide context that the test names alone don't fully convey,
making the tests easier to understand and maintain.
Add back 7 comments that explain test scenarios in test methods:
- test_use_temperature_in_send_completion: 3 comments for different scenarios
- test_model_override_kwargs: 3 comments for grouping test cases
- test_model_override_kwargs_with_existing_extra_params: 1 comment

These comments preserve context about what each section tests, making the
tests easier to understand without needing to split into separate functions.
Restored explanatory comments in:
- test_model_override_kwargs_with_existing_extra_params
- test_send_completion_with_override_kwargs

These comments improve test readability by clarifying:
- Test grouping (override precedence vs nested merging)
- What assertions verify (override wins, new param added, etc.)
- Test structure (setup, checks, validation)

Addresses MR7 discussions Aider-AI#700, Aider-AI#701, Aider-AI#702.
pytest discovers and runs tests automatically without requiring
the if __name__ == '__main__' block.
Restore structural comments that explain what is being tested:
- test_model_aliases: common aliases vs non-alias
- test_parse_token_value: integer, string, k/K suffix, m/M suffix
- test_set_thinking_tokens: integer, string, decimal value
- test_get_repo_map_tokens: default, boundary, middle range cases
- test_configure_model_settings: all model case labels
Resolve conflict in test_scrape.py:
- Remove orphaned setUp method (unittest pattern not used in pytest)
- Remove orphaned test code fragment from upstream
- Remove unused imports (sys, pytest, Commands, InputOutput)
Restore the playwright installation test that was removed during
the unittest to pytest migration:
- Add pytest fixture for Commands with DummyCoder
- Convert test to pytest style with AsyncMock
- Update patch paths for new module structure (aider.commands.web)
- Verify playwright module import and scraper content return
Fix 16 test failures introduced in merge commit 2641a3f:

1. Add null-check for self.args in check_for_urls (base_coder.py:1644)
   - Fixes 15 tests that create Coder with args=None

2. Update gpt_prompts test assertion (test_copypaste_coder.py)
   - gpt_prompts is now a property returning PromptObject, not a class attr
   - Check behavior (not None, has main_system) instead of identity

3. Simplify tool_call_propagation tests (test_models.py)
   - Remove incorrect expectations for base_url/custom_llm_provider
   - These params only apply to custom JSON providers, not standard OpenAI

All 613 tests now pass.
- Remove unused imports (Model, pytest, AsyncMock, os)
- Fix comparison to None/True/False using 'is' instead of '=='
- Fix F631 assertions with trailing commas creating always-true tuples
- Apply isort and black formatting
Refator test suite, remove unittest base to avoid the async test case pitfall
Add defensive check for self.args which may not exist when
ArchitectCoder is instantiated without full initialization.
Use getattr to safely access the attribute and default to None.
- Remove xfail markers from 3 ArchitectCoder tests
- Use AsyncMock for async generate() method
- Fix assertions to include allow_tweak parameter
- Handle SwitchCoder exception raised by reply_completed()
- Add missing coder attributes (aider_commit_hashes, etc.)
- Remove boilerplate comments
Tests patch AskCoder.__init__ which bypasses normal initialization.
Fix the tests to properly set coder.args rather than adding defensive
code to production that handles a test-only scenario.
Replace manual __init__ patching with proper Coder.create() factory.
This reduces test maintenance burden by letting the coder initialize
normally instead of manually setting 10+ attributes.
Fix xfail tests - "args" attribute in ArchitectCoder
@dwash96 dwash96 merged commit 823efd5 into main Jan 2, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants