Skip to content

Test: start a test suite for migration utils functions#8225

Open
CarolineDenis wants to merge 79 commits into
issue-8124-8126-fix-7660-7682-2-after-review-2from
issue-8224
Open

Test: start a test suite for migration utils functions#8225
CarolineDenis wants to merge 79 commits into
issue-8124-8126-fix-7660-7682-2-after-review-2from
issue-8224

Conversation

@CarolineDenis

@CarolineDenis CarolineDenis commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #8224
Fixes #8049
Fixes #8042

Checklist

  • Self-review the PR after opening it to make sure the changes look good and
    self-explanatory (or properly documented)
  • Add relevant issue to release milestone
  • Add pr to documentation list
  • Add automated tests
  • Add a reverse migration if a migration is present in the PR
  • Add migration function to
    def fix_schema_config(stdout: WriteToStdOut | None = None):

Testing instructions

Summary by CodeRabbit

  • New Features

    • Updated the key migration flow to use a centralized routine for de-duplicating discipline-scoped app resource directories.
  • Bug Fixes

    • Ensured selectseries defaults are consistently enforced to False during migrations.
    • Improved command error handling by explicitly re-raising exceptions after logging.
  • Tests

    • Added extensive unit and Django TestCase coverage for schema reader/writer defaults, idempotent bulk-create behavior, deduplication logic, and key-migration command ordering/dispatch.
    • Added additional command/database integration tests to verify idempotency across repeated runs.

@CarolineDenis CarolineDenis added this to the 7.12.0.7 milestone Jun 16, 2026
@github-project-automation github-project-automation Bot moved this to 📋Back Log in General Tester Board Jun 16, 2026
@CarolineDenis CarolineDenis linked an issue Jun 16, 2026 that may be closed by this pull request
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 72cf9b98-6fe9-4daf-ba55-ec2e4513eb1f

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds comprehensive unit tests for migration utilities and management command, extracts discipline resource directory deduplication to a dedicated module, and consolidates migration helper imports. The test suite spans bulk creation, schema operations, default data initialization, deduplication, and tectonic ranks. Integration tests verify the migration command is idempotent across multiple runs without creating duplicates.

Changes

Migration Utils Unit Test Suite

Layer / File(s) Summary
Tests package initialization and bulk_create idempotent helper
specifyweb/specify/migration_utils/tests/__init__.py, specifyweb/specify/migration_utils/tests/test_bulk_create.py
Initializes the tests package and adds BulkCreateSplocaleitemstrIdempotentTest, which mocks model/queryset behavior and verifies idempotent bulk creation returns correct count, filter invocation, and bulk-create payload for locale item strings.
Helper 0003 cotype picklist migration test
specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py
Adds Helper0003CotypePicklistTest that configures mocked apps to return model stubs, mocks queryset chains to represent missing container-item and item-string records, and verifies Splocalecontaineritem and Splocaleitemstr creation calls.
Schema reader utility tests
specifyweb/specify/migration_utils/tests/test_schema_reader.py
Adds SchemaReaderTests covering hidden override detection and normalization, schema_overrides.json loading and field key normalization, hidden-field aggregation per discipline, explicit override filtering, and pure string/type conversion helpers.
Schema writer ORM interaction tests
specifyweb/specify/migration_utils/tests/test_schema_writer.py
Adds SchemaWriterTests covering bulk-create helper invocation for default schema config and revert logic that deletes container items and locale item-string records via queryset filtering.
Deduplication utility tests
specifyweb/specify/migration_utils/tests/test_deduplication.py
Adds comprehensive test coverage for SQL-based schema config deduplication via database cursor, ORM-based container/item/string deduplication with targeted __in filtering, combined orchestration, and idempotent behavior when no duplicates exist.
Default collection object type and treedef initialization tests
specifyweb/specify/migration_utils/tests/test_default_cots.py
Adds tests for default data setup: collection type creation per discipline, treedef discipline/institution link assignment, picklist item creation when picklists are created, cotype picklist per-collection creation, and taxon treedef discipline updates.
Tectonic ranks migration helper tests
specifyweb/specify/migration_utils/tests/test_tectonic_ranks.py
Adds tests validating default tectonic rank creation across missing disciplines, root node creation with logging per tree, discipline treedef creation via bulk-create and queryset update, and discipline link fixes.
Helper 0031 selectseries default test
specifyweb/specify/migration_utils/tests/test_helper_0031_add_default_for_selectseries.py
Adds MakeSelectSeriesFalseTests that creates Spquery records with various smushed values, invokes the migration helper, and verifies only None and False are normalized to False while True remains unchanged.
Helper 0039 loan and gift agent fields test
specifyweb/specify/migration_utils/tests/test_helper_0039_agent_fields_for_loan_and_gift.py
Adds UpdateLoanAndGiftAgentFieldsTests that patches the schema config update helper, verifies 10 calls with expected hidden=True default.

