feat(encryption): report count of users still holding encrypted data (#687)
## What `encryption:notify-removal` now prints, on every run, the total number of **non-deleted** users who still hold legacy browser-encrypted data: ``` 4 non-deleted user(s) still have encrypted data. ``` The count is taken before the billing exclusion, so it reflects the full remaining scope of browser-encrypted data. Soft-deleted users are excluded (model default scope), as are users whose data is already plaintext. ## Why It's the signal for deciding when the browser-side encryption code can finally be removed: once this reaches zero, nothing in the app depends on client-side encrypted columns anymore. ## Not changed Email-sending logic is untouched. Subscribed users are still never warned — the recipient set continues to go through `excludeBilledUsers()` exactly as before. This PR only adds a reporting line. ## Testing - New test asserts the count spans everyone with encrypted data (subscribed included), while excluding soft-deleted and plaintext users, and that sending still skips the subscribed user. - Full `NotifyEncryptedDataRemovalCommandTest` suite green.
This commit is contained in:
parent
808d41eee2
commit
4cd7619791
|
|
@ -39,16 +39,22 @@ class NotifyEncryptedDataRemovalCommand extends Command
|
|||
*/
|
||||
public function handle(): int
|
||||
{
|
||||
$users = $this->excludeBilledUsers($this->usersWithLegacyEncryption()->get());
|
||||
$encryptedUsers = $this->usersWithLegacyEncryption()->get();
|
||||
|
||||
// Always report the total scope of legacy encrypted data (soft-deleted users are
|
||||
// excluded by the model's default scope). This is the signal for deciding whether
|
||||
// the browser-side encryption code can finally be removed, independent of who we
|
||||
// actually email below.
|
||||
$this->info("{$encryptedUsers->count()} non-deleted user(s) still have encrypted data.");
|
||||
|
||||
$users = $this->excludeBilledUsers($encryptedUsers);
|
||||
|
||||
if ($users->isEmpty()) {
|
||||
$this->info('No users with encrypted data found.');
|
||||
$this->info('No users to warn.');
|
||||
|
||||
return self::SUCCESS;
|
||||
}
|
||||
|
||||
$this->info("Found {$users->count()} user(s) with encrypted data.");
|
||||
|
||||
if ($this->option('dry-run')) {
|
||||
$this->renderTable($users);
|
||||
$this->info('[dry-run] No emails sent.');
|
||||
|
|
|
|||
|
|
@ -45,6 +45,39 @@ test('it never warns a subscribed user', function () {
|
|||
Queue::assertNotPushed(SendUpdateEmailJob::class);
|
||||
});
|
||||
|
||||
test('it always reports the total count of non-deleted encrypted users, including subscribed ones', function () {
|
||||
config(['subscriptions.enabled' => true]);
|
||||
|
||||
$subscribed = User::factory()->create();
|
||||
Transaction::factory()->for($subscribed)->create();
|
||||
$subscribed->subscriptions()->create([
|
||||
'type' => 'default',
|
||||
'stripe_id' => 'sub_test123',
|
||||
'stripe_status' => 'active',
|
||||
'stripe_price' => 'price_test123',
|
||||
]);
|
||||
|
||||
$unsubscribed = User::factory()->create();
|
||||
Transaction::factory()->for($unsubscribed)->create();
|
||||
|
||||
$deleted = User::factory()->create();
|
||||
Transaction::factory()->for($deleted)->create();
|
||||
$deleted->delete();
|
||||
|
||||
$clean = User::factory()->create();
|
||||
Transaction::factory()->for($clean)->plaintext()->create();
|
||||
|
||||
// The count spans everyone still holding encrypted data (subscribed included); the
|
||||
// soft-deleted and plaintext users are excluded.
|
||||
artisan('encryption:notify-removal', ['--force' => true])
|
||||
->expectsOutputToContain('2 non-deleted user(s) still have encrypted data.')
|
||||
->assertSuccessful();
|
||||
|
||||
// Sending is unchanged: the subscribed user is counted but never emailed.
|
||||
Queue::assertPushed(SendUpdateEmailJob::class, 1);
|
||||
Queue::assertPushed(SendUpdateEmailJob::class, fn (SendUpdateEmailJob $job) => $job->user->is($unsubscribed));
|
||||
});
|
||||
|
||||
test('it queues on the emails queue', function () {
|
||||
$user = User::factory()->create();
|
||||
Transaction::factory()->for($user)->create();
|
||||
|
|
|
|||
Loading…
Reference in New Issue