From 981df8cd68acdcb6393ef7f145c4990268692701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vi=CC=81ctor=20Falco=CC=81n?= Date: Wed, 26 Nov 2025 11:52:01 +0100 Subject: [PATCH] Fix improve testing --- app/Console/Commands/ResetUserCategories.php | 6 +- tests/Browser/AuthenticationTest.php | 92 +++++++ tests/Browser/BankAccountsTest.php | 186 ++++++++++++++ tests/Browser/CategoriesTest.php | 145 +++++++++++ tests/Browser/ImportTransactionsTest.php | 233 ++++++++++++++++++ tests/Browser/TransactionsTest.php | 96 ++++++++ ...-11M-14D-11_59_09-Últimos movimientos.xlsx | Bin 0 -> 53213 bytes tests/Browser/assets/test-transactions.csv | 7 + .../ResetUserCategoriesCommandTest.php | 12 +- tests/Feature/Settings/CategoryTest.php | 2 +- 10 files changed, 769 insertions(+), 10 deletions(-) create mode 100644 tests/Browser/AuthenticationTest.php create mode 100644 tests/Browser/BankAccountsTest.php create mode 100644 tests/Browser/CategoriesTest.php create mode 100644 tests/Browser/ImportTransactionsTest.php create mode 100644 tests/Browser/TransactionsTest.php create mode 100644 tests/Browser/assets/2025Y-11M-14D-11_59_09-Últimos movimientos.xlsx create mode 100644 tests/Browser/assets/test-transactions.csv diff --git a/app/Console/Commands/ResetUserCategories.php b/app/Console/Commands/ResetUserCategories.php index ac71fcf8..a564c077 100644 --- a/app/Console/Commands/ResetUserCategories.php +++ b/app/Console/Commands/ResetUserCategories.php @@ -29,9 +29,9 @@ class ResetUserCategories extends Command { $userIdentifier = $this->argument('user'); - $user = is_numeric($userIdentifier) - ? User::find($userIdentifier) - : User::where('email', $userIdentifier)->first(); + $user = filter_var($userIdentifier, FILTER_VALIDATE_EMAIL) + ? User::where('email', $userIdentifier)->first() + : User::find($userIdentifier); if (! $user) { $this->error("User not found: {$userIdentifier}"); diff --git a/tests/Browser/AuthenticationTest.php b/tests/Browser/AuthenticationTest.php new file mode 100644 index 00000000..3796c2a9 --- /dev/null +++ b/tests/Browser/AuthenticationTest.php @@ -0,0 +1,92 @@ +assertSee('Create an account') + ->fill('name', 'Test User') + ->fill('email', 'newuser@example.com') + ->fill('password', 'password123') + ->fill('password_confirmation', 'password123') + ->click('@register-user-button') + ->waitFor('Setup your encryption') + ->assertUrlIs('/setup-encryption') + ->assertNoJavascriptErrors(); + + $this->assertDatabaseHas('users', [ + 'email' => 'newuser@example.com', + 'name' => 'Test User', + ]); +}); + +it('shows validation errors for invalid registration', function () { + $page = visit('/register'); + + $page->assertSee('Create an account') + ->fill('name', 'Test User') + ->fill('email', 'invalid-email') + ->fill('password', 'short') + ->fill('password_confirmation', 'different') + ->click('@register-user-button') + ->wait(1) + ->assertNoJavascriptErrors(); +}); + +it('can login with valid credentials', function () { + $user = User::factory()->create([ + 'email' => 'test@example.com', + 'password' => bcrypt('password123'), + 'encryption_salt' => str_repeat('a', 24), + ]); + + $page = visit('/login'); + + $page->assertSee('Log in to your account') + ->fill('email', 'test@example.com') + ->fill('password', 'password123') + ->click('@login-button') + ->waitFor('Dashboard') + ->assertUrlIs('/dashboard') + ->assertNoJavascriptErrors(); + + $this->assertAuthenticated(); +}); + +it('shows validation error for invalid login credentials', function () { + $user = User::factory()->create([ + 'email' => 'test@example.com', + 'password' => bcrypt('password123'), + ]); + + $page = visit('/login'); + + $page->assertSee('Log in to your account') + ->fill('email', 'test@example.com') + ->fill('password', 'wrongpassword') + ->click('@login-button') + ->wait(1) + ->assertSee('These credentials do not match our records') + ->assertNoJavascriptErrors(); +}); + +it('can navigate from login to register', function () { + $page = visit('/login'); + + $page->assertSee('Log in to your account') + ->click('Sign up') + ->waitFor('Create an account') + ->assertUrlIs('/register') + ->assertNoJavascriptErrors(); +}); + +it('can navigate from register to login', function () { + $page = visit('/register'); + + $page->assertSee('Create an account') + ->click('Log in') + ->waitFor('Log in to your account') + ->assertUrlIs('/login') + ->assertNoJavascriptErrors(); +}); diff --git a/tests/Browser/BankAccountsTest.php b/tests/Browser/BankAccountsTest.php new file mode 100644 index 00000000..a676cab6 --- /dev/null +++ b/tests/Browser/BankAccountsTest.php @@ -0,0 +1,186 @@ +create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->assertSee('Manage your bank accounts') + ->assertNoJavascriptErrors(); +}); + +it('shows existing accounts in list', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $bank = Bank::factory()->create(['name' => 'Test Bank']); + 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', + ]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->assertSee('Test Bank') + ->assertSee('Checking') + ->assertSee('USD') + ->assertNoJavascriptErrors(); +}); + +it('can open create account dialog', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->click('Create Account') + ->waitFor('dialog') + ->assertSee('Add a new bank account to track your transactions') + ->assertNoJavascriptErrors(); +}); + +it('can create a new bank account', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->click('Create Account') + ->waitFor('dialog') + ->fill('#display_name', 'My Savings Account') + ->click('Select a bank...') + ->wait(0.5) + ->fill('input[placeholder="Search banks..."]', 'My Bank') + ->wait(0.5) + ->click('My Bank') + ->click('Select account type') + ->wait(0.3) + ->click('Savings') + ->click('Select currency') + ->wait(0.3) + ->click('EUR') + ->click('button[type="submit"]') + ->wait(2) + ->assertNoJavascriptErrors(); + + $this->assertDatabaseHas('accounts', [ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'type' => 'savings', + 'currency_code' => 'EUR', + ]); +}); + +it('shows empty state when no accounts exist', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->assertSee('No accounts found') + ->assertNoJavascriptErrors(); +}); + +it('can filter accounts by name', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $bank = Bank::factory()->create(['name' => 'Test Bank']); + 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'); + + $page->assertSee('Bank accounts') + ->fill('input[placeholder="Filter accounts..."]', 'Checking') + ->wait(0.5) + ->assertNoJavascriptErrors(); +}); + +it('can edit an existing account via dropdown menu', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $bank = Bank::factory()->create(['name' => 'Test Bank']); + $account = Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'Old Account Name', + 'name_iv' => str_repeat('b', 16), + 'type' => 'checking', + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->click('button[aria-label="Open menu"]') + ->wait(0.3) + ->click('Edit') + ->waitFor('dialog') + ->assertSee('Edit Account') + ->fill('#display_name', 'Updated Account Name') + ->click('Save') + ->wait(2) + ->assertNoJavascriptErrors(); +}); + +it('can delete an account via dropdown menu', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $bank = Bank::factory()->create(['name' => 'Test Bank']); + $account = Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'Account To Delete', + 'name_iv' => str_repeat('b', 16), + ]); + + actingAs($user); + + $page = visit('/settings/accounts'); + + $page->assertSee('Bank accounts') + ->click('button[aria-label="Open menu"]') + ->wait(0.3) + ->click('Delete') + ->waitFor('dialog') + ->assertSee('Delete Account') + ->click('Delete') + ->wait(2) + ->assertNoJavascriptErrors(); + + $this->assertDatabaseMissing('accounts', [ + 'id' => $account->id, + ]); +}); diff --git a/tests/Browser/CategoriesTest.php b/tests/Browser/CategoriesTest.php new file mode 100644 index 00000000..b53766fb --- /dev/null +++ b/tests/Browser/CategoriesTest.php @@ -0,0 +1,145 @@ +create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->assertSee('Manage your transaction categories') + ->assertNoJavascriptErrors(); +}); + +it('shows existing categories in list', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $category = Category::factory()->create([ + 'user_id' => $user->id, + 'name' => 'Groceries', + 'icon' => 'ShoppingCart', + 'color' => 'green', + ]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->assertSee('Groceries') + ->assertNoJavascriptErrors(); +}); + +it('can open create category dialog', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->click('Create Category') + ->waitFor('dialog') + ->assertSee('Add a new category to organize your transactions') + ->assertNoJavascriptErrors(); +}); + +it('can create a new category', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->click('Create Category') + ->waitFor('dialog') + ->fill('name', 'Entertainment') + ->click('Select an icon') + ->wait(0.3) + ->click('Film') + ->click('Select a color') + ->wait(0.3) + ->click('purple') + ->click('button[type="submit"]') + ->wait(2) + ->assertSee('Entertainment') + ->assertNoJavascriptErrors(); + + $this->assertDatabaseHas('categories', [ + 'user_id' => $user->id, + 'name' => 'Entertainment', + 'icon' => 'Film', + 'color' => 'purple', + ]); +}); + +it('can filter categories by name', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create([ + 'user_id' => $user->id, + 'name' => 'Groceries', + ]); + Category::factory()->create([ + 'user_id' => $user->id, + 'name' => 'Entertainment', + ]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->fill('input[placeholder="Filter categories..."]', 'Groceries') + ->wait(0.5) + ->assertSee('Groceries') + ->assertNoJavascriptErrors(); +}); + +it('shows empty state when no categories exist', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Categories settings') + ->assertSee('No categories found') + ->assertNoJavascriptErrors(); +}); + +it('can edit an existing category via context menu', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $category = Category::factory()->create([ + 'user_id' => $user->id, + 'name' => 'Old Category', + 'icon' => 'Tag', + 'color' => 'blue', + ]); + + actingAs($user); + + $page = visit('/settings/categories'); + + $page->assertSee('Old Category') + ->click('button[aria-label="Open menu"]') + ->wait(0.3) + ->click('Edit') + ->waitFor('dialog') + ->assertSee('Edit Category') + ->fill('name', 'Updated Category') + ->click('Save') + ->wait(2) + ->assertSee('Updated Category') + ->assertNoJavascriptErrors(); + + $this->assertDatabaseHas('categories', [ + 'id' => $category->id, + 'name' => 'Updated Category', + ]); +}); diff --git a/tests/Browser/ImportTransactionsTest.php b/tests/Browser/ImportTransactionsTest.php new file mode 100644 index 00000000..d6714ada --- /dev/null +++ b/tests/Browser/ImportTransactionsTest.php @@ -0,0 +1,233 @@ +create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'Test Bank']); + Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'Test Account', + 'name_iv' => str_repeat('b', 16), + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->assertSee('Import Transactions') + ->assertSee('Select the account to import transactions into') + ->assertNoJavascriptErrors(); +}); + +it('shows no accounts message when none exist', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->assertSee('No accounts found') + ->assertNoJavascriptErrors(); +}); + +it('can select account for import', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'My Checking', + 'name_iv' => str_repeat('b', 16), + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->assertSee('My Bank') + ->assertSee('USD') + ->click('label') + ->wait(0.3) + ->click('Next') + ->waitFor('Drop your file here') + ->assertSee('Supports CSV, XLS, and XLSX files') + ->assertNoJavascriptErrors(); +}); + +it('can upload a CSV file for import', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'My Checking', + 'name_iv' => str_repeat('b', 16), + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $testFile = __DIR__.'/assets/test-transactions.csv'; + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->click('label') + ->wait(0.3) + ->click('Next') + ->waitFor('Drop your file here') + ->attach('input[type="file"]', $testFile) + ->wait(1) + ->assertSee('test-transactions.csv') + ->assertNoJavascriptErrors(); +}); + +it('can complete full import flow', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + $account = Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'My Checking', + 'name_iv' => str_repeat('b', 16), + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $testFile = __DIR__.'/assets/test-transactions.csv'; + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->click('label') + ->wait(0.5) + ->click('Next') + ->waitFor('Drop your file here') + ->attach('input[type="file"]', $testFile) + ->wait(1) + ->assertSee('test-transactions.csv') + ->click('Next') + ->wait(1) + ->waitFor('Transaction Date') + ->click('#date-column') + ->wait(0.3) + ->click('Date') + ->click('#description-column') + ->wait(0.3) + ->click('Description') + ->click('#amount-column') + ->wait(0.3) + ->click('Amount') + ->wait(0.5) + ->click('Preview Transactions') + ->wait(2) + ->assertSee('Total') + ->assertSee('New') + ->assertNoJavascriptErrors(); +}); + +it('shows column mapping step after file upload', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'My Checking', + 'name_iv' => str_repeat('b', 16), + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $testFile = __DIR__.'/assets/test-transactions.csv'; + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->click('label') + ->wait(0.5) + ->click('Next') + ->waitFor('Drop your file here') + ->attach('input[type="file"]', $testFile) + ->wait(1) + ->click('Next') + ->wait(1) + ->assertSee('Transaction Date') + ->assertSee('Description') + ->assertSee('Amount') + ->assertSee('Balance (Optional)') + ->assertNoJavascriptErrors(); +}); + +it('can navigate back through import steps', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + $bank = Bank::factory()->create(['name' => 'My Bank']); + Account::factory()->create([ + 'user_id' => $user->id, + 'bank_id' => $bank->id, + 'name' => 'My Checking', + 'name_iv' => str_repeat('b', 16), + 'currency_code' => 'USD', + ]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('button[aria-label="More actions"]') + ->wait(0.3) + ->click('Import Transactions') + ->waitFor('drawer') + ->click('label') + ->wait(0.5) + ->click('Next') + ->waitFor('Drop your file here') + ->click('Back') + ->wait(0.5) + ->assertSee('Select the account to import transactions into') + ->assertNoJavascriptErrors(); +}); diff --git a/tests/Browser/TransactionsTest.php b/tests/Browser/TransactionsTest.php new file mode 100644 index 00000000..f6139b38 --- /dev/null +++ b/tests/Browser/TransactionsTest.php @@ -0,0 +1,96 @@ +create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + Account::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->assertSee('View and manage your transactions') + ->assertNoJavascriptErrors(); +}); + +it('can open add transaction dialog', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + Account::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('Add Transaction') + ->waitFor('dialog') + ->assertSee('Create Transaction') + ->assertNoJavascriptErrors(); +}); + +it('can create a transaction', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + $category = Category::factory()->create(['user_id' => $user->id]); + $account = Account::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->click('Add Transaction') + ->waitFor('dialog') + ->fill('description', 'Test Transaction') + ->click('Select Account') + ->wait(0.5) + ->click($account->name) + ->click('Select Category') + ->wait(0.5) + ->click($category->name) + ->fill('#amount', '50.00') + ->click('Create') + ->wait(2) + ->assertNoJavascriptErrors(); + + $this->assertDatabaseHas('transactions', [ + 'user_id' => $user->id, + 'amount' => 5000, + ]); +}); + +it('shows empty state when no transactions exist', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + Account::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->assertSee('No transactions found') + ->assertNoJavascriptErrors(); +}); + +it('can filter transactions by search text', function () { + $user = User::factory()->create(['encryption_salt' => str_repeat('a', 24)]); + Category::factory()->create(['user_id' => $user->id]); + Account::factory()->create(['user_id' => $user->id]); + + actingAs($user); + + $page = visit('/transactions'); + + $page->assertSee('Transactions') + ->fill('input[placeholder="Search transactions..."]', 'grocery') + ->wait(0.5) + ->assertNoJavascriptErrors(); +}); diff --git a/tests/Browser/assets/2025Y-11M-14D-11_59_09-Últimos movimientos.xlsx b/tests/Browser/assets/2025Y-11M-14D-11_59_09-Últimos movimientos.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ef4a3c10730c07b7f735ee257a8a3edfb86053df GIT binary patch literal 53213 zcmeFaWmr^S|2+(%V9+8U5>nEkFo3jlcY~yWgml-SNTUc!NF&`yH_|1b3_}kf-8gg% zGtU9^zJK@gyI*`?J=b&n|GZ#0%&fik-s^nUKAbb_I!8qwtktd(D-}Sg3!K0X5_%O|phvck~fv_|X(ivNOlbt6)O6yMtIIA}C;W1mn^7%J}fG?q=Cg$s6xlzk8Mna=E9gw!>89O2Pt}p9vp& zag;g4ep8cE701S+;?(X-h#thj+jjaMaj)kTm!!xky`e-gJtSwbckS-S;r``pw9PQ{ zwoll;XjxycrDYHJa266h5^e7jqaO6~(ogY7cx3T@e)&GSbpzW{6YhO3eQd4H`MVhG zeC?n6FukYo?2S0dqQBnWDVv7@R$vl)HUVQx>gnf+5#Lb?#YPLay>w86O66ehu`r|V55bxVC? zjt-Q^zPB9efBmwSa$D2AdF}K1JrVx0Js8Vdk9E)r*L0cht6;Fo|J*@nm71Kid%%uS zxQ2#y2l#E|WNz!s!G8W)7_F$>#)%!YErST|K7_(P;ETz-Ka;7a*6{O}ox-bpmqGUc z*33YTuR#`oAq{TyL-bC<1mCT7QyMg1{xd;t#TGlij+TMIJ;f^!c@3r=sq(=}|E{R7`!d zD5$iWH*M|XU?O-lHhUFEB;={W#?a?`-Oi?#58LfR>4!p3_AM2{Ji+hfB&m!NpRP3|ZM#zB`omv~RdK zw_mlIk;MF%4l;~UEYvZ;EF*Aw^9v;{vP*zl(bD0;_f!8AdkLAjuY*TlM)D-J97ZG$ z&^-nG9$N}78#ZL7F4XG4%yO4r9AU4@;6`zfgR=zFQkU7kJB|4ACI4u0?Cs)t-dBOR zLtDabcZTzy@}x5ttV;mWj9y1W!@HE{e+pBi-e)(-iI2GV#$7tHJCscamqtVlBaq;_ zr-4s;R*Ymkjb?Nh1<~0dJ3WhRBIVE*5~tb;ih=KH9}CRrySZMr#VEXVSW>_CMEz-8 z{afKF4lG)^2=gbFVzM$TcSyZJ$>g7=?PDhH+QvOf&#H9Y@O;FQZn~rZeF&FNcj;iI z+#leo#EeZO{HE1<#~0OFKpIye9mWP-kh z{Lss&AG!^moZa?jM3Z()#s^x^#QnL@%`ST=d z)D5E?r|HHa_Js4s$Rg8ef?!NC_R->-BfIIaGqd{O-nvTY>b`R#^7?-k+{++sL3125 zG!Zj2G@{FbbAD;!Wd1_k#mUOf()rwul?E`UIX+^)dz!`7p$Pk&I`3X|hDA10(uWF% zQ@1jHBs}3UdyxH6`g8SILVe?u-&dh0=n<C)blGpJ9yt^MEcpeY?f(Im$m6ErZx2zLEc8v3^$sSAa6%l2dA8eamy}F^U2ZX zU?x#t;ln;#+SoTa=30#CC^%e#s|1A8{*$c|S3o_!&EV{p99z9E*{L|79jnmB*7d>zFyr&cB-SR+Zyu;UgAU zLFySd+`i3kY}dPe+pX;xQAE#k%iKqAvojvSwHgZJ5t{9}6S=fa zmh#q6o3V1s@V@B@7KTf-JDJ>zVG7Ak=b-WozTRYw?tzb+koEEbL%x}6{{3t_vPW|C zxSWLkauL2q1QA=}sSyHx!AU-KZFva;ua%64O+_|WDyIhpyz)R{WOH zuO|1&KXpX=CL0cV*P1pkM$ZDO>*?dV8lFXfvQR2!Yf10 zRhUAvFKXE(B8D>t$GzUPZHc!rn{Y&|SVNle?ucXe`ZJ}ksSlYy^$OqoG?QBE`@e6{3C|;(P#$2 zf!Iu$iE-M9*t&#GYxZ7BJkFsz+u~0sPfR2pJrh~MrR=VmnOI)OwG*?@)q8rjH)$jv zmEVCxbL$^;K2QkT$U_9YKGjHSG$xA>K12+@5Ufc=%tc=NprOb-^(2*y2Vu{oVRaH} zW%eT>oGgFpt4YHA_hU{17W9!jzpxJA-Lj-KQNuQ7(E{N^&u0k}vJz8@mS(@)9m~@F zn~0 zYyGCg5csOCp8d%HR{0pa9@l2DH0O)J{fO@5nQaS#`$AzxnzHzO&liuOLK?f`JeE9@ zoUPFkr5Xv{rpYW@1j88kYdhrZ)kQ+e6EF9%iavFTj>`qm`kGYmFGFz7BAXRg$8?h;aXsO&rQW-FI zW)G!I@DyhKL|b1HMtl)vJIq+m_58tV;B2rRAfomeb8sZgvp73ecZjVf=++CW40F&} zRz`F9@DsxDNPjq16WjN08S0c;G9Dy3F#RcpS_r7Nw ze8OqlC6oPrz^}W=dFC85^y${iaI!~AOn9XS5ncY{ZUn;}lAsw2-WNOtg8Xa7nK~aC zs$PQ^CbNv+=DcOOo3PZ=Q-w$WQUp;ej@gDAnwkD4F|I=YFlmi64R+Eat^#p-h2gxV z68u~kD^O444%DD+)!$-PBl~f!Q^GfRtMVtuVdFTFAVK+*uqLnUy!0U!(AWcf!ncB5 zKM~cgGfi?Fzb}j+I3S3pxzs1vfB5>6o=Co2ZsIPrRfJ>#L|Zi>;xZcXmAi& zb_J&omc_?t3PHkhtjoPf;cS!*Mn{sdg4D{0Z76kp4jp!oR%xvaVaLYVmWi(@CMliJ z;Qq^KZLOVnB8m}qXIZunhSh8VU&^dOr12wk zD_V7VUaY|h_mT*B#C=c5iV_2f+P``2OM%T+>_fJd%T?$0jvmR6Kpjt*2lELNnM>O> zwcfTL^Dph#$A*7mBf2ApDpp?H(`BKyyNU8;)3Vjqls7Ef+yC$(w`13klVXp`{M8c9 zEk8f(V5B&y#7FdFSN!AI8SiJQ5m1Sj=C%UmKLvYU3BhW&>gU_NzrmkDW4fNb{s4Tf4%jktLNotheqR_o(_V5Y(9*v8cu5GfTAyQG2W;3 zrEemb`;khmaxD8U`(a2m<)|?UhSGyVUE*V{?XC?&f{<~!Phm{nx;p}70pOXKCHFHE zK2?Q@$Z@I6JC!m|4suLfN{CtL?5m(Livk@*T1LD<1dZAQZ>cHWbw!iOMa8$&y@*Bn1= ztKKVPNbAKLhbS&ucJDD-q$n&1#!$4TdKiQ$N`=rlaG_Jm47ur+L8B~H9-qEj)(tLo z&8xzq`jj86)vv}ubEhQFaxJf%oAcb2*x<-c^Tg}t5dc?4g+EBCZ3x7LUrsrRux0Suh z0X>hJWU_vv@BX;M9gtx)EIc798?!=hv&wn9ri$V7m$@mMnhrbm7?VB(Rt+JJGpAng zbAHqLSUEyYiQUH^BzHbp%IoNUzNXHCC6Fd{Q=?q)MQnIxKegq93We1;QG#S~J7m>3~6Yp$F88#pzXQa*P^rzAiniJmFtZ&)SYEOEWeEqpDFz4Ph1 zZqU*y<{YVxxR6SlqN(~~t{|5`Mcn$AG9F-cdGS<_se^|ts=0!_N$kz4Zk3Xdu4s{G zm4!+43ZRx%Xh3xE-0)sp+UT7OZ$ck3%%6NJeKINEKWja99UXCWcJoixE;gI(eM8Wn z7+vSomM@SZ{g^2T7}M>SgHh7cug%DUewoO4P`H`AijxI52!_f$rFD+lt+}49-spgd z;IS-{f5A-95>vhIG2->03v)l4}20&1;bLOi}+(MKacP z>_Mr}Tfrx_udUwiT2~Zl7aaT~ak0rctAyM1cU13(mDk5wnGrW*+6<=9`TlU+xb?Gv zDQ{8h0f(})O7w_voQ3iuIZ7N}eMPupLEW1nCzX4|z&gw+ zS#oCi@cX)~NC8Wb`LowdT)Odl+JI>vNWK*uvNTJWS1{K&YwfQ0gE#!KTszNd;~k)GC@{$UlO+6?}W5XobjMLYO&Z?yyPm3_Py6_My~nxJ%q(m zYidha>(LBYqai%E3flgSdn&*0$RkL$XY?mG&e5H<8xufjFmLP4#0Ufz%^*`)Do+mQ zLttMp!ql^uuBo_d?*>n3L5=P4zv%awk?}Rw;42^?z zx4Q{A9isy-qR?KRmc4K?aX&vJ`*XeI&!3O~b-m2;7Y2ZXv4RJXCe)(dR9)!XA2)(|!!Ra}T&yw9WlxIO@q;4GnXLubd&7BxYd`UbcQQqbZgp*?p_l zHhl7bDbpZAWi~o(p7xtyD8RvLb-*t;{+eZFWBjLq2wn$I*05E{r_yUS(f= zy}^f16tJ)boix#(6)Rby_CiFjFz7@%Rv|TtKeI&*)5V?WqloWKhQ3C2VA_VBI`So@ zMsiIRj8eY9rXZwd8cbB(ccK@gi#+yUlI5y{tQ+cuf6(BgGVXL9is+P(8s(V=Z!;Te zGxgkjb6jiC)z7GzuVbVa{TSQK^D_xuu%$Trlfu~onOMwgRIjHZ*x0n03*_%-d%kr_ zdObGTM*Z&AnpM=#P6?0TmQe#)A$+c{C6agTgIaKL^U0{Y%KESKw-?@T;m7bBrp42E zOB#Edk~<^pI5d{0nEltj6QTN@CH~#IrJN*fB~o(MM3CfAD!bn12$^kjd zye&cZDN<{-WDE(ia#^Jh64VYiWotXEUAZQTabJsD+S=d)^?T~DNxth~MMz zw$%4jLi~P588M5BQ=Oi3Na1t+*Ax4;o^hH|e40nfM5c2rmEie}Vn8=MJPqA$rIP&4p_C_~p+SVf z6lG^{dgUbFp~2++d*AZ#%XX{mbK2FN<9-?Zdb=P&eHKB5PZ;kOsWw0Z+&Yb10_#CJgF+_jF|eAZ5QVt zh*QV;lI%7_Qz@~ACNR=GeD)XN>wa>&nv;-x7mOllsb&FGto&HN zfDDSw8c7b{boOW_$`3=* zUNgJG`D6@=rsjlF=v1psyF^q^3qTcP)jFH3N=|VFY&v#xjn(j>%<&XY+2zepmlw5( zNvzV+{kf_}#nOzOl3ZkBi8xf{*BaPe^Fnp6xodtU+Qv$KEOs!RR&jYU zfisIK8hy-86pg6}1$nyggHR#tt=w zjhc(La}85^GTy34sqD%*Q&=S>G^VfOUS7Lv-;s&9@u~x79yPAD=Xs5Uhy`QwHK|?f z4Jm%3L;@1##cyJga`VOhHlQ{Nd^QIm>`3a|S-(jH$$|>1`l+RPd_|D3`sUB;w{9;- z$=#r?7nOf>qC}v@HptEr9rH#(;^|s%Yd(x2`0YM7R`FW7kQme3I+KslnAlq{5XUop z*lP@R-N!jv!aSb`^(j}@zQyXeA@AEnC5WF4sQ$Q_;+vhFFw|m$d*Do28T)m~t-(NK zS5^NT=Cm<48U9fP*b(q;*Z=;;hcGdFo7?~m zZR8C;8gQZc{2L!<7f)OBi%ZL!F#YZ1<*1b760zS?OQ`YT^2e-Ln{Ib*ez=9{fcHUN z?~5(X)J-~A6j%JVuzcn#==0T-nt;7zY=>hBI?ckv#$2fuyCSL)4ipT}en#*#N1IBx z$q%(vu)MjfJeb<(b=ZGuZ`6Exrag!%0V7T>c!nBlju{fzM80yBrVv;0KA#<~@^fRLEVbX4E+E_KY8D z2eLlX=(l?UNfqAqLT+sGHXj`}gAb8yeKU=o4J&xBnh;Clc&~g{z@DwkMkn@^I|uu` zRMTJ|6f7rIB(})|1#>TH_S~OoTK9D{YCtUIC`MMI%o6n)Jl2d_!TWvgV4u_dNo6V{ z#9G`;BlvK2zI@RBC>({@Is_5dDG;BUSfZ~-jovnEmf=xQyIcA#tc5^tI$50)~ z%c;6gky+Iz+J%TzxEPfms=g#e#21M-AHg-iclGjroCYX+0pu|U6;<=*ej+az*;nFs zeAcI&YUt~6xIfP@h(yeT5s<@@CNJdHS-rNA-|liPYG4mA4MKe#UX2Qh+@5Jb9vqu( z9D-307Vz1zNNQZ>Ot+j?S#(U{3wjpao~z9(9j=Pt{}*OJJt#sT;v#`#a944b_he)5Vm4nmeuK7OfGSSZBXW-H|G!m{>pVkea|*mEBEvu{QD6>4Zo z8Jy}eB+HO|^>U@bRUKT};A$zjS_!XK!mE|=Y9;(XV#^BLR( zwOSuJ*yM@oa!+mYnx>3I{HnLyMjjokSEnM+sw2$@Q+*GSFeC8MNq?dU>UUoPhsZHp z)pIfqmJ0q|Vsx+(yp62W^m9){<gut{z3@iJnEL&WzW_>jsFHQKe($7>{nLV#-r zQ_te?IGRyi+9HTWMMK1ISTlnE3_QM$Sj1UUw*RsFpaOi*Vk9C3J{d0&Q66 zh+OL8Jz9c8w5gEwCHNwZt=%O*d`n9GyaGQPnH(9Kp!RM@8JV0I{zB~CW@#2XGW>wVN!cE7}2ez~lt(Y7;Se5k|L=}98oqRNFk(V*W#Fi|ME|B-w7eLPF$ z;PReD+fK+hWOc)dKN0SB;37~zHDK3}EQI1!s7;1>2zb{eKS$|j|Id$L1MJo159}?K ztIPW~H##TBA)6ac0`+jW_kUZ{{(&sZR6BQ{L!J(8k~;|mUqFJV222`~X;FACwaINB z0@8KK;wb65|M?MYfV$e5)c&$HInWyNr}cn~z-4PL*Z|W7M4{TbcQfe%lH??i19t-; zIa32x4ar<66z`&&()?!9s+-ao%8|@|`G0|>>XOy%mbs?~fN@^X++TGIx@a9`XW6}p zJ-->@A|P&O$%krGsY|A==8CB18rsCJO$H_qh1&lxHNbh^mTS+5;J&?O=Hz`R0boYj zf1u7;|9(T>&JviAT(xu0ra|Xq?a-!ylRzBYt=L5%Zfal=F}k+u#$KD;chOkhZu!ad zKn!ZLZfd~1A(;g>kcA2Yc7=Og^5sX#cFWO|wGq|MIg_=EZtU}$Q>$)vwaJqg5PiF4 zp6LMq5<6M@?hnLnIVXB(bIM7e8t%5`B2Yaw@TwtM0L&+Qf&FN=JTk~|-+o!P+PQCY zs&mqFXjAY4o8lr6IW=I`kjwxZpsn5oW*}R=+r26H7ZM3~yM)XmR4nU~rR|m=1#CO-C=cNEiYzMkimXa6rY5DvfP1N8!9&V@B2Puw zQJ+LOP?=QmU?)FQBop`rQdWcuwIG;(|A8-ZzK6r~hBRAqh@TU($5SVeB2VB1Vc0}M z;FTE%4U3eZ()3$sD7^L|Otg%U?y<}bYTbL$oSAbU3h`y>)Y4_N!s$r!UeYm0ThB$^ zmT?Npd_xDJbHoJJV#x-cwia z98P+lXWl)m@dU(RZaw}4)S=Ly_gQaAK~YHE`Av4Lq`Laha8h=j`S7p?2*hAxJq`i| zD)uua>SZe`zEXFd%Z_zbSDzkEYRWU89oFCjF*sU}^MNFl`ty?XER+bTG(%6xNtsD=cHA=G+Y0wk&0pZ7)2LRIm(hI4Cftht6d88nF}-<%w(Aq`@Pv>ulR z-B9ai%G7(Lrua_7nIR!;KgM4!zsD=@Up?U4I5olk#|5m9NrjFtbEoZ{Qlv`TbZ-5<8U>N{~8oTDStc@GH zf^=Y3rCvNb&vCTe$qG|&w6p`D$^V~2*0sgVF9x+<{bysFM0~L?AG1Ke`GH&$dA^Up zr~UQxz0>r)GQDPMxh9%?Kj}}qWxdrBF(sj8e#)PGR5gyX8P~NXd)y^sIKs>P^gsDL z*Enus1g5$3PIZwb1DJSUoK%ecJEx0zau|^>a_a~^C@jV-5WvwI#4IRiSyKvtmba`~ zm)1dy_ian-$O^ZvAXmElqhfgXw_a%fl+;K!rcLg7NrB2*$gE3)AjSYBh^+7zw+;YF z$F$+YxrC5iL9TSUDu#b2g&J|J)>!JUZ$)BQ9ixz`-oP1+dS(apS-XV@2U;<+j|;X$i9}6~ocu z!_j&XaL>B5)vA={L7_^(y}w9@^QJ7r#(@xHfGZu7B?*(ItmQUH2RIB~QCw;9e??vB zU6=;2Ld+l20B^8eSb*h0VHT#%Q=E}rOq;wGJ*9xkvKBq-(pZRbk!@)#Ss`$Was|24 z<*FF|sYwR@`l$Smk2Y;ft1f&b@Sv~=(?$Si1b`H@OaN9ZZ<(+zt%4X|LN>W|t{_*s zTouDVHA&AOAGwzO@lmpE>3gz5z())Z3NtX*fm7NZ%=O$Bex(2a!f##r9%6h6nZJTu z>2g&J|J)?~e|=Q`$44;R(qghgz(>3f3JWmTfm2!llHXDXSgpLJ&bqW1Vtff%zk*!p za#alf*d$usy*JmHTkDkG?t6Un(bqJ2p3~~pAj@BDgnYYGKw7ds|4ZCZa%(+Ko2#ks zll$u8^9bGpc*O_z`@ohZutmT9q_3WoV{_h0f119xo-`8JlAbn?aeq8L*##+X=wshD z6{^*>^gULOcK=EV!>(vTF!j{y4BCAxFOXU9tG_=Zxn;-VK6mW1@@N&41 zTGdc0$JI~v!CapdpYt97ywMlDjZ@DVmM(Y!*lO(sF93`F7cBo@unlWv@k_RP%556= z>bUy#h3Ap(QS}1AcyE_j?=!Ub&hU$Eo9 zu%4y~$fpZfZ%J2uH;iI&iyuBZ|9oL<0N_Qg!(D3Y%YpHtd!O^Nx{v04yWj=JD^_~W zdk!PsIG5`jhV>W5)cP0prSyQv$=$NHUWw$ugX9v1<9#j{Fv`5PbCDhZoxUEMw{mhn zp8~--hux?8^%tg*IX$p4 zem$SdbAWbpY<|_rT^oQ2A9y58!8Esu)jynbxX0A5OT0Mm;2cIVe%|{zOzvN>kAGp; z%{Q(3E?|n|=gJfVBP!g0vpc!F0}XT!JnE)k&w;VUT{Hl8(VqD^FAU%fz2H5EnP2dp z!xH|&DlGoOYIVK&FWDR~G^z)rw$oW9!#7tRi#zV2uE;$wdQu zph4mVul`o?$D9jZ07hhe&U+4v`WGzcFDxd%lA*S~dg?igmW{HLJ3ygTJo2`nGRMii z$gn6>+oq!cRG@7$2i1dH_pd<{JSw#s^vb;|9~lH(o)mbh#>qe`t~ie|;46*GJTb zuZT)UiVRH(o)mbh#>qe`u0BaO-l9N~?Wq z;7A2HtjLh9y+HE9SYi!&L?y&pCB$>kFCLY^u?)Y2xT7EtS`g?_$!N${_y=in-jpp& z`%Zy2z@^%tr_-nxq8&a5ZC!(2QCw;9e??tXU6=;2g8U!T0B;msSRmG;QnO*A%&U^# z@YTJND&Pnfs;%Eqz*eBGKL^!jeD$iNHQXdBaVs6eLPoD3SGrsk!#^~Mt5+qG?!rjPhMVW#+=pw| zE0r90#JRXPpT|N-x3}h3#m~POk8}T5;Kcp#Z{Xy7>oRZx;Qj z{{~LZ8(aoXE_u(>8W+6hfs;$#^T5d^@B4ps|NnxW{0W=@us?y5#)-dy6M*+JaPo&2 z2%P-k{nvO0{>2LfPX0R#`6qA!>|m0MzzG2R6F50%y9}HFodVg2e~s-ta01K}2soSv zPA=yM1Wo{X{{~JjcGh{|q+}%ZGH`OK(M1~NVlK~tXaqpJdYL2vdcO>uTymTTPA)t6 z0|Rno_B=@fG`I|${NX(hoLmeXcG*3^`v(Se4+KvBD@+gk zCvbA95fC`}qmla7R&%YkH@v@q*B%LRrvmPtOa=Zud2uV~`EvVd6ub{IR3hwsxU*SM zl7i|wq?&H>*+q=w&72**5oy|_GHO2FJAuWeHXiM^a(Mgg_t}Gwwn`?2Q7~l=@ZnO7 z@Z#6`N$r_N#C)PM>S(n27`mJ4hX6O69PCrgOgAf?rIw&p&vHbR&-Ug~zfTUqN5dnL zgC0c4;K)>A)c!b?NCR^17g&FDP&iYB2KP-#3y5?;HLC@l_}mF*l#sH5@F`g*;vapF&D}_XW=u z`qsh7UGD}rzr)E<$c&`-$@u7e-kEK2+_I(RyQA^ZpC|Ssh<%av(J0`K*9}SWImXj}$>}&LEE}jLw?>;~^Lu>QY~Y zws_FcxHQnwNH1?A{y!cG^6!U%#3aLA&#g}Cp*XeBHL8WV6USS z$RwPz!PgnJ-`3WpYy>`C-9#acMz?z>d1q3$n*1705x@75In}5p6!JjC4+)P9?rH|3 z&XCB}{@SzAZIQ;av(?o}yzLn<>i6kcT>W-%u>GOYpvdw5FL)meatszm9w1MvyH4vP zz8*(Ao2>9^M^OL(XCw^xI2dJXv)_r;zpu1f-ai1^3bT*&%*W&7@K`tr$HjA{M?$rRC>S`|J}Ye2^zTv!#M3-q4Oy zKHzweM`q|@5l-uG(*RbSodbK1+GkOii-SlUO|oH;Q(i%ohwRkvUXjzYXm}qjbSq^G zPznAkk~cE$6By-oIA#Bq>WR1M0CbSA~*j3Nb<#sV2v-9_#9zgeV@$4^-1smR^I#(s;dDB47ANUR`j+1b|ambPM? zk!xA=8Z@Pw#I#Cc^6sJ--O=(^s26n^eeq#CpTP&ysElWwchuON%sx0g>uh;a9TY0` z`Qx)#OFMGyPKhT)Q3|J0@*~n7Y9-ho>lC=Y6aFTkqH1HMrlPhVwK`rU+;X266n*&K zJF4`p&)eZ?RvME~x0{bCAF@zG(`mJKHyqF_jkwfstipW|dK3vQ|jKY~A-aCfxAiye~e%53KtGuv<^;GIBl|tJd z#xyq9;l=ytt**~wQ`yt~c*Vhv+=N+FicvbI6zVwa3EnYzDF0OKj7U$B1lM;KFy7l| zFUhj0;1;CERlf@C;_2tmjrQvhE}{GNdE~b-hjSPFL?yGj=Z{%2&_T>+!H5dWCHfVU zEv&tv!M;+nG2V}Y-$dqm#m;JY?{pw(t*Ju={i0LSm#GGa0_G08!!rF;H0k6uY=r5K z++lj@$a%lAAA|@M8RIUt(s?8X$8j#H4#vCZxw?!WzL7B=cn>+=7t78|T*`pP%Nj_U z-STFgp9(D?SQFW2xD!Ki3hDbGHdjX#n=Ln27wjZ*)GsFgd*0s>1aO8Vuhn2XJAX0+ z4U+0+Jc;TK3&S>W&P(nlOGjpa%RV%qW!g7lx6_rK{&bF=W;A_)qfxKIrEtA8j;!o;#q;LhrLS7XMHeyB%0V5avSxCi-`X7DeGs0} zNEkFCT6+g{M;5^XY{?^X(8UG9ll8<<>NOfvQV z^2b=#|3DLThtZcoCW3bmWC2lf3gEgX+W$NsJ*GRjU+uHccp0MMhjJp68dt%!eM`xT zHJIpeyA(72156E%o>;Yf5MZ0<$4sVj*YiCaeVzmD46_JH0WmBS zdj-FsYK@gCUYtSz_BDpauV2mdqva4qaR1l9mWl`Tqxhz2Vj}IL1Q5(&I+V*zN79Am zCoXTWwr?`GR~0conSXkC4O^|f2A!Vc#%g%m(p2koe1vJ1uRj|o8cm;hFV!T?6m=h0 zjRt=b!9I9|9f0fTP@oizzdUpc=#3ILcV~yI;d2eXLcN@rEhGGgaC?@zrWt^oWL~lbEnbHjt~FXnnY8Z^14BQF;<^S3nfX4rQfx^BN#E zk(X`O;A~a(qoiv!0canI-b~~F4K`@qv z&riR%{l9Lg=Mo+!JG{Uip2{Lq$*jWs)IemXLA?la9QPZu&F+8x6-Q)+J8 zUvKEcc)aPZdpb}u*MXAZ=1#NN!PaMvvIOdd%QvV9*U`P;y~0V>ZueO5m`v2%&aGVd zqNRtkdwPOfG#RgLxrPV<4kIvSnk{yX^b}lo~}gtqCf%x%cr9XQWIn7)(SfU z8f&~zGkl-e9R9Q7VAY44bpcvA^vsE3s6 zyS|t%NtwZiet!OmKW1j z4M$_~>XR2BlE?&pPQ`xrWC zzj2qldob@S28HQ{Rx0I;D_@utt8?^fh1Ce8E%ZMO2(xl_huBW^*t|)N zWBKMpW#iwIWA^QY(Rhb|s#KJpZGbhb&5!q82i}Jwtw)dSW1@GS%PjiOo2k%H;xWp0 ziH2oDEOxSAkPWa75!2qX@|T5m?|+YtHqBrE{1IW0fA{Ui^a4R=1&Ppk9JTW5hr&u*!S%(ZenX#I(YjRN5UWdaxAME&%-(XQb8pYh9 z)_qTgY`YJel>Bpt{?QuPwsh6^n|d}FA$o`YANTw_gh$XIFKQywI)H{o&&Kf>s!pFX zVHxDQUS~2r)uR85lO(qxF(F4VY00!dgK>O&AaOep3!ORCN@gZ6 z-j4GcLNu7KSyP%n=V@ec*t)i87@tU*A^U27;%?$$ki}5hVMvVCw@#o(+HjuNVRYRY z2%j&{s`%T_iulXId{!{W

V3G(4YnC!QwuzsA=)a?{^r%f9m+w&n1x`#>OVukzdY zg6_a)d@Q8hj%t-yJFe4#5XaIhLg{JWw0CMW><;&$V;03z@M^pe#F-&D*X7Cxx`}oh#=kxN z9K;n~-XX(JX8eMJLFEO-a?--G3q=mJr~P{V`(k#gAg^yQNuX2|9;%!_9-j9upC!c3 z^jQ^Yk0b_b)YiD#j>Yw);rMd2xohh#@eWTJWC*BoqQ<`Yed=o2FD1LkIuS z!vG>je^FUx8YW~`|F!JKg3r*j(}#edU(ls`!N3Poqa5$9t;Bjat6%>Va?2nv^SXyy zT{53Olj*hBLmjAv(>NZI_%~KMx=A?1=Tf!<>q?^TSJ9X^2m^&Q4wcuqk6#zOcTpIW zX1|U&dVR6q(?j=36R{M4Wrnqzn6~yMad_tKo5N#NJz0z*qx;E77l8tC?Ad%;0nULE zkMqMAsc70zIBjjkaWnU4x6e1mP3cH*ICc91H1Eo1Ij(^IFTP_eacT=w& ztyrxtxa7f07D?=fDs_X2tkLp`=nCalxl}F&EjLJIWiS+kH(pk}j;4-1MPsjeMNB(g zQlppm8Bj&88s2g2?>h|q+ktiS6H#yIPo?^goFA=nZi(4n(^)ga;S?f(+?X&up?8c@ zx+cS+iMM6ITkvVDC!U5*Nf2*v@$R9hK9ieZcGli$|Fy=?-##g}g#vgwDjd#n?NCW( zphCAc(m&n6zq=BqktR-_)*EtnP`nV!0Lv^!#ust@Q>G~mT^|u|QRM;*Si@{ZUHZV1MbhV?N&?Ok*H65(@eB|Z zBVq#~G@Vso|CRk5R9k;+%Xce*0lk85a-FA;2>!ZYNbe>&UR%WLDz$d3Ygwxg)lWQk zUymDjm1+$z4Ks9ybb}yB2$Ir`q;!`c-7xeJ(j|Nc-}nFB zd*6F`o&n~W+51;}ueJ6*Ge^o#V@c#W9>51<^JdT*1f@Nn_3SN{K*!(R6#9hj*1^TQ zZ!Z{ART5%xzQ%qA*o5Yi!3l!{e~P2#?{rlR6i+MdyHNEB^Q$`l5|zx#*o!xlneJ;#w*T- zxdAhKPIXuW#mc$PP_T@!zSdp14~2NR?sqL&Oa|ZV4`4N5r*8qAn;Q+EXKv3CzJrG904_B6QSIRl~{FSmAF$$^mFY2258<}BaFpwGB*AE?NVkuq#`KV-3B ziDwa=Xz;2-{@H~3TLO>%(-eikLvO^!0^IzFU-~KM6b;q*Km^5*t0tkl^BT{`7;}2e zf93I@?}tqVV7TnOh$}Ej2mZ}TGh4LzuvzD7>{ej3d%Skk5~h1ClLBlNbX4D9ny!R6k3;+CF8-uEIMQnUK22&ixO|iRc}E#{cL(L+K+%(eAJZ1v_dr(U2Crd zWGTufea`tU$42$=(NU`z(Y~SM(XDW_G7yA?y}DQY`mdumkG$C6b_+t=Gtm{HydtHjERf7{+x6xkMB| z;fg9Y!)^QkfCPyT3M^Sb4ETt3ug2&q8{^mcUQ|^B)cA_j6PwFqf5631hxFMC@@roV zo)s^51lEmbI*iNwWJ`Ft`l?jgFBq0prBdCdM^UuR9e;jp329;}Y$ticVi$O3x+I3`epEPs|nAgY?~n7QpHo`5SA5l z_h0?S@1P;|jGJ`>2(O(U`dK18trG}WUXo49^)!t92touP=u~z~GKkBzZAC8jk0RJQ z6rYZTE2p4NGH&PEU{(P}hYr4w8^!iKBZJ z-IOZ#`!nLef+tcZ2KaXteuvNYW#AEVoh0N-4e>OV2HCbICjSlyRlNrduD}iSa%&WA z#(rUs(#g@py1MB}d@usL7arEV^Xb_O?%g4Pf<|(NUJJU+q1Pl*Jv+r#^^O%J7Im_! z!UK?nK|h8j54Ld7xh<^M!lzJ(pb!uC9Oe5Q;j|v(2BHqF-Z}42qkI67k^n@y3dus( z@c3A!Go`BWJ~{*TNsD9g0q4<;eozAAz1e)sQ}&|8#n5dgnk@LcAWHsR{Xy7d_Y@LW ziGIay?)tfrFtfFKRFfA1!KeRD@A^YK^Psnkt216r36i+9Tx zv7MD$z-*}l`pR)yHvP1cS`_+i1lNAvutrr!ufQh9|CSd^Zy8=e0{^~Ec-O%y_i`$M z+D;y8%A?PNDSEHe3?fwTl0fw_XW3qHGN8c!X_42!B5}^fw~vmlDANm*n3tlxLk;%9 z=mLb;n)uGqgQ%M-!cETjU-knzTFySj=g2zBwCMHrOpaH(n>+T-$(#hrT(JkH9Uldg z*VqXDNYjsl6jJuHy+>l1GNMJ5z$BJobZ2a3+PipXNw7@9`z~?g?s@ge+oGBl0)K`= zoBUr5B!fy)D%x14xmcWf^}(TisP3??Uu`Nuac0o4YpvHqjMc<0c``#M^gWOQ%HA`V zIm4G?np5haP_VnsK(FX*LNeD!@)F(G4S^dtYtLF>PQO5sFw@$oqpI&VbT~yQ1YY~= z@JCe>Z?e+$eNO4=bFt>>@2n%y>j|O;4s&nJ<|+ohh`3-`N)$tKm!yEbC=c%}qx6HT z32h<#*x2e9g0qak!EiB-JMJU2oo^m0TJ(s5l#L_`%2FzLgzHRLJ?B|2o4;IeB$W~! zSuhkw94jPGN7=`HK2%ADnTJyzKk$04IC22za)y4W=Q*2y> zn~cEO)pjJ!(bm#u-vz-9m@&^Y6(>LH(u%&SvqIAn&JM6AdFCwX)x+Ba)nYC?BeUS00OGYrqy!zu6zNuS^c zElJ|0+=SZu)$a5z#I+wM^jSfVOQR}^9}A0Wg~0G6AIw$o1Pt{CP`pidEJEtd1n(xE z3kOCbl8J9yBu+IB;EDu)dXx2T*Y#Z1j>cnhxlDaCr_uGNf}BB_74Vj(tMhZJ z_EYlR7;Dnq7!;*nsrsWzav6S^Y2j|A#V;~L8oMto_?_S(tc$byi%;&W_({+g2)j)!cWVS&Drn-ZqR5i z{bGgiiDS`;FH(hA@C*c;p&_F|2XtikUNCMYoo?qYn$qJi`<{%N=DhCMO1Bf^ZF9ik zRS)bj1c{yt5fg_v=-WV9`ICX5O3KQKOD1XR{>TQT&wj7UnnZN^oU-C~@!uUGB;3^l zgW?&a-F2=oMAz>Y@rXQO6vBvty+;C%_pp9;ZyUTJ&!dv?@m>-J18eMcGy~yTLHo&o z2#`_fTZl|4+!*Wa8*CVT^;ZB7-LmYM1tmu!eve>OQ5;}V^)e#dYp-GGH-OU7v+-gK zk~(hwOpBCJ(Kf5b@>}wn<6kU{lqC~S`m!zFfJh)8*d;~3FAIoEUEW@k+(VT2Eqh@y=KSPbspu5&$8;5*fhWwGHGP$9*Ny&oIwvt2|a@`6!_`UFo8{Iw>!DTzq6 z@X1Bu*|y;0yg?XUo(Li0uIbnddVF<0H8!9U;viHrK@H?7Nik}e;qDWZls~2+%FJY) zvz;>Zl|c&&?hd^ZWWqg=WS{n+{WJ%>r(07nt)9#$@O5|^gNf%D*0%`_k1)85vM11Fh^fXxVU3vP)Pwt}biVa_na-5l8yr8+U!LaDng`C^d z;9usy&)zxw;+Qg!63CaK{)4q#gKwXrpM?eFy^R`uJJzW(6S6k~pPk^j)sZesDy|KL z>MEI7mD&^|HrnxhlG#tQ2e*`@!BBy)ulOF!a;aRIB=pqpCHr}V;Dz*13#Tx$I^g+# zbifa!;!xA?5Z(0~vKCdCon)agaGo_Y4*D^1P8-d|8qlB;s>Ds9TI>Dv*mf&vu*p$F z;+QOk=dUh?FFKCTpv*3z;yQPqcKTq}=cbXwgXVG+MhZy-KU1N@mo%N~J{xNqdy129 z)C9UqSQbT0ltq>Ccg15NRNh8teWV3x4$B{EF1gxPNVn3Q0BUtS&l5cMJ+RNz`8khh z0QKMMs0Px8Yc(pfReU<7eMb$8(CzbbT(ocm=VG(XAY3xocqv+VfD?EFS_N~d-?-4K zAkt#PNko5u8qNEi2zsck#CM5^$?MaN%*72HaF17 zH!Au~c=xzHb?KQkxkYX49lT3Q-8&O0RV(~^NqUHd?~YGK@%!tU2TA%5<}+@|Q$$eV zZ{1jqStPX;1AA?@I7>P+gT1-){DG*&_q<>z$Re5H7G2gIyj-|`Itx@6N#85 zL(kf&mz$q#_hbM(bCEf!M}fA2E5J1lmiKF8;%+AU0OTq~-Iu^a&N;C0uk+uRg|HNO zkYNubGNU{33Z!exn?GR-lo?d>DK3uLHOvtsdRT;^ zefL(k+FjJe`K7P#GmE;aSOCb)@SJI2;_U5fdMND2AH0tq)d5ZCGw zR9`j8omRs+iO8E-Wrde#twV^`*G_72EG7WFV5V8Bt=`PedsmeZg2Txu2%fUTP&Nj5 z2$vojG%BEluW5hG1rR>tlLb9wAsXOnY`Z3#2{?p|8U-Qj48%q4t6xpI=)t*9v49=D z^wTe=l6^_CY|)hSh@tm2l&+vwESJQYQiD_$k8X--)=iTgExZ6siTlsMvIx2MK>3r!ZaiF{BIlkx$y zC&UvF;hzvYlSstp?fyb^R+W)PV=a7j?x{Hfupk*A3<_k(@3O*`e>AYZI)_Oj;X$Bg zZl{uqu4H6-`BVO8AcO#1m63|kc9)1Gux*m)Q-1x)9gkPJp&aed`QdITr~sH=lD>7q zln?2N@6rJfs9c>BhfdUnx>tW7nl9V|V= zrDB+oRDjW(trC$`Vv~+{qN%)MTQNn7If+{$%k^WMpA-xh5MW$Mn;{ul@yat{dm~cx ze|8dSh)SN%G7KPzhuq4j0iul~7i@-pduSnDt@xjqwr6qHauTwP_gk5tvoBA3EhZgM z!TTprWKZX)`KmGKOK-}!51m#2fPG1|c{x(6+WyQSCv~)eUqSbv5Pe20c81$&F7{v4 zXUVptyh1@gjdB$_n)lMoV)Km4OovQD0N_A@uM}DRT2Jx?fc(MIp7t6qYYT9dGO$FA zY8~W#d-&kdATHb^K@tcg@ExDr=a84Dw-Ctju880t^JOgk@G&m4KU(sue3ig$D|d~+ zLSbkapepjha$fUZM^S)oiJBBWqYTO2B)<3s|V| zd_#I4sd!QjaQc`S&dvFqfBRXeyx7r9fa{ade` zp+A!abjrDqrHb)mRc}7iL4S~hj0(Kf@q}$$%+VzEH3H0C zsF{gW;(SJjwzE=3dmE<9n!LCXp2UbA51zo_3l(xBxPZ}usl|le4JUptNPOnlW|`z?Jv=s$+W9n~0s?~6)BarOLUctM&=iZ~-Q1i%nDM$NPj*MPxX1}u znJ4-bkmHLn8kON*iBa{?j?cJ(RPOj zyHW!+R3{aE<&PdjUQ26Jr8);%JI}k$6nHywd*uCB@D*FPy+NO-uKZZlur8|DPAGa= zpoRH)A^t$c0URDg{x*L%tz?4|YG=^92F%B@B{cwx4m$icmnswiX7aIM#kF5P(7&!8NqDgV@p zo>nTPHe|OKAo~y*l`E3!s$(>Jg@(P?e?dgfcE8v!7`%mkdYN-nNQH{<7ow)}Q!DJ` z`OsRzqc}V9xOpAOhF#nJSbM7dqT+FQ?{8+LF#se~oy0-8NGf26P)1pV(JAmzYJEBS zel%!F;SR^h)VxenBr(t_deS5wk?wp#@cK~>r5@2ulgPmuPfM%^0(XXQbrnbE+C_z- z@BW~WwsOLYz_p|NsF6WVn>&-Fh`HfieC=6f!9qjO-+F%AI8ozq|MP8${KjY97Xb53 zC4nRR{UJc{rn5F<$2`w%?D0^K(NOLl=%^WKvJ0^7$agCGjJ~CnP(g;=(l$WXAWihF`wfH2Ql=$9H{dSB>FN|fJ@WA@`FlB#&66qy%W85!;rD>O;k*>3J zoue2`P4i1P>%B5$s#JQ}gfvY-0<3@NxPJ7Ryf&M06x z8mYyk&nh!J8v4?oQ1p*5gDTT!o4G698XT}=g#&41MuUD2vj(_iwoA0f;}55YXq$w) zc1-ul{iJTxUx7kW&tH$j52>6;#0daJ?+0a}+pN%XpzpHzYDv}t!7X^2^|M@pL%2+w zLwLEu^LY%Ro~5RJILN?7Dqlt>*`TxkI)>woa0w?;^b&K6>A_gXjD3d6??5AQOH0UCslS3=$FQX5htoYKnjJBvZUl@t%rO5JC0kIjtn7 z4^XW)bZ3)mlrfQIk#JTE@raDJJpK#;{*BZB$OT+MHH4O9F?);fYRi&5Y22!p*a!R~ zQ;VTSS=Q4iEEqdkIDi$Sq0GJSJ}mI;$#9BVLvOCgaIPGE$_uG%!a$(vsex2Oi1+PF zDZ!FN3IJdEAqS$ra%2slDzAkaDKDuoK|Q^Kj)p@Wl48e_Vv7x2*Y4qCLVpxXP28Nz zDN57S1N+)DLN{*vqh4UoOIZQE;{DY!s%-@k5)tW^iPkV5^f;_cMs#PfIQDndz8@sP zcG-b@8><|p`4C%g2C*8>P*%e9C+VK^*_Vj()a~z`ZLjvizXAAe=AIAl591_JXOYm~ z_1;T;PMLVwget^BN5T(qT?eh0 zXT=tK95vo0&|K4<6VwWbNKdB5bEl^_Ki;vt&YA<*CvF|`jaDwDOh~ul*N+LEMa@3< zqbStBI&)0G9pQBbFda&$Y)!$)o|FSW_OOUslJz|lAn;_az6X#9K$hunR|&DD`Xtg^ zZ<;I)z35tYS?`ztI*|%kGcF+QcAukuiRUiW6}%hyX_3qM7AS7WJL|2{LPMO`(V0s+ z;)M>Wc7?B?eWAEnUc2uBN$`1FeA_fJkIbC}cbyEcH!Xa*4pMhvdV~ldM7Ycm52nzO zFq8m*TxzGz*Xln>nGcAyhnVO#X9Dm=nVMRz_+mw%|X`Bn=sk}X_8W`lSsLJ*S zdLWz&4U>0GLTrDXaNbY9NgG33p7N-vd!lW>E|lTw^5$C@BWYM9+;T zaA^*-XKjJj%ej_W?&{54^;}`%A3+DCN5Y&7z)>=ZhpP1Wo-fSV0J240&K}3@+H?U* zBTIe(N8j?6`T4o(g$BV^Fn90YqGblaQ5n%wEv))#(UTm2*y-wxe3sFHDi;+sGy7gw zZ>@W*D-2L9dd_@0f7215G5D^U8EZKhzVQ7rT~dqEn^RVJb3x|)q*_c9^;fCzuCl_> z`$Dy{+;@Bal8#UFJ4MtYzfwz_?r!b~!KFika_A-1 z;*&wk8RG)FFY+qD5wN`ki6|pSNiX#05pY*!%-nB036>73kH&kO-D4eL?|}4MV&M#? zkwNbqiK-gd6N1R{QZi93FLuV-szx`>Dk};$2ztN+Mr~9@S1r}1%5(Fx9PPL_WiSZ~sD>%PdaTXa!${=f^E|xQWJOF=bPci|Zy$dQa+0@v-P3nHI$TB&Dd8kW z%^cx=O??2`80+inY)ENc-$K&C_|r~AjY`ZzjoQpXHdc)>1NvES1%HT)BDH?r`%@b& z*#WPO4fOq}%X(#3Q7h;CRPPh&L0nf04xpRG5k{(8e_Ga#iVJPij!S>Fxpf2%_BO}* z`<__I19j{Z#q1O{L#gULHfClf@!?PqCj%vK!&t2l<;E@q=s`m2JeYIweG-7)dK0aj z>!|V);XHwZhH^$}EX5NsaR4=1raO4-O>c3;*c55iH&39kI|MY|W*@zViQ{0Nx-UoCr-}CEz-vBNqKT+F{I;QsJo|KV)s*hy6Qe)FK|=B%>jVK1Zq&iFI+O*dz&-|?gHgLBMy zz=MDC%}u}4cQN0#fZOwR_IM|Mzw2AqyI)0(-Gdm_0KVBgu}}@xMP`JU)2z^uBh#taMrhzVWoTw?4cj7V!Q1 zooB#<+~ zqq<(gAM957=AWV>??XT;%9hu)oAej*eN~T{K#<;*Mi5g zx2FfkAd^%iXq<3uLrDqlIDU8|rl&`o;8RFQ3u&mrmwhkC#@WS~4l~x+-X7kf!mJXv z-@S&c)!M^*rVa7uyU4=>-6GJz-xm1aH@)8zV-z_x^XDgO5`?;Z{-DC;dWqH(MeOk1 z=}Map@&2Sp3L|`rHYse?62rV+Ut(6iE&Vy!bMSj&EW@v%o1<@Lk&Iv9e={72{ML!B z!G%&eMUb6}cfP?~qpw^c(pA#=+wxjfZ6@?Cw zdqoy!EueZenR!+F)#z@yb?{DVP#2}gy)>JL9El-Eqe)ssR3xUe^OplfskckV?;&2a zFO4YcVGH8^^dAB}X2B*kU%lUPqcRW%NgkM1Ss|m+phWXbbB9y2m^v#Fz&)4IT~l?= zY-(Ehh>uX!SBS7FOxoyI{jAKM!l%gq4+AkeCgxSN^e4`FN)8IWDvoagBTFPG6gim; zh#v>Sx%h95dHB@@EO=vk3pfeAneC_|2R9eQDU9RAt^G8H*L411=;VGAGbbW>wJ_eXCE z5u|Wj?P~9gU(#H$)ipNv^o%9c&hz5hp5a|;s$f*7K#x~MNd1mM8JlY-tGS+Zp?D-0 z=ux3vKUBf*`QRd@sGO0n#Uz6A^IkOdJYv13+zd{)7sNs)j8#F)?b3~T5wXHY<%}`H zK}XDi_DbjS=+~F9fF{(u zz4C41IC-P5)WpbMDat62VctVy=`}H-&g0WWpRY0Bm7`ZpNvt^dk_HMFLSc&T$3d8v z$0{9^viEI|kAxuszaCc$Rs#+n@fA*usHoqg)LGB%N16moz~3>0jlo>+l?x`c1*bkD z&tXeFkf7#d2NBEe^5!tvQ*=ZP6~bYiY`~37QX0cBdmXDlj-DZ7nx8VLQiV%x!Tk_Fp;Y%f8s0?ZJdjCez6t;2%g8V)R#q(456>1z6!Q_O zb2ZVrGGEuws520h;hGm*2S?JN5XDkTu?~n+*}!o3k{>na~7{azUdgZfP%pn{%Vt`WhuY7 z^%{-H53+A%Yop&D-10UqRv{s|y03Yo)ON520dwBr<+BXhipaZ6kzLq@4R0~yDmMB7 zea|KAheu2+(n;1P>_L)}({HX@JX+?UQiGJjg3v42A1RbHM6qMz?)!*Nk-Wu~o@4<^ zIppMCU)ZTd^_O-+;^RGabYv!Z_;2<<}9|ImOZY zdqbsw*7o0XAftQEf%>D%b`HM@Vfp-%=O?0SCb-53eKfD;-D8SAgfxy%u_G%M3d=Hu zZo#qMaV}>#-Mu7tz>Her*rQA4UGjh+ko`EDH=M5_kZ#R)8z((ifU0kkQw z+2{QvMfWn?=xoAOy*s;5*lUW;$d6}`N}4xwY@Q$OI(+S{yWrgAqqYp?nh?0 zw)If1=UkXVZJ$TG`(O?}!rP#k@E04+4PPr}!i|=}HyO9)m@-yBP|84usY;nNc*;8) z^+{eXGj}BJwQ(O_C`Eu1}e73U?J_PNf>iVll%*-D12?8n}5YTlqWldtPo%$x1ef=hQ$mq&~L zQYM-Il7H(PGd1$WRTuYMM{7hvi+hxqcM!(#DI^*=+ICbwAyo})VWIL5S86f{7_oAQ z<`{hF;ds}F3nA&`%RbZcb>Xd2%mucetc=@vt0TRz@48A7(|KiKbf#mLsa6|(;mJN4 zp9~tmdN4RcH8rBaDIlxa#pnOE6vabVlz%BVU1u?VG_e$z)tJOSHpfCzjBIRdzt{F5 zw!HDf_Y4BCkAMjcxqPAjcO~@qUib1DO^+Uk8gCkIlTiD59cbabUm9+_Qr>XiZBU-9 zuw#43w6GS_Zdu9KW>x%HpSZ=Sb>3{FI7l;Q-{(AYFgvG1YbjJYW4|e86IZnInypHS z-A|8RC(|dXQg9`^7wLkgw$72uFh$QRJOl0*VNw4L>j}pvzXvK==S_~h;+sq5Zihq? zrPDQuZe&+gmAj-BsFzpubH{$EyVFSgo%+~wcjZ@^LWO-qTJ`wPotEcDRqU*xa>nDI zZTY##ifr}LW5a*y+!wDljK5}cXyRQqeICW2L_2kgZ`saQ9A_}~op0*5ceu(o&8XFU zjIp1TA~G^|CFvQr&D(3sw+$y4)g^?Z&~e!Bq1THRHm~@??zY%xj4JDgvb7Uiho|TA zn=O0%C^eAZ7yFww$vm3PAN;mbNt98%XdsN5W?AY}c8Y@IQ8sZU%Z8WyRv;?&6PG?& zyVQI3N{dyYikg$`N6Dgs#j~wR`sn1Vg?g3sdtcYs)P9(hjsJ|&ccTZZ8G0#c_Gjr0hJ&?-e#^-(1as;i9ag+ufa@$VrTe26p)*B8fO!xK5aGWKWy!&t4 z^dhw7^;d9R-&v_aQq(CNcInD0ceTooQn|^?esFv(06uIR1ldJ*SX8tYfUx^(%b%>K zw4R?ja;|(`FLMVj4{_zN8c6c#Mw&-c^Y77ZaF7fzZvcYWysoCL(Eci zCnH+pS-Wh~$MtmX=c9dIvwD|cBbR&ishc^d(@PYh+Tp~>{=^eWswI!k!@Fke(RFQ8 zBdR7#=bN>yOpB`^s@+B4rfla^n(}%# z)?sC@e$ck~;zR4(6BO2IGu|Xo|)q+Pg_y+&+5pNP}1vF1A$Y0_e*< z$9K}nKsg=dyfNm{-$Yq@XDmwHKiLB{QlgNrylKBH3A0x-vXuzIUPxLYj1RYX4;N5R z(6UswCT{Ix8RxopEv zosO}uy~j&=94`zgn)MLd+ncUKPrct19*FRW^6+Wn`QVo;PAzx{d&V#$fBi%#HiA6Z zlq#W;v?a$jc&UaWnfE>Z*H}W_9__h*QsEjMZ(C%jsrL#!CZVzu;gspN&h6S}24_tO z&mpF?<2d^u}50 z%pC5R#%qOLsM7cFZMLG(SzFAq5QJUGxUNej2ww1ffofT}nh=sFhq%b=AoTKgs`N`p zyBECCXF-F7&%Lut`r2d^Hy)X^zIoujz1NaopmvX`*AvJJZDLW$*7wDLUdF&RXDbqfaD&wM=e zjeFB?rvqk1fB#I|uzJN3TTLA%K^diHQhi%}Jy(l4hXbiN+}i}wLq>#0jvA3WMS7l| zDgNP^*8VL&ElInqvasx{$(z2&9^_cApR&yZu715)FqapjPPouvfx*@ulJIm^Ioq1c z>T@|^V|zufO6;YW?*;zsIh9UXCzD1ASW>GqgS^2BfeX8Bns@K6-Hy*E(*~iOGKnD`e$LTSzEDe=Bxc0t#=#9~gs$W2#qRSVo^N$dDh12ZauJz_ergtYo8VKB zn*Anj6Qx4*_XN2%-GljfPw0}`o0PtY`Cs-}G!5MXYkAxX*%td43uFFfBYU>z{NU0g ztZ6V|T5}{}rTW0Rl)kV&I=pheoY$;pTbAo;zK93E3Y{y?qJEm7Gu!i&JR1t!)3PtG zyq7byzZ`bXvIZwe?)+rh&KPu{TAPT#AHT(J!LZQsAw)!u4cI;&bU+kJ+hO==^d%I; za&7m9FG7012rf)E?sKf>(P7tR&U2ydjDX$tL>q3Q%Y2oiu4mUH(^)_GTI?dNElT@( zvJ_hB27{r|i<=0(IG$4iK)zO$ z?^`g&LXmlSsgW^koRbovXu;vN)G*^f1?xQOLCmZYX6lx71@J8`QDg3esV!8D=XcI_ zqeTT^;QEY7SZHGRm~HUl8>`vLnj^_Al@n?5go{B8B6LL=_VUg*gRXmZTg49p+C}#= z0w}L>5dzOX(zqJAyQZEtq&7@?HBDz`1JIECpl2&Nn)f?Y9BN%Ar zq~a0JSoU&(fY&5N#UPJ}=iz*@oz??q*dn)6a@khim|Cs19JUa0V#D~9y4c|${9b3lO}RJUsaAVnZ8*ZXtl#~+21BL#I3PUPC|OuJnaAp z^1&I6FFR~$3$)rKOIMd~;&1Yr+nWeFRnN-|zV5j@3vN<*R1a}}?Si}fINg+nfSxg| z-x=++gun4hPR7PQB&A^d5lzORniqhsBvf}V?zxlARe`F?Wp{i`+$3k@sRd<|k&bCUdfn=SA zcMe|at$;00oA`ciJaXT`K+LD984`iUaK_+m-*dhibWQoXUs?0f3uo=Citd_%Yvon? z8kA`rUOOUL+TC>-U;VLHTpKQuesN0gJEPB7 z>PR~nuVnR{F=>P6vhc_k($ehpsoberf4yLdVNO#goBWy2PcZglCb=8M0tyz6ydcc? z7xna`@Ng7Al8g_1%&nREZPaqdm8INmmT64kwbRxNUmoq}BNNvQ*(LG#)VvLFDcW=! z3^YO=@_vW^cJsoc2P4?VQq%f4jfiU6E%=uV14S}_q;Pfn__)`Kg_}y0b4%#~M`W#5 zT5`;LPxPvCm*&!6`>0X8SBb~|H+PJ5!OJ6!zvwyMJ%?%2fr??v*Nds&j5Jz7M!Hg9 zEbu-eF2$=IpauzOa2L!DmZ6n<-cv$o)2C{m+G6(0DDLsnl8=xes#N znm=yXB4@&iOWCz!h=k-9(tTuQtJaKjG|v0jSs*E+s2;GEF+jmGE5ROyvNh+Yp`oHz zZ~xm=Nk(iwXVYj*9ZWsGeGqEZ2IECiTo6sM+Uj@aex>fz4y3^2BRtH1BnK2`)4S&_ zEDTH%@G2J2rM7S~R|PpagW1iUK$cHg5u2p!)Wv}o_NbWRqX7TRL5q$!3w54R{qKuGVB>8d4#tFU(Z3Wjdp6U!=%ZY zQd?gU^;uwMzm77Pdrg2eKBntj+#wf~=SK%kD|_J}`xT$L_}lTbaEy(vZ&BvR%Smt9 zW_+L5eWsy)3kw=doI)g6#7-nhP|r+o-yFiDcozDSX=0c0fYbYu>f4a9^1BpN>jGz| z^^yyQ>?NT}mA>W*pNpLOuv^04p5DGT`mrgF{)#Y+AVw}tT{wq$|Fa8pxE68#wRZ-b zS?$YP=Nn&ce)2%=HuI0mUbZ?9nWdpSSR(0fwd_vVenjU&p|vuUlH2lge&qO^_~moe zBEPANSXQkBFNvki?Y!_k^CMbW*9rbUd5`ImHF{#fe1hSN_&`S`IA5EkY%+IQbAkm_ z(9$cBNiiSI`~N%$7>EA$gFxUQz9GQC(EoM0z!_xY=xPbluyl3(16Kcd&dFwG;|SFl zR_qqX5wYn-V*8neioanbUyp3qM`Kp!U`9rz&QKl4^}=O*+`ROkduyFI}~IL%jq1dx9_X1-kq2X4OY7f~F+?WIj<19BqUq zn@%QhU-2+k|EDJ3nG^+4{KH zb#g55R=Hmlm^hQkzba5of4wciLf3pOv1FjkzFvMb#owgyY`|vjGKRys;;;cLl&wry zd5)URLCp1a-ox_o3CjPK*Gy!iKQGIe%-N~Wx2Wv9ibWQxl50X~Kw zMVX-W$|f8`w;D!aikp6_T2-G*dPe^8fPS&+piJnnkG;)XT7CaWQ%4~>sLUf>mrXq} zhg-DqC@u2i@5a(fDoVtUQd^`}8!tcXniZbp zBE~uLD;nd>tcCC$^9ZKS+ITbn>e&U2RDM!WMT;s;ebjk%Rk)eg{jel0QhtnNr7(AT*CgbH2*4eL-9qC4j z@O8}HtwXBcNrk_SVmN8VE)5@HRh3}jaA1IAgh2)#l+e(E@fK&`?8*R{@N~WIKY#S4 zoPb<(bT!uWa<&8;J{{&i9Kh6TaZsNFR<8jlMg0%!T)?d{$kHCn&i3^CABNEB^s4uS|2X*nhxG5q^A9io zr5gU_rL*sUXo!E9{}artisan('categories:reset', ['user' => $user->id]) ->expectsOutput("Resetting categories for user: {$user->name} ({$user->email})") ->expectsOutput('Deleted 5 existing categories.') - ->expectsOutput('Created 49 default categories.') + ->expectsOutput('Created 63 default categories.') ->expectsOutput('✓ Categories reset successfully!') ->assertSuccessful(); - expect($user->fresh()->categories()->count())->toBe(49); + expect($user->fresh()->categories()->count())->toBe(63); $categoryNames = $user->categories->pluck('name')->toArray(); expect($categoryNames)->toContain('Food', 'Transportation', 'Salary', 'Insurance'); @@ -31,11 +31,11 @@ test('command resets categories for a user by email', function () { $this->artisan('categories:reset', ['user' => 'test@example.com']) ->expectsOutput("Resetting categories for user: {$user->name} (test@example.com)") ->expectsOutput('Deleted 3 existing categories.') - ->expectsOutput('Created 49 default categories.') + ->expectsOutput('Created 63 default categories.') ->expectsOutput('✓ Categories reset successfully!') ->assertSuccessful(); - expect($user->fresh()->categories()->count())->toBe(49); + expect($user->fresh()->categories()->count())->toBe(63); }); test('command works when user has no existing categories', function () { @@ -46,11 +46,11 @@ test('command works when user has no existing categories', function () { $this->artisan('categories:reset', ['user' => $user->id]) ->expectsOutput("Resetting categories for user: {$user->name} ({$user->email})") ->expectsOutput('No existing categories found.') - ->expectsOutput('Created 49 default categories.') + ->expectsOutput('Created 63 default categories.') ->expectsOutput('✓ Categories reset successfully!') ->assertSuccessful(); - expect($user->fresh()->categories()->count())->toBe(49); + expect($user->fresh()->categories()->count())->toBe(63); }); test('command fails when user is not found by ID', function () { diff --git a/tests/Feature/Settings/CategoryTest.php b/tests/Feature/Settings/CategoryTest.php index e8d24e74..c151d757 100644 --- a/tests/Feature/Settings/CategoryTest.php +++ b/tests/Feature/Settings/CategoryTest.php @@ -183,7 +183,7 @@ test('default categories are created when user registers', function () { $service = new \App\Actions\CreateDefaultCategories; $service->handle($user); - expect($user->categories()->count())->toBe(49); + expect($user->categories()->count())->toBe(63); $categoryNames = $user->categories->pluck('name')->toArray(); expect($categoryNames)->toContain('Food', 'Transportation', 'Salary', 'Insurance');