From a53e2be57bc01e89d4866c4624fd1c693a36f8b0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADctor=20Falc=C3=B3n?= Date: Tue, 17 Feb 2026 11:45:27 +0100 Subject: [PATCH] Remove encryption from browser tests and demo user (#129) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## Summary - Removed all `setupEncryptionKey()` / `visitWithEncryptionKey()` calls from browser tests — encryption key setup in localStorage is no longer needed since new users don't have encryption - Removed `encryption_salt` from `UserFactory::onboarded()` state and `OnboardingFlowTest` user creation - Removed `name_iv` from `Account::factory()` calls in `BankAccountsTest` - Deleted `DemoEncryptionService` and its unit test — demo command now stores plaintext account names and transaction descriptions - Removed `demoEncryptionKey` Inertia shared prop and `encryption_key` from demo config - Removed encryption helper methods from `TestCase.php` and global `setupEncryptionKey()` from `Pest.php` ## Test plan - [x] Run `php artisan test --exclude-testsuite=Browser` — all non-browser tests pass - [x] Run `php artisan test --testsuite=Browser` — browser tests pass without encryption key setup - [x] Run `php artisan demo:reset` — demo account created with plaintext data - [x] Verify existing encryption migration tests still pass (`EncryptionTest`, `DecryptTransactionsTest`, `PlaintextTransactionsTest`) --- .../Commands/ResetDemoAccountCommand.php | 57 ++-------- app/Http/Middleware/HandleInertiaRequests.php | 1 - app/Services/Demo/DemoEncryptionService.php | 102 ------------------ config/app.php | 1 - database/factories/UserFactory.php | 1 - tests/Browser/AmountInputTest.php | 6 +- tests/Browser/AutomationRuleBuilderTest.php | 8 -- tests/Browser/BankAccountsTest.php | 31 ++---- tests/Browser/ImportTransactionsTest.php | 8 -- tests/Browser/OnboardingFlowTest.php | 5 - tests/Browser/TransactionsTest.php | 8 -- .../Console/ResetDemoAccountCommandTest.php | 1 - tests/Pest.php | 11 -- tests/TestCase.php | 57 ---------- tests/Unit/DemoEncryptionServiceTest.php | 85 --------------- 15 files changed, 18 insertions(+), 364 deletions(-) delete mode 100644 app/Services/Demo/DemoEncryptionService.php delete mode 100644 tests/Unit/DemoEncryptionServiceTest.php diff --git a/app/Console/Commands/ResetDemoAccountCommand.php b/app/Console/Commands/ResetDemoAccountCommand.php index d11996c4..109c6138 100644 --- a/app/Console/Commands/ResetDemoAccountCommand.php +++ b/app/Console/Commands/ResetDemoAccountCommand.php @@ -10,13 +10,11 @@ use App\Models\Account; use App\Models\Bank; use App\Models\Budget; use App\Models\Category; -use App\Models\EncryptedMessage; use App\Models\Label; use App\Models\User; use App\Services\BudgetPeriodService; use App\Services\BudgetTransactionService; use App\Services\Demo\DemoAutomationRulesProvider; -use App\Services\Demo\DemoEncryptionService; use App\Services\Demo\DemoLabelsProvider; use App\Services\Demo\DemoTransactionsProvider; use Illuminate\Console\Command; @@ -30,13 +28,10 @@ class ResetDemoAccountCommand extends Command private const MIN_BALANCE_GROWTH_PERCENTAGE = 0.05; - private string $encryptionKey; - public function __construct( private DemoTransactionsProvider $transactionsProvider, private DemoLabelsProvider $labelsProvider, private DemoAutomationRulesProvider $rulesProvider, - private DemoEncryptionService $encryptionService, private BudgetPeriodService $budgetPeriodService, private BudgetTransactionService $budgetTransactionService, ) { @@ -47,7 +42,6 @@ class ResetDemoAccountCommand extends Command { $demoEmail = config('app.demo.email'); $demoPassword = config('app.demo.password'); - $demoEncryptionKey = config('app.demo.encryption_key'); if (! $demoEmail || ! $demoPassword) { $this->error('Demo configuration not set. Please set DEMO_EMAIL and DEMO_PASSWORD in .env'); @@ -57,15 +51,10 @@ class ResetDemoAccountCommand extends Command $this->info("Resetting demo account: {$demoEmail}"); - $salt = $this->encryptionService->generateSalt($demoEncryptionKey); - $this->encryptionKey = $this->encryptionService->deriveKey($demoEncryptionKey, $salt); - - $user = $this->findOrCreateDemoUser($demoEmail, $demoPassword, $salt); + $user = $this->findOrCreateDemoUser($demoEmail, $demoPassword); $this->deleteExistingData($user); - $this->createEncryptedMessage($user); - $this->createCategories($user); $labels = $this->createLabels($user); @@ -85,12 +74,11 @@ class ResetDemoAccountCommand extends Command return self::SUCCESS; } - private function findOrCreateDemoUser(string $email, string $password, string $salt): User + private function findOrCreateDemoUser(string $email, string $password): User { $user = User::where('email', $email)->first(); if ($user) { - $user->encryption_salt = $salt; $user->email_verified_at ??= now(); $user->save(); @@ -102,7 +90,6 @@ class ResetDemoAccountCommand extends Command 'name' => 'Demo User', 'password' => $password, 'onboarded_at' => now(), - 'encryption_salt' => $salt, 'currency_code' => 'USD', ]); $user->email_verified_at = now(); @@ -124,23 +111,6 @@ class ResetDemoAccountCommand extends Command $this->info(' Deleted existing data'); } - private function createEncryptedMessage(User $user): void - { - $testMessage = $this->encryptionService->encrypt( - 'Hello, world', - $this->encryptionKey, - 'demo_test_message' - ); - - EncryptedMessage::create([ - 'user_id' => $user->id, - 'encrypted_content' => $testMessage['encrypted'], - 'iv' => $testMessage['iv'], - ]); - - $this->info(' Created encrypted message for key verification'); - } - private function createCategories(User $user): void { (new CreateDefaultCategories)->handle($user); @@ -234,16 +204,10 @@ class ResetDemoAccountCommand extends Command $createdAccounts = []; $transactionAccounts = []; - foreach ($accounts as $index => $accountData) { - $encrypted = $this->encryptionService->encrypt( - $accountData['name'], - $this->encryptionKey, - "demo_account_{$index}" - ); - + foreach ($accounts as $accountData) { $account = $user->accounts()->create([ - 'name' => $encrypted['encrypted'], - 'name_iv' => $encrypted['iv'], + 'name' => $accountData['name'], + 'name_iv' => null, 'bank_id' => $accountData['bank_account_id'], 'currency_code' => 'USD', 'type' => $accountData['type'], @@ -329,7 +293,6 @@ class ResetDemoAccountCommand extends Command } $allTransactions = $this->transactionsProvider->getTransactions(); - $transactionIndex = 0; $count = 0; foreach ($allTransactions as $transactionData) { @@ -344,14 +307,7 @@ class ResetDemoAccountCommand extends Command $account = $accounts[array_rand($accounts)]; - $encrypted = $this->encryptionService->encrypt( - $transactionData['description'], - $this->encryptionKey, - "demo_tx_{$account->id}_{$transactionIndex}" - ); - - $transactionData['description'] = $encrypted['encrypted']; - $transactionData['description_iv'] = $encrypted['iv']; + $transactionData['description_iv'] = null; $transaction = $account->transactions()->create([ 'user_id' => $account->user_id, @@ -365,7 +321,6 @@ class ResetDemoAccountCommand extends Command } } - $transactionIndex++; $count++; } diff --git a/app/Http/Middleware/HandleInertiaRequests.php b/app/Http/Middleware/HandleInertiaRequests.php index 27997f18..b64b21ea 100644 --- a/app/Http/Middleware/HandleInertiaRequests.php +++ b/app/Http/Middleware/HandleInertiaRequests.php @@ -73,7 +73,6 @@ class HandleInertiaRequests extends Middleware 'email' => config('app.demo.email'), 'password' => config('app.demo.password'), ] : null, - 'demoEncryptionKey' => $isDemoAccount ? config('app.demo.encryption_key') : null, 'subscriptionsEnabled' => config('subscriptions.enabled', false), 'pricing' => [ 'plans' => config('subscriptions.plans', []), diff --git a/app/Services/Demo/DemoEncryptionService.php b/app/Services/Demo/DemoEncryptionService.php deleted file mode 100644 index 16f8277f..00000000 --- a/app/Services/Demo/DemoEncryptionService.php +++ /dev/null @@ -1,102 +0,0 @@ - base64_encode($encryptedWithTag), - 'iv' => base64_encode($iv), - ]; - } - - /** - * Decrypt ciphertext using AES-256-GCM. - */ - public function decrypt(string $encryptedBase64, string $key, string $ivBase64): string - { - $encryptedWithTag = base64_decode($encryptedBase64); - $iv = base64_decode($ivBase64); - - $tagLength = 16; - $encrypted = substr($encryptedWithTag, 0, -$tagLength); - $tag = substr($encryptedWithTag, -$tagLength); - - $decrypted = openssl_decrypt( - $encrypted, - 'aes-256-gcm', - $key, - OPENSSL_RAW_DATA, - $iv, - $tag - ); - - if ($decrypted === false) { - throw new \RuntimeException('Decryption failed'); - } - - return $decrypted; - } -} diff --git a/config/app.php b/config/app.php index 2dbb23bb..e57dc652 100644 --- a/config/app.php +++ b/config/app.php @@ -126,7 +126,6 @@ return [ 'demo' => [ 'email' => env('DEMO_EMAIL', 'demo@whisper.money'), 'password' => env('DEMO_PASSWORD', 'demo'), - 'encryption_key' => env('DEMO_ENCRYPTION_KEY', 'demo'), ], ]; diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 89130e5e..a11b72ad 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -64,7 +64,6 @@ class UserFactory extends Factory { return $this->state(fn (array $attributes) => [ 'onboarded_at' => now(), - 'encryption_salt' => 'test-salt', 'locale' => 'en', ]); } diff --git a/tests/Browser/AmountInputTest.php b/tests/Browser/AmountInputTest.php index b0ae162d..7cb97be2 100644 --- a/tests/Browser/AmountInputTest.php +++ b/tests/Browser/AmountInputTest.php @@ -14,7 +14,6 @@ it('formats amount on blur', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('Add Transaction') @@ -33,7 +32,6 @@ it('accepts comma as decimal separator', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('Add Transaction') @@ -55,7 +53,7 @@ it('can create a transaction with amount input', function () { actingAs($user); - $page = $this->visitWithEncryptionKey('/transactions'); + $page = visit('/transactions'); $page->wait(3); // Extra wait for IndexedDB to sync $page->assertSee('Transactions') @@ -98,7 +96,6 @@ it('formats amount when pressing enter', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->wait(3); // Extra wait for IndexedDB to sync $page->assertSee('Transactions') @@ -137,7 +134,6 @@ it('accepts negative amounts', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->wait(3); // Extra wait for IndexedDB to sync $page->assertSee('Transactions') diff --git a/tests/Browser/AutomationRuleBuilderTest.php b/tests/Browser/AutomationRuleBuilderTest.php index 424bbf06..6dedd9c1 100644 --- a/tests/Browser/AutomationRuleBuilderTest.php +++ b/tests/Browser/AutomationRuleBuilderTest.php @@ -15,7 +15,6 @@ it('can create an automation rule with visual builder', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); // Create category via UI to ensure it syncs to IndexedDB createCategoryViaUI($page, 'Groceries'); @@ -53,7 +52,6 @@ it('can add multiple conditions to a group', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Shopping'); @@ -92,7 +90,6 @@ it('can add multiple groups', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Food'); @@ -131,7 +128,6 @@ it('can select different field types and operators', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Bills'); @@ -172,7 +168,6 @@ it('can edit an existing rule with visual builder', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Transport'); @@ -217,7 +212,6 @@ it('validates that at least one condition is required', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Entertainment'); @@ -248,7 +242,6 @@ it('can toggle group operators between AND and OR', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Health'); @@ -290,7 +283,6 @@ it('can use is empty operator for nullable fields', function () { actingAs($user); $page = visit('/settings/categories'); - $this->setupEncryptionKey($page); createCategoryViaUI($page, 'Travel'); diff --git a/tests/Browser/BankAccountsTest.php b/tests/Browser/BankAccountsTest.php index fa22507a..1ae63148 100644 --- a/tests/Browser/BankAccountsTest.php +++ b/tests/Browser/BankAccountsTest.php @@ -20,12 +20,11 @@ it('can view bank accounts page', function () { it('shows existing accounts in list', function () { $user = User::factory()->onboarded()->create(); - $bank = Bank::factory()->create(['name' => 'Test Bank']); + $bank = Bank::factory()->create(['name' => 'Test Bank', 'logo' => null]); Account::factory()->create([ 'user_id' => $user->id, 'bank_id' => $bank->id, 'name' => 'My Checking', - 'name_iv' => str_repeat('b', 16), 'type' => 'checking', 'currency_code' => 'USD', ]); @@ -33,7 +32,7 @@ it('shows existing accounts in list', function () { actingAs($user); $page = visit('/settings/accounts'); - $this->setupEncryptionKey($page); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2); $page->assertSee('Bank accounts') ->waitForText('Test Bank') @@ -48,7 +47,6 @@ it('can open create account dialog', function () { actingAs($user); $page = visit('/settings/accounts'); - $this->setupEncryptionKey($page); $page->assertSee('Bank accounts') ->click('Create Account') @@ -59,12 +57,11 @@ it('can open create account dialog', function () { it('can create a new bank account', function () { $user = User::factory()->onboarded()->create(); - $bank = Bank::factory()->create(['name' => 'My Bank']); + $bank = Bank::factory()->create(['name' => 'My Bank', 'logo' => null]); actingAs($user); $page = visit('/settings/accounts'); - $this->setupEncryptionKey($page); $page->assertSee('Bank accounts') ->click('Create Account') @@ -109,24 +106,22 @@ it('shows empty state when no accounts exist', function () { it('can filter accounts by name', function () { $user = User::factory()->onboarded()->create(); - $bank = Bank::factory()->create(['name' => 'Test Bank']); + $bank = Bank::factory()->create(['name' => 'Test Bank', 'logo' => null]); Account::factory()->create([ 'user_id' => $user->id, 'bank_id' => $bank->id, 'name' => 'Checking Account', - 'name_iv' => str_repeat('b', 16), ]); Account::factory()->create([ 'user_id' => $user->id, 'bank_id' => $bank->id, 'name' => 'Savings Account', - 'name_iv' => str_repeat('c', 16), ]); actingAs($user); $page = visit('/settings/accounts'); - $this->setupEncryptionKey($page); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(2); $page->assertSee('Bank accounts') ->waitForText('Test Bank') @@ -137,18 +132,16 @@ it('can filter accounts by name', function () { it('can edit an existing account via dropdown menu', function () { $user = User::factory()->onboarded()->create(); - $bank = Bank::factory()->create(['name' => 'Edit Bank']); + $bank = Bank::factory()->create(['name' => 'Edit Bank', 'logo' => null]); actingAs($user); $page = visit('/settings/accounts'); - $page->wait(1); - $this->setupEncryptionKey($page); // Create account via UI to ensure it syncs to IndexedDB createAccountViaUI($page, 'Old Account Name', 'Edit Bank', 'Checking', 'USD'); - $page->navigate('/settings/accounts')->wait(5); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(5); $page->assertSee('Bank accounts') ->click('button[aria-label="Open menu"]') @@ -160,7 +153,7 @@ it('can edit an existing account via dropdown menu', function () { ->click('button[type="submit"]:has-text("Update")') ->wait(2); - $page->navigate('/settings/accounts')->wait(5); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(5); $page->assertSee('Updated Account Name') ->assertNoJavascriptErrors(); @@ -168,18 +161,16 @@ it('can edit an existing account via dropdown menu', function () { it('can delete an account via dropdown menu', function () { $user = User::factory()->onboarded()->create(); - $bank = Bank::factory()->create(['name' => 'Delete Bank']); + $bank = Bank::factory()->create(['name' => 'Delete Bank', 'logo' => null]); actingAs($user); $page = visit('/settings/accounts'); - $page->wait(1); - $this->setupEncryptionKey($page); // Create account via UI to ensure it syncs to IndexedDB createAccountViaUI($page, 'Account To Delete', 'Delete Bank', 'Checking', 'USD'); - $page->navigate('/settings/accounts')->wait(5); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(5); $page->assertSee('Bank accounts') ->assertSee('Account To Delete') @@ -193,7 +184,7 @@ it('can delete an account via dropdown menu', function () { ->click('button[type="submit"]:has-text("Delete")') ->wait(2); - $page->navigate('/settings/accounts')->wait(5); + $page->navigate('/settings/accounts', ['waitUntil' => 'domcontentloaded'])->wait(5); $page->assertDontSee('Account To Delete') ->assertNoJavascriptErrors(); diff --git a/tests/Browser/ImportTransactionsTest.php b/tests/Browser/ImportTransactionsTest.php index 8fe9c728..a874311e 100644 --- a/tests/Browser/ImportTransactionsTest.php +++ b/tests/Browser/ImportTransactionsTest.php @@ -12,7 +12,6 @@ it('can open import transactions drawer', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -30,7 +29,6 @@ it('shows no accounts message when none exist', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -48,7 +46,6 @@ it('can select account for import', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -67,7 +64,6 @@ it('can upload a CSV file for import', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -86,7 +82,6 @@ it('can complete full import flow', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -105,7 +100,6 @@ it('shows column mapping step after file upload', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -124,7 +118,6 @@ it('can navigate back through import steps', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') @@ -143,7 +136,6 @@ it('applies automation rules when importing transactions', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('button[aria-label="More actions"]') diff --git a/tests/Browser/OnboardingFlowTest.php b/tests/Browser/OnboardingFlowTest.php index 88efa3c6..4f7d7ec9 100644 --- a/tests/Browser/OnboardingFlowTest.php +++ b/tests/Browser/OnboardingFlowTest.php @@ -41,7 +41,6 @@ it('redirects onboarded user away from onboarding page to dashboard', function ( it('redirects non-onboarded user from dashboard to onboarding', function () { $user = User::factory()->create([ 'onboarded_at' => null, - 'encryption_salt' => 'test-salt', ]); $this->actingAs($user); @@ -95,7 +94,6 @@ it('navigates from welcome to account types', function () { it('shows existing accounts instead of create form when accounts exist', function () { $user = User::factory()->create([ 'onboarded_at' => null, - 'encryption_salt' => 'test-salt', ]); $bank = Bank::factory()->create(['name' => 'Test Bank']); @@ -125,7 +123,6 @@ it('shows existing accounts instead of create form when accounts exist', functio it('allows continuing with existing accounts', function () { $user = User::factory()->create([ 'onboarded_at' => null, - 'encryption_salt' => 'test-salt', ]); $bank = Bank::factory()->create(['name' => 'Existing Bank']); @@ -162,7 +159,6 @@ it('allows continuing with existing accounts', function () { it('shows import transactions step after account creation', function () { $user = User::factory()->create([ 'onboarded_at' => null, - 'encryption_salt' => 'test-salt', ]); $bank = Bank::factory()->create(['name' => 'My Bank']); @@ -192,7 +188,6 @@ it('shows import transactions step after account creation', function () { it('shows add another account form without first account restriction', function () { $user = User::factory()->create([ 'onboarded_at' => null, - 'encryption_salt' => 'test-salt', ]); $bank = Bank::factory()->create(['name' => 'Primary Bank']); diff --git a/tests/Browser/TransactionsTest.php b/tests/Browser/TransactionsTest.php index de91aa88..57ba1575 100644 --- a/tests/Browser/TransactionsTest.php +++ b/tests/Browser/TransactionsTest.php @@ -28,7 +28,6 @@ it('can open add transaction dialog', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->click('Add Transaction') @@ -43,17 +42,12 @@ it('can create a transaction', function () { actingAs($user); - // Generate a single encryption key to use throughout the test - $encryptionKey = base64_encode(random_bytes(32)); - // Create category via UI $page = visit('/settings/categories'); - $this->setupEncryptionKey($page, $encryptionKey); createCategoryViaUI($page, 'Groceries'); // Create account via UI $page = visit('/settings/accounts'); - $this->setupEncryptionKey($page, $encryptionKey); createAccountViaUI($page, 'My Checking', 'Test Bank'); // Verify account was created @@ -62,7 +56,6 @@ it('can create a transaction', function () { // Visit transactions page $page = visit('/transactions'); - $this->setupEncryptionKey($page, $encryptionKey); $page->wait(3); // Extra wait for IndexedDB to sync $page->assertSee('Transactions') @@ -111,7 +104,6 @@ it('can filter transactions by search text', function () { actingAs($user); $page = visit('/transactions'); - $this->setupEncryptionKey($page); $page->assertSee('Transactions') ->wait(2) diff --git a/tests/Feature/Console/ResetDemoAccountCommandTest.php b/tests/Feature/Console/ResetDemoAccountCommandTest.php index b83d2565..8b950b98 100644 --- a/tests/Feature/Console/ResetDemoAccountCommandTest.php +++ b/tests/Feature/Console/ResetDemoAccountCommandTest.php @@ -6,7 +6,6 @@ beforeEach(function () { config(['app.demo' => [ 'email' => 'demo@whisper.money', 'password' => 'demo', - 'encryption_key' => 'demo', ]]); }); diff --git a/tests/Pest.php b/tests/Pest.php index a441c62b..7f9c989b 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -43,17 +43,6 @@ pest()->browser()->timeout(15000); | */ -function setupEncryptionKey($page, ?string $key = null): void -{ - $key ??= base64_encode(random_bytes(32)); - $currentUrl = $page->url(); - $page->script("localStorage.setItem('encryption_key', ".json_encode($key).')'); - // Reload to trigger sync - $page->navigate($currentUrl)->wait(1); - // Reload again to ensure sync completes - $page->navigate($currentUrl)->wait(3); -} - function createCategoryViaUI($page, string $name, string $color = 'green', string $type = 'Expense'): void { $page->click('Create Category') diff --git a/tests/TestCase.php b/tests/TestCase.php index 99d48139..6b717d9d 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -12,61 +12,4 @@ abstract class TestCase extends BaseTestCase config(['subscriptions.enabled' => false]); } - - /** - * Generate a valid base64-encoded encryption key for testing. - * This creates a 32-byte key (AES-256) and encodes it as base64. - */ - protected function generateTestEncryptionKey(): string - { - return base64_encode(random_bytes(32)); - } - - /** - * Set up encryption key in browser localStorage for testing. - * This can be called before or after visiting a page. - * If called after visiting, it will reload the page. - */ - protected function setupEncryptionKey($page, ?string $key = null, bool $reload = true): void - { - $key ??= $this->generateTestEncryptionKey(); - try { - $page->script("localStorage.setItem('encryption_key', ".json_encode($key).')'); - } catch (\Exception) { - // If script fails (page not ready), wait and retry - $page->wait(0.2); - $page->script("localStorage.setItem('encryption_key', ".json_encode($key).')'); - } - if ($reload) { - $currentUrl = $page->url(); - $page->navigate($currentUrl)->wait(1.5); - } - } - - /** - * Visit a page with encryption key already set up. - */ - protected function visitWithEncryptionKey(string $url, ?string $key = null) - { - $key ??= $this->generateTestEncryptionKey(); - $page = visit($url); - $page->script("localStorage.setItem('encryption_key', ".json_encode($key).')'); - $page->navigate($url)->wait(1); - - return $page; - } - - /** - * Get or generate a consistent test encryption key. - * This ensures the same key is used across the test. - */ - protected function getTestEncryptionKey(): string - { - static $key = null; - if ($key === null) { - $key = $this->generateTestEncryptionKey(); - } - - return $key; - } } diff --git a/tests/Unit/DemoEncryptionServiceTest.php b/tests/Unit/DemoEncryptionServiceTest.php deleted file mode 100644 index 61645ff7..00000000 --- a/tests/Unit/DemoEncryptionServiceTest.php +++ /dev/null @@ -1,85 +0,0 @@ -generateSalt('demo'); - $salt2 = $service->generateSalt('demo'); - - expect($salt1)->toBe($salt2); - expect(base64_decode($salt1))->toHaveLength(16); -}); - -test('generates different salts for different seeds', function () { - $service = new DemoEncryptionService; - - $salt1 = $service->generateSalt('demo'); - $salt2 = $service->generateSalt('other'); - - expect($salt1)->not->toBe($salt2); -}); - -test('derives key from password and salt', function () { - $service = new DemoEncryptionService; - - $salt = $service->generateSalt('demo'); - $key = $service->deriveKey('demo', $salt); - - expect(strlen($key))->toBe(32); -}); - -test('derives same key for same password and salt', function () { - $service = new DemoEncryptionService; - - $salt = $service->generateSalt('demo'); - $key1 = $service->deriveKey('demo', $salt); - $key2 = $service->deriveKey('demo', $salt); - - expect($key1)->toBe($key2); -}); - -test('encrypts and decrypts text correctly', function () { - $service = new DemoEncryptionService; - - $salt = $service->generateSalt('demo'); - $key = $service->deriveKey('demo', $salt); - - $plaintext = 'Hello, world'; - $encrypted = $service->encrypt($plaintext, $key); - - expect($encrypted)->toHaveKeys(['encrypted', 'iv']); - expect($encrypted['encrypted'])->not->toBe($plaintext); - - $decrypted = $service->decrypt($encrypted['encrypted'], $key, $encrypted['iv']); - - expect($decrypted)->toBe($plaintext); -}); - -test('deterministic iv produces same encrypted output', function () { - $service = new DemoEncryptionService; - - $salt = $service->generateSalt('demo'); - $key = $service->deriveKey('demo', $salt); - - $plaintext = 'Hello, world'; - $encrypted1 = $service->encrypt($plaintext, $key, 'seed1'); - $encrypted2 = $service->encrypt($plaintext, $key, 'seed1'); - - expect($encrypted1['encrypted'])->toBe($encrypted2['encrypted']); - expect($encrypted1['iv'])->toBe($encrypted2['iv']); -}); - -test('different iv seeds produce different encrypted output', function () { - $service = new DemoEncryptionService; - - $salt = $service->generateSalt('demo'); - $key = $service->deriveKey('demo', $salt); - - $plaintext = 'Hello, world'; - $encrypted1 = $service->encrypt($plaintext, $key, 'seed1'); - $encrypted2 = $service->encrypt($plaintext, $key, 'seed2'); - - expect($encrypted1['encrypted'])->not->toBe($encrypted2['encrypted']); -});