Management Command Test Infrastructure and Integration Tests

Layer / File(s) Summary
Migration test base classes and helpers
specifyweb/specify/management/commands/tests/test_migration_base.py
Creates MigrationCommandTestCase with wired stdout/stderr and helpers for recording and asserting ordered section calls; creates MigrationDatabaseTestCase extending ApiTests.
Command dispatch and section ordering tests
specifyweb/specify/management/commands/tests/command_tests.py
Adds KeyMigrationCommandTests verifying full pipeline section dispatch order with verbose stdout, subset selection without verbose, apply_patches dispatch with apps registry, and unknown function error handling.
Section invocation order and log_and_run tests
specifyweb/specify/management/commands/tests/test_run_key_migration_functions_order.py
Adds KeyMigrationSectionTests verifying section execution order with captured calls, logs without stdout, and fix_tectonic_ranks/fix_misc invoke internal functions in expected sequence.
App resource directory migration section tests
specifyweb/specify/management/commands/tests/app_resource_tests.py
Adds AppResourceTests verifying create_missing_app_resource_dirs writes formatted summary when stdout is provided, produces no output when stdout is None, and fix_app_resource_dirs invokes creation then deduplication in order.
Business rules migration section tests
specifyweb/specify/management/commands/tests/business_rules_tests.py
Adds BusinessRulesMigrationTests and BusinessRulesDatabaseTests verifying fix_business_rules runs in order, apply_default_uniqueness_rules skips existing constraints, and catnum_rule_editable updates only matching rules.
Collection object type migration section tests
specifyweb/specify/management/commands/tests/cots_tests.py
Adds CotsMigrationTests and CotsDatabaseTests exercising three ORM-backed migrations: taxon treedef discipline assignment, idempotent cotype picklist creation per collection, and cogtype type picklist creation with predefined items.
Permissions migration section tests
specifyweb/specify/management/commands/tests/permissions_tests.py
Adds PermissionsMigrationTests verifying fix_permissions runs sections in order and initialize_permissions passes expected arguments including migrate_sp6_users=False.
Schema config migration section tests
specifyweb/specify/management/commands/tests/schema_config_tests.py
Adds SchemaConfigTests verifying fix_schema_config initializes schema defaults in task order; adds KeyMigrationSelectedHelperDatabaseTests with database tests for locale string bulk-create idempotency and container item/string deduplication repointing.
Tectonic migration section database tests
specifyweb/specify/management/commands/tests/tectonic_tests.py
Adds TectonicDatabaseTests verifying create_default_tectonic_ranks creates ordered rank chain with parent links and create_root_tectonic_node idempotently creates single root Tectonicunit.
Discipline resource dir deduplication database test
specifyweb/specify/management/commands/tests/test_deduplicate_discipline.py
Adds KeyMigrationAppResourceDirDatabaseTests exercising deduplicate_discipline_resource_dirs to verify deletion of empty duplicates, tie-breaking by id, and preservation of scoped directories.
Full pipeline idempotency integration test
specifyweb/specify/management/commands/tests/test_run_key_migration_functions.py
Adds RunKeyMigrationFunctionsTests tracking record counts across multiple models, simulating Specify 7 usage twice with between-run data, verifying first run creates records and second run produces no duplicates while preserving between-run data.

Deduplication Helper Extraction and Import Updates

Layer / File(s) Summary
Discipline resource dir deduplication helper extraction
specifyweb/specify/migration_utils/deduplication.py
Adds deduplicate_discipline_resource_dirs(apps) that deletes per-discipline duplicate SpAppResourceDir rows by identifying empty directories with no related persisted views/resources and keeping the oldest per discipline.
Management command and migration import updates
specifyweb/specify/management/commands/run_key_migration_functions.py, specifyweb/specify/migrations/0031_add_default_for_selectseries.py
Updates management command to import deduplicate_discipline_resource_dirs and deduplicate_schema_config_orm from deduplication module, imports make_selectseries_false from dedicated helper_0031 module, removes local deduplication implementation, and ensures exceptions are re-raised.

