Skip to content

Implement idempotency test for run_key_migration_functions#8050

Open
acwhite211 wants to merge 6 commits into
mainfrom
issue-8042
Open

Implement idempotency test for run_key_migration_functions#8050
acwhite211 wants to merge 6 commits into
mainfrom
issue-8042

Conversation

@acwhite211
Copy link
Copy Markdown
Member

@acwhite211 acwhite211 commented Apr 30, 2026

Fixes #8042

Adds a Django regression test for the run_key_migration_functions management command to verify that it can be run repeatedly without creating duplicate migration-backed records.

The test snapshots relevant database record counts before the command runs, after the first run, and after a second run. The first run is expected to create or backfill records, while the second run must leave tracked record counts unchanged.

There is tracking on counts for models touched by the command, including collection object types, picklists, schema config records, permissions records, app resource dirs, uniqueness rules, and tectonic unit records. I also went ahead and added user-created picklist and permission records before the first run and between runs to simulate normal Specify 7 usage. We could try doing more here to simulate other Specify 7 usage.

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

Testing instructions

  • See the the back-end testing github action runs successfully.

Summary by CodeRabbit

  • Tests
    • Added comprehensive tests for the key migration to ensure records and relationships are preserved during migration and that the process is idempotent.
    • Updated legacy stored-query test expectations to reflect adjusted error expectations for collection object query directions.

Review Change Stack

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 1fd671f6-59e1-4710-8f9b-7cee0804660d

📥 Commits

Reviewing files that changed from the base of the PR and between 04662f3 and 9fcea02.

📒 Files selected for processing (1)
  • specifyweb/backend/stored_queries/tests/tests_legacy.py
💤 Files with no reviewable changes (1)
  • specifyweb/backend/stored_queries/tests/tests_legacy.py

📝 Walkthrough

Walkthrough

This PR adds a Django test module that simulates Specify 7 usage, runs the run_key_migration_functions management command, and verifies the command is idempotent by asserting no duplicate records are created on a second run while preserving all created data.

Changes

Migration Idempotency Verification

Layer / File(s) Summary
Test infrastructure and model tracking
specifyweb/specify/tests/test_run_key_migration_functions.py
Imports, TRACKED_MODELS mapping for rule/permission/domain models, and record_counts()/count_diff() helpers to snapshot and compare record counts across migrations.
Test data simulation and setup
specifyweb/specify/tests/test_run_key_migration_functions.py
setUp() customizes test discipline, and simulate_specify7_usage() creates coordinated records spanning picklists, collection object types (optional group type), roles/policies, app resources, and locale/schema data.
Assertion and command execution helpers
specifyweb/specify/tests/test_run_key_migration_functions.py
assert_simulated_specify7_usage_preserved() validates created records and relationships; run_key_migration_functions() executes the management command and captures stdout.
Idempotency test implementation
specifyweb/specify/tests/test_run_key_migration_functions.py
test_second_run_does_not_create_duplicate_records runs the command twice with simulated usage between runs, asserts the first run made tracked changes, and the second run made none while preserving all data.
Stored queries test expectation update
specifyweb/backend/stored_queries/tests/tests_legacy.py
Removed "projects" from the CollectionObject entry in expected_errors.not_found, leaving existing incorrect_direction expectations.

Possibly related issues

  • #8042: Add record count idempotency tests for run_key_migration_functions — This PR implements the idempotency test plan described in the issue and exercises the same tracked models and run/second-run assertions.

Possibly related PRs

  • specify/specify7#8039: Fixes duplicate locale container records; directly related to locale/schema behavior this test validates.
🚥 Pre-merge checks | ✅ 4 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The PR contains one minor out-of-scope change: removal of 'projects' from CollectionObject's not_found expectations in test_legacy.py, which appears unrelated to the idempotency test implementation. Clarify whether the test_legacy.py change is intentional and document its relationship to the migration functions, or defer it to a separate PR.
Testing Instructions ❓ Inconclusive Raw summary shows code changes but not PR description. Cannot assess whether testing instructions exist without full PR text. Review the PR description to verify testing instructions are present, clear, and reference running the back-end testing GitHub action or the local test command.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely describes the main change: implementing an idempotency test for the run_key_migration_functions management command.
Linked Issues check ✅ Passed The PR fully implements all coding requirements from issue #8042: new test module, helper functions (record_counts, count_diff), tracked models, count snapshots at three points, assertions for first-run increases and second-run idempotency, and simulation of Specify 7 usage.
Automatic Tests ✅ Passed PR adds RunKeyMigrationFunctionsTests with test_second_run_does_not_create_duplicate_records(), helper functions, and idempotency verification for the run_key_migration_functions command as required.

✏️ 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-8042

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 and usage tips.

@CarolineDenis CarolineDenis modified the milestones: 7.12.2, 7.12.1 May 4, 2026
@acwhite211 acwhite211 marked this pull request as ready for review May 19, 2026 22:03
@acwhite211 acwhite211 requested a review from a team May 19, 2026 22:03
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (3)
specifyweb/specify/tests/test_run_key_migration_functions.py (3)

322-325: ⚡ Quick win

First-run assertion may not catch partial migration failures.

