-
Notifications
You must be signed in to change notification settings - Fork 4.2k
fix!: redacting user retirement data in lms #37886
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
19fc427
5ac51b6
b0e4bce
3c338d4
5972c46
83e52e1
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -13,7 +13,9 @@ | |
| from django.contrib.sites.models import Site | ||
| from django.core import mail | ||
| from django.core.cache import cache | ||
| from django.db import connection | ||
| from django.test import TestCase | ||
| from django.test.utils import CaptureQueriesContext | ||
| from django.urls import reverse | ||
| from enterprise.models import ( | ||
| EnterpriseCourseEnrollment, | ||
|
|
@@ -1079,8 +1081,79 @@ def cleanup_and_assert_status(self, data=None, expected_status=status.HTTP_204_N | |
| return response | ||
|
|
||
| def test_simple_success(self): | ||
| """ | ||
| Test basic cleanup with default redacted values. | ||
| """ | ||
| # Verify redaction happens (records exist before cleanup) | ||
| assert UserRetirementStatus.objects.count() == 9 | ||
|
|
||
| # Make the cleanup request | ||
| self.cleanup_and_assert_status() | ||
| assert not UserRetirementStatus.objects.all() | ||
|
|
||
| # Records should be deleted after redaction | ||
| retirements = UserRetirementStatus.objects.all() | ||
| assert retirements.count() == 0 | ||
|
|
||
| def test_redaction_before_deletion(self): | ||
| """ | ||
| Verify that redaction (UPDATE) happens before deletion (DELETE). | ||
| Captures actual SQL queries to ensure UPDATE queries contain redacted values. | ||
| """ | ||
| with CaptureQueriesContext(connection) as context: | ||
| self.cleanup_and_assert_status() | ||
|
|
||
| # Verify records are deleted after redaction | ||
| retirements = UserRetirementStatus.objects.all() | ||
| assert retirements.count() == 0 | ||
|
|
||
| # Verify UPDATE queries exist with default 'redacted' value | ||
| queries = context.captured_queries | ||
| update_queries = [q for q in queries if 'UPDATE' in q['sql'] and 'user_api_userretirementstatus' in q['sql']] | ||
| delete_queries = [q for q in queries if 'DELETE' in q['sql'] and 'user_api_userretirementstatus' in q['sql']] | ||
|
|
||
| # Should have 9 UPDATE and 9 DELETE queries | ||
| assert len(update_queries) == 9, f"Expected 9 UPDATE queries, found {len(update_queries)}" | ||
| assert len(delete_queries) == 9, f"Expected 9 DELETE queries, found {len(delete_queries)}" | ||
|
|
||
| # Verify UPDATE queries contain the redacted values | ||
| for update_query in update_queries: | ||
| sql = update_query['sql'] | ||
| assert "'redacted'" in sql, f"UPDATE query missing 'redacted' value: {sql}" | ||
| assert 'original_username' in sql, f"UPDATE query missing original_username field: {sql}" | ||
| assert 'original_email' in sql, f"UPDATE query missing original_email field: {sql}" | ||
| assert 'original_name' in sql, f"UPDATE query missing original_name field: {sql}" | ||
|
|
||
| def test_custom_redacted_values(self): | ||
| """Test that custom redacted values are applied before deletion.""" | ||
| custom_username = 'username-redacted-12345' | ||
| custom_email = 'email-redacted-67890' | ||
| custom_name = 'name-redacted-abcde' | ||
|
Comment on lines
+1128
to
+1130
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks @ktyagiapphelix2u. I'm glad you were able to confirm the updates are happening with If you peak under the covers of Could you try using Hopefully this makes sense. Thank you.
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah @robrap, that make sense so i have implemented using CaptureQueriesContext and tested them so these 3 cased now what i see is- Case 2: Swapped Broken Order (DELETE then SAVE) Case 3: Why Tests Fail |
||
|
|
||
| data = { | ||
| 'usernames': self.usernames, | ||
| 'redacted_username': custom_username, | ||
| 'redacted_email': custom_email, | ||
| 'redacted_name': custom_name | ||
| } | ||
|
|
||
| with CaptureQueriesContext(connection) as context: | ||
| self.cleanup_and_assert_status(data=data) | ||
|
|
||
| # Verify records are deleted after redaction | ||
| retirements = UserRetirementStatus.objects.all() | ||
| assert retirements.count() == 0 | ||
|
|
||
| # Verify UPDATE queries contain the custom redacted values | ||
| queries = context.captured_queries | ||
| update_queries = [q for q in queries if 'UPDATE' in q['sql'] and 'user_api_userretirementstatus' in q['sql']] | ||
|
|
||
| assert len(update_queries) == 9, f"Expected 9 UPDATE queries, found {len(update_queries)}" | ||
|
|
||
| for update_query in update_queries: | ||
| sql = update_query['sql'] | ||
| assert custom_username in sql, f"UPDATE query missing custom username '{custom_username}': {sql}" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not sure of the exact syntax, but can you update |
||
| assert custom_email in sql, f"UPDATE query missing custom email '{custom_email}': {sql}" | ||
| assert custom_name in sql, f"UPDATE query missing custom name '{custom_name}': {sql}" | ||
|
|
||
| def test_leaves_other_users(self): | ||
| remaining_usernames = [] | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Consider moving this to a method like
_assert_redacted_update_delete_queries(queries, redacted_username, etc.)to be reused for these two unit tests.