Possibly related issues

  • specify/specify7#8042: Implements the idempotency test for run_key_migration_functions as specified, verifying the command produces no duplicate records on second run with between-run data insertion.

Possibly related PRs

  • specify/specify7#6308: Both PRs modify the run_key_migration_functions flow and command execution.
  • specify/specify7#8009: PR #8225 refactors deduplication helpers that align with the deterministic deduplication logic introduced in PR #8009.
  • specify/specify7#8039: PR #8225 adds tests exercising bulk-create idempotency and deduplication helpers used to prevent duplicate locale-schema records.

Suggested reviewers

  • grantfitzsimmons
  • acwhite211
🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Testing Instructions ⚠️ Warning PR description's "Testing instructions" section contains only empty template comments with no actual testing steps, procedures, or verification guidance for the ~2,700 lines of new test code added. Fill in the "Testing instructions" section with clear steps: (1) how to run the test suite, (2) which Django test commands to use, (3) expected test pass/fail behavior, and (4) any special setup needed for database migration tests.
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: introducing a comprehensive test suite for migration utility functions.
Linked Issues check ✅ Passed The PR adds extensive unit tests for migration utility functions, directly addressing issue #8224's requirement to add unit tests for migration code.
Out of Scope Changes check ✅ Passed All changes are focused on adding test coverage for migration utilities and a refactoring of import paths to support the testing infrastructure.
Automatic Tests ✅ Passed PR includes comprehensive automatic tests with 20 test files containing 35 test classes and 61 test methods, covering migration utilities and command functions as required by issue #8224.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch issue-8224

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@CarolineDenis

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

♻️ Duplicate comments (1)
specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py (1)

63-63: ⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Same issue: passing _mock_new_parent instead of a proper apps mock.