The assertion only verifies that some tracked model saw an increase, not which models or by how much. If the migration incorrectly skips creating certain expected record types (e.g., only creates UniquenessRule but fails to create Picklist records), the test would still pass.

Consider asserting on specific expected models or minimum thresholds to better verify the migration's completeness.

🔍 Example: Assert on expected models
 first_run_diff = count_diff(before_first_run, after_first_run)
 
-self.assertTrue(
-    any(change > 0 for change in first_run_diff.values()),
-    f"Expected first run to create or backfill records. Diff: {first_run_diff}",
-)
+# Verify that the first run created records (exact models depend on migration logic)
+self.assertGreater(
+    len(first_run_diff),
+    0,
+    f"Expected first run to create or backfill records. Diff: {first_run_diff}",
+)
+# Example: Assert specific models if known
+# self.assertIn("Picklist", first_run_diff)
+# self.assertGreater(first_run_diff["Picklist"], 0)
🤖 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/tests/test_run_key_migration_functions.py` around lines
322 - 325, The test's first-run assertion (using first_run_diff) only checks
that some tracked model increased and can miss partial failures; update the
assertion to check specific expected models and/or minimum counts (e.g., assert
first_run_diff["UniquenessRule"] > 0 and first_run_diff["Picklist"] > 0 or
similar) so the test verifies creation/backfill of each required record type;
modify the test that uses first_run_diff (in
test_run_key_migration_functions.py) to assert per-model increases or thresholds
rather than any(change > 0).

20-40: 💤 Low value

Consider standardizing TRACKED_MODELS key casing.

The keys mix different conventions: "Collectionobjecttype" (all lowercase after first letter), "UniquenessRule" (PascalCase), and "LibraryRole" (PascalCase). Standardizing to either all PascalCase or all lowercase would improve readability.

🤖 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/tests/test_run_key_migration_functions.py` around lines 20
- 40, TRACKED_MODELS contains mixed key casing; make the keys consistent by
replacing the hard-coded mixed-case strings with a single consistent convention
(for example, use each model class's __name__ or normalized PascalCase) so keys
match their model identifiers; update the TRACKED_MODELS declaration (the dict
with keys like "Collectionobjecttype", "UniquenessRule", "LibraryRole", etc.) to
generate or use uniform keys (e.g., model.__name__ or a normalized casing
function) for all entries so lookups are predictable and readable.

145-145: 💤 Low value

Suffix format assumption is fragile.

The schema container name construction assumes suffix contains hyphens. While current test calls use hyphenated suffixes, this creates a hidden dependency between the test method and helper.

Consider using a more robust transformation or documenting the suffix format requirement.

🤖 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/tests/test_run_key_migration_functions.py` at line 145,
The test currently builds the schema container name with
name=f"test_schema_container_{suffix.replace('-', '_')}", which assumes suffix
contains hyphens; update the construction to robustly normalize suffix (e.g.,
replace any non-alphanumeric characters with underscores) so tests don't depend
on hyphen-only input, by changing the name formatting where suffix is used (the
expression creating name in the test helper or test function) to use a general
sanitization/normalization routine (or call a small helper sanitize_suffix)
and/or add a brief comment documenting the expected allowed characters for
suffix.
🤖 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.

Nitpick comments:
In `@specifyweb/specify/tests/test_run_key_migration_functions.py`:
- Around line 322-325: The test's first-run assertion (using first_run_diff)
only checks that some tracked model increased and can miss partial failures;
update the assertion to check specific expected models and/or minimum counts
(e.g., assert first_run_diff["UniquenessRule"] > 0 and
first_run_diff["Picklist"] > 0 or similar) so the test verifies
creation/backfill of each required record type; modify the test that uses
first_run_diff (in test_run_key_migration_functions.py) to assert per-model
increases or thresholds rather than any(change > 0).
- Around line 20-40: TRACKED_MODELS contains mixed key casing; make the keys
consistent by replacing the hard-coded mixed-case strings with a single
consistent convention (for example, use each model class's __name__ or
normalized PascalCase) so keys match their model identifiers; update the
TRACKED_MODELS declaration (the dict with keys like "Collectionobjecttype",
"UniquenessRule", "LibraryRole", etc.) to generate or use uniform keys (e.g.,
model.__name__ or a normalized casing function) for all entries so lookups are
predictable and readable.
- Line 145: The test currently builds the schema container name with
name=f"test_schema_container_{suffix.replace('-', '_')}", which assumes suffix
contains hyphens; update the construction to robustly normalize suffix (e.g.,
replace any non-alphanumeric characters with underscores) so tests don't depend
on hyphen-only input, by changing the name formatting where suffix is used (the
expression creating name in the test helper or test function) to use a general
sanitization/normalization routine (or call a small helper sanitize_suffix)
and/or add a brief comment documenting the expected allowed characters for
suffix.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 100c0c3d-be1b-4077-855d-3f7b1fb63ce9

📥 Commits

Reviewing files that changed from the base of the PR and between 5d85c03 and 04662f3.

📒 Files selected for processing (1)
  • specifyweb/specify/tests/test_run_key_migration_functions.py

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: 📋Back Log

Development

Successfully merging this pull request may close these issues.

Add record count idempotency tests for run_key_migration_functions

2 participants