This line has the same problem as line 36. Use a proper mock apps object.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py`
at line 63, The call to create_cotype_splocalecontaineritem is passing
mock_container._mock_new_parent as the apps argument, but this should be a
proper mock apps object instead. Replace the _mock_new_parent argument with a
properly constructed mock apps object (following the same pattern used to fix
the similar issue at line 36) to ensure the test passes the correct type of
dependency to the function.
🧹 Nitpick comments (1)
specifyweb/specify/migration_utils/tests/test_schema_reader.py (1)

122-122: 💤 Low value

Remove trailing blank line.

The file ends with an extra blank line after the closing of the if __name__ == '__main__' block.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@specifyweb/specify/migration_utils/tests/test_schema_reader.py` at line 122,
There is an extra blank line at the end of the file after the if __name__ ==
'__main__' block. Remove the trailing blank line at the end of the
test_schema_reader.py file to clean up the file formatting.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@specifyweb/specify/migration_utils/tests/test_bulk_create.py`:
- Around line 46-50: The test is named with "idempotent" but doesn't actually
verify idempotent behavior. After the first assertion where result equals 2, add
a second call to the same function being tested using the same input rows, then
assert that this second call returns 0 to confirm that no duplicate rows are
created. This verifies that the function properly checks for existing rows and
avoids redundant bulk_create operations when called with identical data.
- Around line 16-22: The mock queryset setup in the test does not properly
simulate the chained filter calls used in the real function. The real
implementation calls filter twice in succession before order_by, but the current
mock setup only chains filter once. Modify the mock_qs setup so that the first
filter call returns an object whose filter method can be called again, and that
second filter call returns an object with an order_by method. Specifically,
ensure mock_qs.filter.return_value has its own filter method configured to
return an object that has order_by chained properly, allowing the double filter
pattern to work correctly.
- Around line 11-12: The `@patch` decorator is replacing the actual
bulk_create_splocaleitemstr_idempotent function with a mock, preventing the test
from verifying the real implementation logic. Remove the `@patch` decorator
entirely and instead mock only the internal ORM dependencies (like
model.objects.filter and model.objects.bulk_create) that the actual function
uses. Update the test assertions to verify that the real function correctly
calls these mocked ORM methods with the expected parameters (FK filters,
language filters, and bulk_create calls), rather than asserting on the mock of
the function itself.

In
`@specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py`:
- Line 36: The function create_cotype_splocalecontaineritem is being called with
mock_container._mock_new_parent, which is an internal MagicMock attribute and
not a valid apps object with the required get_model method. Create a proper mock
apps object that implements the get_model(app_label, model_name) method as
expected by the function signature, then pass this mock apps object as the
argument to the create_cotype_splocalecontaineritem call instead of the
_mock_new_parent attribute.
- Around line 9-11: The test is patching module-level names that don't actually
exist in helper_0003_cotype_picklist.py, but the real
create_cotype_splocalecontaineritem function dynamically obtains models using
apps.get_model('specify', ModelName). Replace the three `@patch` decorators
targeting Splocalecontainer, Splocalecontaineritem, and Splocaleitemstr with a
single `@patch` for the apps parameter, then configure apps.get_model.side_effect
to return the appropriate mock models based on the model name argument passed to
it. Apply this same pattern to both test methods in the file.

In `@specifyweb/specify/migration_utils/tests/test_schema_reader.py`:
- Around line 28-48: The test assertion for mock_path is incorrect because it
expects the full concatenated path string, but the actual function constructs
the path using chained division operators starting with
Path(settings.SPECIFY_CONFIG_DIR). Update the test to properly mock the Path
chaining behavior by setting up mock_path.return_value to support the
__truediv__ method (which implements the / operator) and chain operations, then
correct the assertion to expect mock_path to be called once with just "/config"
instead of the full path string. The mock path instance should also have
exists() and open() methods properly configured to support the chaining pattern.

In `@specifyweb/specify/migration_utils/tests/test_schema_writer.py`:
- Around line 12-16: The test patches are targeting non-existent module-level
imports in schema_writer, but the real
`update_table_schema_config_with_defaults` function obtains models through
`apps.get_model()` calls, not from direct imports. Instead of patching
`Splocalecontainer`, `Splocalecontaineritem`, `Splocaleitemstr` as if they were
module-level imports, create a mock `apps` object, configure its `get_model()`
method to return the appropriate mock models for each model name (specify,
Splocalecontainer), (specify, Splocalecontaineritem), and (specify,
Splocaleitemstr), and pass this mock as a parameter to the
`update_table_schema_config_with_defaults` function call in the test. Also patch
the `apps` import in the schema_writer module itself.
- Around line 65-72: The test is patching module-level names that the function
`revert_table_schema_config` does not use. Instead, the function obtains models
via `apps.get_model()`, but the test calls
`revert_table_schema_config("TestTable")` without passing an `apps` argument,
causing it to use the real Django registry (global_apps). Remove the three
`@patch` decorators for Splocalecontainer, Splocaleitemstr, and
Splocalecontaineritem. Instead, create a mock `apps` object that is configured
to return the mocked model instances when `get_model()` is called with the
appropriate model names, and pass this mock `apps` object as a parameter to the
`revert_table_schema_config` function call.

---

Duplicate comments:
In
`@specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py`:
- Line 63: The call to create_cotype_splocalecontaineritem is passing
mock_container._mock_new_parent as the apps argument, but this should be a
proper mock apps object instead. Replace the _mock_new_parent argument with a
properly constructed mock apps object (following the same pattern used to fix
the similar issue at line 36) to ensure the test passes the correct type of
dependency to the function.

---

Nitpick comments:
In `@specifyweb/specify/migration_utils/tests/test_schema_reader.py`:
- Line 122: There is an extra blank line at the end of the file after the if
__name__ == '__main__' block. Remove the trailing blank line at the end of the
test_schema_reader.py file to clean up the file formatting.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 09e00975-8c37-4bc2-91a7-45fae919b02c

📥 Commits

Reviewing files that changed from the base of the PR and between 4c752cc and 6c779a5.

📒 Files selected for processing (5)
  • specifyweb/specify/migration_utils/tests/__init__.py
  • specifyweb/specify/migration_utils/tests/test_bulk_create.py
  • specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py
  • specifyweb/specify/migration_utils/tests/test_schema_reader.py
  • specifyweb/specify/migration_utils/tests/test_schema_writer.py

Comment thread specifyweb/specify/migration_utils/tests/test_bulk_create.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_bulk_create.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_bulk_create.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_helper_0003_cotype_picklist.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_schema_reader.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_schema_writer.py Outdated
Comment thread specifyweb/specify/migration_utils/tests/test_schema_writer.py Outdated
@github-project-automation github-project-automation Bot moved this from 📋Back Log to Dev Attention Needed in General Tester Board Jun 17, 2026
@CarolineDenis

Copy link
Copy Markdown
Contributor Author

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Base automatically changed from issue-8058 to issue-8124-8126-fix-7660-7682-2-after-review-2 June 24, 2026 17:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Dev Attention Needed

2 participants