diff --git a/src/app/Observers/UserObserver.php b/src/app/Observers/UserObserver.php index 61762d99..a81035f8 100644 --- a/src/app/Observers/UserObserver.php +++ b/src/app/Observers/UserObserver.php @@ -1,97 +1,97 @@ {$user->getKeyName()} = $allegedly_unique; break; } } // can't dispatch job here because it'll fail serialization } /** * Handle the "created" event. * * Ensures the user has at least one wallet. * * Should ensure some basic settings are available as well. * * @param \App\User $user The user created. * * @return void */ public function created(User $user) { // FIXME: Actual proper settings $user->setSettings( [ 'country' => 'CH', 'currency' => 'CHF', 'first_name' => '', 'last_name' => '', 'billing_address' => '', 'organization' => '' ] ); $user->wallets()->create(); \App\Jobs\ProcessUserCreate::dispatch($user); } /** * Handle the "deleting" event. * * @param User $user The user that is being deleted. * * @return void */ public function deleting(User $user) { - \App\Jobs\ProcessUserDelete::dispatch($user); + // TODO \App\Jobs\ProcessUserDelete::dispatch($user); } /** * Handle the "retrieving" event. * * @param User $user The user that is being retrieved. * * @todo This is useful for audit. * * @return void */ public function retrieving(User $user) { - \App\Jobs\ProcessUserRead::dispatch($user); + // TODO \App\Jobs\ProcessUserRead::dispatch($user); } /** * Handle the "updating" event. * * @param User $user The user that is being updated. * * @return void */ public function updating(User $user) { \App\Jobs\ProcessUserUpdate::dispatch($user); } } diff --git a/src/app/Wallet.php b/src/app/Wallet.php index dae8e63d..0c92d268 100644 --- a/src/app/Wallet.php +++ b/src/app/Wallet.php @@ -1,140 +1,141 @@ 0.00, 'currency' => 'CHF' ]; protected $fillable = [ 'currency' ]; protected $nullable = [ 'description' ]; protected $casts = [ 'balance' => 'float', ]; protected $guarded = ['balance']; /** * Add a controller to this wallet. * - * @param User $user The user to add as a controller to this wallet. + * @param \App\User $user The user to add as a controller to this wallet. * * @return void */ - public function addController($user) + public function addController(User $user) { if (!$this->controllers()->get()->contains($user)) { - return $this->controllers()->save($user); + $this->controllers()->save($user); } } /** * Remove a controller from this wallet. * - * @param User $user The user to remove as a controller from this wallet. + * @param \App\User $user The user to remove as a controller from this wallet. * * @return void */ - public function removeController($user) + public function removeController(User $user) { if ($this->controllers()->get()->contains($user)) { - return $this->controllers()->detach($user); + $this->controllers()->detach($user); } } /** * Add an amount of pecunia to this wallet's balance. * * @param float $amount The amount of pecunia to add. * * @return Wallet */ public function credit(float $amount) { $this->balance += $amount; $this->save(); return $this; } /** * Deduct an amount of pecunia from this wallet's balance. * * @param float $amount The amount of pecunia to deduct. * * @return Wallet */ public function debit(float $amount) { $this->balance -= $amount; $this->save(); return $this; } /** * Controllers of this wallet. * - * @return User[] + * @return \App\User[] */ public function controllers() { return $this->belongsToMany( 'App\User', // The foreign object definition 'user_accounts', // The table name 'wallet_id', // The local foreign key 'user_id' // The remote foreign key ); } /** * Entitlements billed to this wallet. * * @return Entitlement[] */ public function entitlements() { return $this->hasMany('App\Entitlement'); } /** * The owner of the wallet -- the wallet is in his/her back pocket. * * @return User */ public function owner() { return $this->belongsTo('App\User', 'user_id', 'id'); } } diff --git a/src/database/migrations/2019_09_17_102628_create_sku_entitlements.php b/src/database/migrations/2019_09_17_102628_create_sku_entitlements.php index 19dd91da..8ed69427 100644 --- a/src/database/migrations/2019_09_17_102628_create_sku_entitlements.php +++ b/src/database/migrations/2019_09_17_102628_create_sku_entitlements.php @@ -1,62 +1,62 @@ string('id', 36)->primary(); $table->string('title', 64); $table->text('description')->nullable(); $table->integer('cost'); $table->smallinteger('units_free')->default('0'); $table->string('period', strlen('monthly'))->default('monthly'); $table->string('handler_class')->nullable(); $table->boolean('active')->default(false); $table->timestamps(); } ); Schema::create( 'entitlements', function (Blueprint $table) { $table->string('id', 36)->primary(); $table->bigInteger('owner_id'); $table->bigInteger('entitleable_id'); - $table->bigInteger('entitleable_type'); + $table->string('entitleable_type'); $table->string('wallet_id', 36); $table->string('sku_id', 36); $table->string('description')->nullable(); $table->timestamps(); $table->foreign('sku_id')->references('id')->on('skus')->onDelete('cascade'); $table->foreign('owner_id')->references('id')->on('users')->onDelete('cascade'); $table->foreign('wallet_id')->references('id')->on('wallets')->onDelete('cascade'); } ); } /** * Reverse the migrations. * * @return void */ public function down() { // TODO: drop foreign keys first Schema::dropIfExists('entitlements'); Schema::dropIfExists('skus'); } } diff --git a/src/database/migrations/2019_12_10_100355_create_package_skus_table.php b/src/database/migrations/2019_12_10_100355_create_package_skus_table.php index f7bf943e..2e8b60a3 100644 --- a/src/database/migrations/2019_12_10_100355_create_package_skus_table.php +++ b/src/database/migrations/2019_12_10_100355_create_package_skus_table.php @@ -1,43 +1,42 @@ bigIncrements('id'); $table->string('package_id', 36); $table->string('sku_id', 36); - $table->integer('qty')->default(1); $table->foreign('package_id')->references('id')->on('packages') ->onDelete('cascade')->onUpdate('cascade'); $table->foreign('sku_id')->references('id')->on('skus') ->onDelete('cascade')->onUpdate('cascade'); } ); } /** * Reverse the migrations. * * @return void */ public function down() { Schema::dropIfExists('package_skus'); } } diff --git a/src/tests/Feature/Controller/PasswordResetTest.php b/src/tests/Feature/Controller/PasswordResetTest.php index a42d9068..7e3e371e 100644 --- a/src/tests/Feature/Controller/PasswordResetTest.php +++ b/src/tests/Feature/Controller/PasswordResetTest.php @@ -1,326 +1,328 @@ 'passwordresettest@' . \config('app.domain')]); + $user = $this->getTestUser('passwordresettest@' . \config('app.domain')); } /** * {@inheritDoc} - * - * @return void */ public function tearDown(): void { User::where('email', 'passwordresettest@' . \config('app.domain')) ->delete(); } /** * Test password-reset/init with invalid input - * - * @return void */ - public function testPasswordResetInitInvalidInput() + public function testPasswordResetInitInvalidInput(): void { // Empty input data $data = []; $response = $this->post('/api/auth/password-reset/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); // Data with invalid email $data = [ 'email' => '@example.org', ]; $response = $this->post('/api/auth/password-reset/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); // Data with valid but non-existing email $data = [ 'email' => 'non-existing-password-reset@example.org', ]; $response = $this->post('/api/auth/password-reset/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); // Data with valid email af an existing user with no external email $data = [ 'email' => 'passwordresettest@' . \config('app.domain'), ]; $response = $this->post('/api/auth/password-reset/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); } /** * Test password-reset/init with valid input * * @return array */ public function testPasswordResetInitValidInput() { Queue::fake(); // Assert that no jobs were pushed... Queue::assertNothingPushed(); // Add required external email address to user settings $user = User::where('email', 'passwordresettest@' . \config('app.domain'))->first(); $user->setSetting('external_email', 'ext@email.com'); $data = [ 'email' => 'passwordresettest@' . \config('app.domain'), ]; $response = $this->post('/api/auth/password-reset/init', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(2, $json); $this->assertSame('success', $json['status']); $this->assertNotEmpty($json['code']); // Assert the email sending job was pushed once Queue::assertPushed(\App\Jobs\PasswordResetEmail::class, 1); // Assert the job has proper data assigned Queue::assertPushed(\App\Jobs\PasswordResetEmail::class, function ($job) use ($user, &$code, $json) { - // Access protected property - $reflection = new \ReflectionClass($job); - $code = $reflection->getProperty('code'); - $code->setAccessible(true); - $code = $code->getValue($job); + $code = TestCase::getObjectProperty($job, 'code'); return $code->user->id === $user->id && $code->code == $json['code']; }); return [ 'code' => $code ]; } /** * Test password-reset/verify with invalid input * * @return void */ public function testPasswordResetVerifyInvalidInput() { // Empty data $data = []; $response = $this->post('/api/auth/password-reset/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Add verification code and required external email address to user settings $user = User::where('email', 'passwordresettest@' . \config('app.domain'))->first(); $code = new VerificationCode(['mode' => 'password-reset']); $user->verificationcodes()->save($code); // Data with existing code but missing short_code $data = [ 'code' => $code->code, ]; $response = $this->post('/api/auth/password-reset/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Data with invalid code $data = [ 'short_code' => '123456789', 'code' => $code->code, ]; $response = $this->post('/api/auth/password-reset/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // TODO: Test expired code } /** * Test password-reset/verify with valid input * * @return void */ public function testPasswordResetVerifyValidInput() { // Add verification code and required external email address to user settings $user = User::where('email', 'passwordresettest@' . \config('app.domain'))->first(); $code = new VerificationCode(['mode' => 'password-reset']); $user->verificationcodes()->save($code); // Data with invalid code $data = [ 'short_code' => $code->short_code, 'code' => $code->code, ]; $response = $this->post('/api/auth/password-reset/verify', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(1, $json); $this->assertSame('success', $json['status']); } /** * Test password-reset with invalid input * * @return void */ public function testPasswordResetInvalidInput() { // Empty data $data = []; $response = $this->post('/api/auth/password-reset', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('password', $json['errors']); $user = User::where('email', 'passwordresettest@' . \config('app.domain'))->first(); $code = new VerificationCode(['mode' => 'password-reset']); $user->verificationcodes()->save($code); // Data with existing code but missing password $data = [ 'code' => $code->code, ]; $response = $this->post('/api/auth/password-reset', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('password', $json['errors']); // Data with existing code but wrong password confirmation $data = [ 'code' => $code->code, 'short_code' => $code->short_code, 'password' => 'password', 'password_confirmation' => 'passwrong', ]; $response = $this->post('/api/auth/password-reset', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('password', $json['errors']); // Data with invalid short code $data = [ 'code' => $code->code, 'short_code' => '123456789', 'password' => 'password', 'password_confirmation' => 'password', ]; $response = $this->post('/api/auth/password-reset', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); } /** * Test password reset with valid input * * @return void */ public function testPasswordResetValidInput() { $user = User::where('email', 'passwordresettest@' . \config('app.domain'))->first(); $code = new VerificationCode(['mode' => 'password-reset']); $user->verificationcodes()->save($code); + Queue::fake(); + Queue::assertNothingPushed(); + $data = [ 'password' => 'test', 'password_confirmation' => 'test', 'code' => $code->code, 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/password-reset', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(4, $json); $this->assertSame('success', $json['status']); $this->assertSame('bearer', $json['token_type']); $this->assertTrue(!empty($json['expires_in']) && is_int($json['expires_in']) && $json['expires_in'] > 0); $this->assertNotEmpty($json['access_token']); + Queue::assertPushed(\App\Jobs\ProcessUserUpdate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessUserUpdate::class, function ($job) use ($user) { + $job_user = TestCase::getObjectProperty($job, 'user'); + + return $job_user->id === $user->id + && $job_user->email === $user->email + && $job_user->password_ldap != $user->password_ldap; + }); + // Check if the code has been removed $this->assertNull(VerificationCode::find($code->code)); // TODO: Check password before and after (?) // TODO: Check if the access token works } } diff --git a/src/tests/Feature/Controller/SignupTest.php b/src/tests/Feature/Controller/SignupTest.php index 14de0d90..cc78c2f2 100644 --- a/src/tests/Feature/Controller/SignupTest.php +++ b/src/tests/Feature/Controller/SignupTest.php @@ -1,627 +1,635 @@ "SignupControllerTest1@$domain"]); + $user = $this->getTestUser("SignupControllerTest1@$domain"); } /** * {@inheritDoc} - * - * @return void */ public function tearDown(): void { $domain = self::getPublicDomain(); User::where('email', "signuplogin@$domain") ->orWhere('email', "SignupControllerTest1@$domain") ->orWhere('email', 'admin@external.com') ->delete(); Domain::where('namespace', 'signup-domain.com') ->orWhere('namespace', 'external.com') ->delete(); } /** * Return a public domain for signup tests */ public function getPublicDomain(): string { if (!self::$domain) { $this->refreshApplication(); $public_domains = Domain::getPublicDomains(); self::$domain = reset($public_domains); if (empty(self::$domain)) { self::$domain = 'signup-domain.com'; Domain::create([ 'namespace' => self::$domain, 'status' => Domain::STATUS_ACTIVE, 'type' => Domain::TYPE_PUBLIC, ]); } } return self::$domain; } /** * Test fetching plans for signup * * @return void */ public function testSignupPlans() { // Note: this uses plans that already have been seeded into the DB $response = $this->get('/api/auth/signup/plans'); $json = $response->json(); $response->assertStatus(200); $this->assertSame('success', $json['status']); $this->assertCount(2, $json['plans']); $this->assertArrayHasKey('title', $json['plans'][0]); $this->assertArrayHasKey('name', $json['plans'][0]); $this->assertArrayHasKey('description', $json['plans'][0]); $this->assertArrayHasKey('button', $json['plans'][0]); } /** * Test signup initialization with invalid input * * @return void */ public function testSignupInitInvalidInput() { // Empty input data $data = []; $response = $this->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); $this->assertArrayHasKey('name', $json['errors']); // Data with missing name $data = [ 'email' => 'UsersApiControllerTest1@UsersApiControllerTest.com', ]; $response = $this->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('name', $json['errors']); // Data with invalid email (but not phone number) $data = [ 'email' => '@example.org', 'name' => 'Signup User', ]; $response = $this->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('email', $json['errors']); // TODO: Test phone validation } /** * Test signup initialization with valid input * * @return array */ public function testSignupInitValidInput() { Queue::fake(); // Assert that no jobs were pushed... Queue::assertNothingPushed(); $data = [ 'email' => 'testuser@external.com', 'name' => 'Signup User', 'plan' => 'individual', ]; $response = $this->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(2, $json); $this->assertSame('success', $json['status']); $this->assertNotEmpty($json['code']); // Assert the email sending job was pushed once Queue::assertPushed(\App\Jobs\SignupVerificationEmail::class, 1); // Assert the job has proper data assigned Queue::assertPushed(\App\Jobs\SignupVerificationEmail::class, function ($job) use ($data, $json) { - // Access protected property - $reflection = new \ReflectionClass($job); - $code = $reflection->getProperty('code'); - $code->setAccessible(true); - $code = $code->getValue($job); + $code = TestCase::getObjectProperty($job, 'code'); return $code->code === $json['code'] && $code->data['plan'] === $data['plan'] && $code->data['email'] === $data['email'] && $code->data['name'] === $data['name']; }); return [ 'code' => $json['code'], 'email' => $data['email'], 'name' => $data['name'], 'plan' => $data['plan'], ]; } /** * Test signup code verification with invalid input * * @depends testSignupInitValidInput * @return void */ public function testSignupVerifyInvalidInput(array $result) { // Empty data $data = []; $response = $this->post('/api/auth/signup/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertArrayHasKey('code', $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Data with existing code but missing short_code $data = [ 'code' => $result['code'], ]; $response = $this->post('/api/auth/signup/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Data with invalid short_code $data = [ 'code' => $result['code'], 'short_code' => 'XXXX', ]; $response = $this->post('/api/auth/signup/verify', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // TODO: Test expired code } /** * Test signup code verification with valid input * * @depends testSignupInitValidInput * * @return array */ public function testSignupVerifyValidInput(array $result) { $code = SignupCode::find($result['code']); $data = [ 'code' => $code->code, 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/signup/verify', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(5, $json); $this->assertSame('success', $json['status']); $this->assertSame($result['email'], $json['email']); $this->assertSame($result['name'], $json['name']); $this->assertSame(false, $json['is_domain']); $this->assertTrue(is_array($json['domains']) && !empty($json['domains'])); return $result; } /** * Test last signup step with invalid input * * @depends testSignupVerifyValidInput * @return void */ public function testSignupInvalidInput(array $result) { // Empty data $data = []; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(3, $json['errors']); $this->assertArrayHasKey('login', $json['errors']); $this->assertArrayHasKey('password', $json['errors']); $this->assertArrayHasKey('domain', $json['errors']); $domain = $this->getPublicDomain(); // Passwords do not match and missing domain $data = [ 'login' => 'test', 'password' => 'test', 'password_confirmation' => 'test2', ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertArrayHasKey('password', $json['errors']); $this->assertArrayHasKey('domain', $json['errors']); $domain = $this->getPublicDomain(); // Login too short $data = [ 'login' => '1', 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('login', $json['errors']); // Missing codes $data = [ 'login' => 'login-valid', 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(2, $json['errors']); $this->assertArrayHasKey('code', $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Data with invalid short_code $data = [ 'login' => 'TestLogin', 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', 'code' => $result['code'], 'short_code' => 'XXXX', ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('short_code', $json['errors']); // Valid code, invalid login $code = SignupCode::find($result['code']); $data = [ 'login' => 'żżżżżż', 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', 'code' => $result['code'], 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(422); $this->assertSame('error', $json['status']); $this->assertCount(1, $json['errors']); $this->assertArrayHasKey('login', $json['errors']); } /** * Test last signup step with valid input (user creation) * * @depends testSignupVerifyValidInput * @return void */ public function testSignupValidInput(array $result) { + Queue::fake(); + $domain = $this->getPublicDomain(); $identity = \strtolower('SignupLogin@') . $domain; - $code = SignupCode::find($result['code']); $data = [ 'login' => 'SignupLogin', 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', 'code' => $code->code, 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/signup', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(4, $json); $this->assertSame('success', $json['status']); $this->assertSame('bearer', $json['token_type']); $this->assertTrue(!empty($json['expires_in']) && is_int($json['expires_in']) && $json['expires_in'] > 0); $this->assertNotEmpty($json['access_token']); + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, function ($job) use ($data) { + $job_user = TestCase::getObjectProperty($job, 'user'); + + return $job_user->email === \strtolower($data['login'] . '@' . $data['domain']); + }); + // Check if the code has been removed $this->assertNull(SignupCode::where($result['code'])->first()); // Check if the user has been created $user = User::where('email', $identity)->first(); $this->assertNotEmpty($user); $this->assertSame($identity, $user->email); $this->assertSame($result['name'], $user->name); // Check external email in user settings $this->assertSame($result['email'], $user->getSetting('external_email')); // TODO: Check SKUs/Plan // TODO: Check if the access token works } /** * Test signup for a group (custom domain) account * * @return void */ public function testSignupGroupAccount() { Queue::fake(); // Initial signup request $user_data = $data = [ 'email' => 'testuser@external.com', 'name' => 'Signup User', 'plan' => 'group', ]; $response = $this->post('/api/auth/signup/init', $data); $json = $response->json(); $response->assertStatus(200); $this->assertCount(2, $json); $this->assertSame('success', $json['status']); $this->assertNotEmpty($json['code']); // Assert the email sending job was pushed once Queue::assertPushed(\App\Jobs\SignupVerificationEmail::class, 1); // Assert the job has proper data assigned Queue::assertPushed(\App\Jobs\SignupVerificationEmail::class, function ($job) use ($data, $json) { - // Access protected property - $reflection = new \ReflectionClass($job); - $code = $reflection->getProperty('code'); - $code->setAccessible(true); - $code = $code->getValue($job); + $code = TestCase::getObjectProperty($job, 'code'); return $code->code === $json['code'] && $code->data['plan'] === $data['plan'] && $code->data['email'] === $data['email'] && $code->data['name'] === $data['name']; }); // Verify the code $code = SignupCode::find($json['code']); $data = [ 'code' => $code->code, 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/signup/verify', $data); $result = $response->json(); $response->assertStatus(200); $this->assertCount(5, $result); $this->assertSame('success', $result['status']); $this->assertSame($user_data['email'], $result['email']); $this->assertSame($user_data['name'], $result['name']); $this->assertSame(true, $result['is_domain']); $this->assertSame([], $result['domains']); // Final signup request $login = 'admin'; $domain = 'external.com'; $data = [ 'login' => $login, 'domain' => $domain, 'password' => 'test', 'password_confirmation' => 'test', 'code' => $code->code, 'short_code' => $code->short_code, ]; $response = $this->post('/api/auth/signup', $data); $result = $response->json(); $response->assertStatus(200); $this->assertCount(4, $result); $this->assertSame('success', $result['status']); $this->assertSame('bearer', $result['token_type']); $this->assertTrue(!empty($result['expires_in']) && is_int($result['expires_in']) && $result['expires_in'] > 0); $this->assertNotEmpty($result['access_token']); + Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, function ($job) use ($domain) { + $job_domain = TestCase::getObjectProperty($job, 'domain'); + + return $job_domain->namespace === $domain; + }); + + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, function ($job) use ($data) { + $job_user = TestCase::getObjectProperty($job, 'user'); + + return $job_user->email === $data['login'] . '@' . $data['domain']; + }); + // Check if the code has been removed $this->assertNull(SignupCode::find($code->id)); // Check if the user has been created $user = User::where('email', $login . '@' . $domain)->first(); $this->assertNotEmpty($user); $this->assertSame($user_data['name'], $user->name); // Check domain record // Check external email in user settings $this->assertSame($user_data['email'], $user->getSetting('external_email')); // TODO: Check SKUs/Plan // TODO: Check if the access token works } - /** * List of email address validation cases for testValidateEmail() * * @return array Arguments for testValidateEmail() */ public function dataValidateEmail() { return [ // invalid ['', 'validation.emailinvalid'], ['example.org', 'validation.emailinvalid'], ['@example.org', 'validation.emailinvalid'], ['test@localhost', 'validation.emailinvalid'], // valid ['test@domain.tld', null], ['&@example.org', null], ]; } /** * Signup email validation. * * Note: Technicly these are unit tests, but let's keep it here for now. * FIXME: Shall we do a http request for each case? * * @dataProvider dataValidateEmail */ public function testValidateEmail($email, $expected_result) { $method = new \ReflectionMethod('App\Http\Controllers\API\SignupController', 'validateEmail'); $method->setAccessible(true); $result = $method->invoke(new SignupController(), $email); $this->assertSame($expected_result, $result); } /** * List of login/domain validation cases for testValidateLogin() * * @return array Arguments for testValidateLogin() */ public function dataValidateLogin() { $domain = $this->getPublicDomain(); return [ // Individual account ['', $domain, false, ['login' => 'validation.logininvalid']], ['test123456', 'localhost', false, ['domain' => 'validation.domaininvalid']], ['test123456', 'unknown-domain.org', false, ['domain' => 'validation.domaininvalid']], ['test.test', $domain, false, null], ['test_test', $domain, false, null], ['test-test', $domain, false, null], ['admin', $domain, false, ['login' => 'validation.loginexists']], ['administrator', $domain, false, ['login' => 'validation.loginexists']], ['sales', $domain, false, ['login' => 'validation.loginexists']], ['root', $domain, false, ['login' => 'validation.loginexists']], // existing user ['SignupControllerTest1', $domain, false, ['login' => 'validation.loginexists']], // Domain account ['admin', 'kolabsys.com', true, null], ['testnonsystemdomain', 'invalid', true, ['domain' => 'validation.domaininvalid']], ['testnonsystemdomain', '.com', true, ['domain' => 'validation.domaininvalid']], // existing user ['SignupControllerTest1', $domain, true, ['domain' => 'validation.domainexists']], ]; } /** * Signup login/domain validation. * * Note: Technicly these include unit tests, but let's keep it here for now. * FIXME: Shall we do a http request for each case? * * @dataProvider dataValidateLogin */ public function testValidateLogin($login, $domain, $external, $expected_result) { $method = new \ReflectionMethod('App\Http\Controllers\API\SignupController', 'validateLogin'); $method->setAccessible(true); $result = $method->invoke(new SignupController(), $login, $domain, $external); $this->assertSame($expected_result, $result); } } diff --git a/src/tests/Feature/Controller/UsersTest.php b/src/tests/Feature/Controller/UsersTest.php index 0bfa5c55..ffe850f8 100644 --- a/src/tests/Feature/Controller/UsersTest.php +++ b/src/tests/Feature/Controller/UsersTest.php @@ -1,77 +1,53 @@ 'UsersControllerTest1@UsersControllerTest.com' - ] - ); - - $user->delete(); + $user = User::where('email', 'UsersControllerTest1@UsersControllerTest.com')->delete(); } /** * {@inheritDoc} - * - * @return void */ public function tearDown(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UsersControllerTest1@UsersControllerTest.com' - ] - ); - - $user->delete(); + $user = User::where('email', 'UsersControllerTest1@UsersControllerTest.com')->delete(); } - public function testListUsers() + public function testListUsers(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UsersControllerTest1@UsersControllerTest.com' - ] - ); + $user = $this->getTestUser('UsersControllerTest1@UsersControllerTest.com'); $response = $this->actingAs($user)->get("api/v4/users"); $response->assertJsonCount(1); $response->assertStatus(200); } public function testUserEntitlements() { - $userA = User::firstOrCreate( - [ - 'email' => 'UserEntitlement2A@UserEntitlement.com' - ] - ); + $userA = $this->getTestUser('UserEntitlement2A@UserEntitlement.com'); $response = $this->actingAs($userA, 'api')->get("/api/v4/users/{$userA->id}"); $response->assertStatus(200); $response->assertJson(['id' => $userA->id]); $user = factory(User::class)->create(); $response = $this->actingAs($user)->get("/api/v4/users/{$userA->id}"); $response->assertStatus(404); } } diff --git a/src/tests/Feature/DomainTest.php b/src/tests/Feature/DomainTest.php index 5cc5892f..3a204459 100644 --- a/src/tests/Feature/DomainTest.php +++ b/src/tests/Feature/DomainTest.php @@ -1,49 +1,62 @@ delete(); } /** * Tests getPublicDomains() method */ public function testGetPublicDomains(): void { $public_domains = Domain::getPublicDomains(); $this->assertNotContains('public-active.com', $public_domains); - Domain::create([ + // Fake the queue, assert that no jobs were pushed... + Queue::fake(); + Queue::assertNothingPushed(); + + $domain = Domain::create([ 'namespace' => 'public-active.com', 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_PUBLIC, ]); // Public but non-active domain should not be returned $public_domains = Domain::getPublicDomains(); $this->assertNotContains('public-active.com', $public_domains); + Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessDomainCreate::class, function ($job) use ($domain) { + $job_domain = TestCase::getObjectProperty($job, 'domain'); + + return $job_domain->id === $domain->id + && $job_domain->namespace === $domain->namespace; + }); + $domain = Domain::where('namespace', 'public-active.com')->first(); $domain->status = Domain::STATUS_ACTIVE; $domain->save(); // Public and active domain should be returned $public_domains = Domain::getPublicDomains(); $this->assertContains('public-active.com', $public_domains); } } diff --git a/src/tests/Feature/EntitlementTest.php b/src/tests/Feature/EntitlementTest.php index c4e602ec..33688e2e 100644 --- a/src/tests/Feature/EntitlementTest.php +++ b/src/tests/Feature/EntitlementTest.php @@ -1,92 +1,89 @@ orWhere('email', 'entitled-user@custom-domain.com') ->delete(); } /** * Tests for User::AddEntitlement() */ public function testUserAddEntitlement(): void { $sku_domain = Sku::firstOrCreate(['title' => 'domain']); $sku_mailbox = Sku::firstOrCreate(['title' => 'mailbox']); - $owner = User::firstOrCreate(['email' => 'entitlement-test@kolabnow.com']); - $user = User::firstOrCreate(['email' => 'entitled-user@custom-domain.com']); - - $this->assertTrue($owner->id != $user->id); - - $wallets = $owner->wallets()->get(); - - $domain = Domain::firstOrCreate( - [ - 'namespace' => 'custom-domain.com' - ], + $owner = $this->getTestUser('entitlement-test@kolabnow.com'); + $user = $this->getTestUser('entitled-user@custom-domain.com'); + $domain = $this->getTestDomain( + 'custom-domain.com', [ 'status' => Domain::STATUS_NEW, 'type' => Domain::TYPE_EXTERNAL, ] ); + $this->assertTrue($owner->id != $user->id); + + $wallets = $owner->wallets()->get(); + $entitlement_own_mailbox = new Entitlement( [ 'owner_id' => $owner->id, 'entitleable_id' => $owner->id, 'entitleable_type' => User::class, 'wallet_id' => $wallets[0]->id, 'sku_id' => $sku_mailbox->id, 'description' => "Owner Mailbox Entitlement Test" ] ); $entitlement_domain = new Entitlement( [ 'owner_id' => $owner->id, 'entitleable_id' => $domain->id, 'entitleable_type' => Domain::class, 'wallet_id' => $wallets[0]->id, 'sku_id' => $sku_domain->id, 'description' => "User Domain Entitlement Test" ] ); $entitlement_mailbox = new Entitlement( [ 'owner_id' => $owner->id, 'entitleable_id' => $user->id, 'entitleable_type' => User::class, 'wallet_id' => $wallets[0]->id, 'sku_id' => $sku_mailbox->id, 'description' => "User Mailbox Entitlement Test" ] ); $owner->addEntitlement($entitlement_own_mailbox); $owner->addEntitlement($entitlement_domain); $owner->addEntitlement($entitlement_mailbox); $this->assertTrue($owner->entitlements()->count() == 3); $this->assertTrue($sku_domain->entitlements()->where('owner_id', $owner->id)->count() == 1); $this->assertTrue($sku_mailbox->entitlements()->where('owner_id', $owner->id)->count() == 2); $this->assertTrue($wallets[0]->entitlements()->count() == 3); $this->assertTrue($wallets[0]->fresh()->balance < 0.00); // TODO: Test case of adding entitlement that already exists } } diff --git a/src/tests/Feature/Jobs/PasswordResetEmailTest.php b/src/tests/Feature/Jobs/PasswordResetEmailTest.php index 22afe41d..7b85afdf 100644 --- a/src/tests/Feature/Jobs/PasswordResetEmailTest.php +++ b/src/tests/Feature/Jobs/PasswordResetEmailTest.php @@ -1,69 +1,67 @@ 'PasswordReset@UserAccount.com' - ]); $this->code = new VerificationCode([ 'mode' => 'password-reset', ]); + $user = $this->getTestUser('PasswordReset@UserAccount.com'); $user->verificationcodes()->save($this->code); $user->setSettings(['external_email' => 'etx@email.com']); } /** * {@inheritDoc} * * @return void */ public function tearDown(): void { $this->code->user->delete(); } /** * Test job handle * * @return void */ public function testPasswordResetEmailHandle() { Mail::fake(); // Assert that no jobs were pushed... Mail::assertNothingSent(); $job = new PasswordResetEmail($this->code); $job->handle(); // Assert the email sending job was pushed once Mail::assertSent(PasswordReset::class, 1); // Assert the mail was sent to the code's email Mail::assertSent(PasswordReset::class, function ($mail) { return $mail->hasTo($this->code->user->getSetting('external_email')); }); } } diff --git a/src/tests/Feature/SkuTest.php b/src/tests/Feature/SkuTest.php index f6301873..a3269df1 100644 --- a/src/tests/Feature/SkuTest.php +++ b/src/tests/Feature/SkuTest.php @@ -1,167 +1,156 @@ 'custom-domain.com' - ], - [ - 'status' => Domain::STATUS_NEW, - 'type' => Domain::TYPE_EXTERNAL, - ] - ); - User::firstOrCreate( - ['email' => 'sku-test-user@custom-domain.com'] - ); - } - - public function tearDown(): void - { User::where('email', 'sku-test-user@custom-domain.com')->delete(); Domain::where('namespace', 'custom-domain.com')->delete(); } /** * Tests for Sku::registerEntitlements() */ public function testRegisterEntitlement(): void { // TODO: This test depends on seeded SKUs, but probably should not + $domain = $this->getTestDomain( + 'custom-domain.com', + [ + 'status' => Domain::STATUS_NEW, + 'type' => Domain::TYPE_EXTERNAL, + ] + ); - $user = User::where('email', 'sku-test-user@custom-domain.com')->first(); - $domain = Domain::where('namespace', 'custom-domain.com')->first(); + $user = $this->getTestUser('sku-test-user@custom-domain.com'); $wallet = $user->wallets()->first(); // \App\Handlers\Mailbox SKU // Note, we're testing mailbox SKU before domain SKU as it may potentially fail in that order $sku = Sku::where('title', 'mailbox')->first(); $sku->registerEntitlement($user); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance = -$sku->cost; $this->assertCount(1, $entitlements); - $this->assertSame($user->id, $entitlements[0]->entitleable_id); + $this->assertEquals($user->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\Mailbox::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); // \App\Handlers\Domain SKU $sku = Sku::where('title', 'domain')->first(); $sku->registerEntitlement($user, [$domain]); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertSame($domain->id, $entitlements[0]->entitleable_id); + $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\Domain::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); // \App\Handlers\DomainRegistration SKU $sku = Sku::where('title', 'domain-registration')->first(); $sku->registerEntitlement($user, [$domain]); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertSame($domain->id, $entitlements[0]->entitleable_id); + $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\DomainRegistration::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); // \App\Handlers\DomainHosting SKU $sku = Sku::where('title', 'domain-hosting')->first(); $sku->registerEntitlement($user, [$domain]); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertSame($domain->id, $entitlements[0]->entitleable_id); + $this->assertEquals($domain->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\DomainHosting::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); // \App\Handlers\Groupware SKU $sku = Sku::where('title', 'groupware')->first(); $sku->registerEntitlement($user, [$domain]); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; $this->assertCount(1, $entitlements); - $this->assertSame($user->id, $entitlements[0]->entitleable_id); + $this->assertEquals($user->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\Mailbox::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); // \App\Handlers\Storage SKU $sku = Sku::where('title', 'storage')->first(); $sku->registerEntitlement($user, [$domain]); $entitlements = $sku->entitlements()->where('owner_id', $user->id)->get(); $wallet->refresh(); if ($sku->active) { $balance -= $sku->cost; // For Storage entitlement we expect additional Quota record $quota = Quota::where('user_id', $user->id)->first(); $this->assertTrue(!empty($quota)); // TODO: This should be a constant and/or config option, and probably // quota should not be in bytes $this->assertSame(2147483648, $quota->value); - $this->assertCount(1, $entitlements); - $this->assertSame($quota->id, $entitlements[0]->entitleable_id); + $this->assertEquals($quota->id, $entitlements[0]->entitleable_id); $this->assertSame(Handlers\Storage::entitleableClass(), $entitlements[0]->entitleable_type); } else { $this->assertCount(0, $entitlements); } $this->assertEquals($balance, $wallet->balance); } } diff --git a/src/tests/Feature/UserTest.php b/src/tests/Feature/UserTest.php index 4fc4ba40..6945e91e 100644 --- a/src/tests/Feature/UserTest.php +++ b/src/tests/Feature/UserTest.php @@ -1,66 +1,92 @@ delete(); + } + + /** + * Verify user creation process + */ + public function testUserCreateJob(): void + { + // Fake the queue, assert that no jobs were pushed... + Queue::fake(); + Queue::assertNothingPushed(); + + $user = User::create([ + 'email' => 'user-create-test@' . \config('app.domain') + ]); + + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, 1); + Queue::assertPushed(\App\Jobs\ProcessUserCreate::class, function ($job) use ($user) { + $job_user = TestCase::getObjectProperty($job, 'user'); + + return $job_user->id === $user->id + && $job_user->email === $user->email; + }); + } + /** * Verify a wallet assigned a controller is among the accounts of the assignee. */ public function testListUserAccounts(): void { - $userA = User::firstOrCreate(['email' => 'UserAccountA@UserAccount.com']); + $userA = $this->getTestUser('UserAccountA@UserAccount.com'); + $userB = $this->getTestUser('UserAccountB@UserAccount.com'); $this->assertTrue($userA->wallets()->count() == 1); $userA->wallets()->each( - function ($wallet) { - $userB = User::firstOrCreate(['email' => 'UserAccountB@UserAccount.com']); - + function ($wallet) use ($userB) { $wallet->addController($userB); } ); - $userB = User::firstOrCreate(['email' => 'UserAccountB@UserAccount.com']); - $this->assertTrue($userB->accounts()->get()[0]->id === $userA->wallets()->get()[0]->id); } public function testUserDomains(): void { - $user = User::firstOrCreate(['email' => 'john@kolab.org']); - + $user = $this->getTestUser('john@kolab.org'); $domains = []; foreach ($user->domains() as $domain) { $domains[] = $domain->namespace; } $this->assertContains('kolabnow.com', $domains); $this->assertContains('kolab.org', $domains); } /** * Tests for User::findByEmail() */ public function testFindByEmail(): void { - $user = User::firstOrCreate(['email' => 'john@kolab.org']); + $user = $this->getTestUser('john@kolab.org'); $result = User::findByEmail('john'); $this->assertNull($result); $result = User::findByEmail('non-existing@email.com'); $this->assertNull($result); $result = User::findByEmail('john@kolab.org'); $this->assertInstanceOf(User::class, $result); $this->assertSame($user->id, $result->id); // TODO: Make sure searching is case-insensitive // TODO: Alias, eternal email } } diff --git a/src/tests/Feature/VerificationCodeTest.php b/src/tests/Feature/VerificationCodeTest.php index 4c9bc8ce..6d8b52d4 100644 --- a/src/tests/Feature/VerificationCodeTest.php +++ b/src/tests/Feature/VerificationCodeTest.php @@ -1,44 +1,44 @@ 'UserAccountA@UserAccount.com']); + $user = $this->getTestUser('UserAccountA@UserAccount.com'); $data = [ 'user_id' => $user->id, 'mode' => 'password-reset', ]; $now = new \DateTime('now'); $code = VerificationCode::create($data); $code_length = env('VERIFICATION_CODE_LENGTH', VerificationCode::SHORTCODE_LENGTH); $code_exp_hrs = env('VERIFICATION_CODE_EXPIRY', VerificationCode::CODE_EXP_HOURS); $this->assertFalse($code->isExpired()); $this->assertTrue(strlen($code->code) === VerificationCode::CODE_LENGTH); $this->assertTrue(strlen($code->short_code) === $code_length); $this->assertSame($data['mode'], $code->mode); $this->assertSame($user->id, $code->user->id); $this->assertInstanceOf(\DateTime::class, $code->expires_at); $this->assertSame($code_exp_hrs, $code->expires_at->diff($now)->h + 1); $inst = VerificationCode::find($code->code); $this->assertInstanceOf(VerificationCode::class, $inst); $this->assertSame($inst->code, $code->code); } } diff --git a/src/tests/Feature/WalletTest.php b/src/tests/Feature/WalletTest.php index aaa91d71..1561d1de 100644 --- a/src/tests/Feature/WalletTest.php +++ b/src/tests/Feature/WalletTest.php @@ -1,224 +1,173 @@ users as $user) { User::where('email', $user)->delete(); } } public function tearDown(): void { foreach ($this->users as $user) { User::where('email', $user)->delete(); } } /** * Verify a wallet is created, when a user is created. */ public function testCreateUserCreatesWallet(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UserWallet1@UserWallet.com' - ] - ); + $user = $this->getTestUser('UserWallet1@UserWallet.com'); $this->assertTrue($user->wallets()->count() == 1); } /** * Verify a user can haz more wallets. */ public function testAddWallet(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UserWallet2@UserWallet.com' - ] - ); + $user = $this->getTestUser('UserWallet2@UserWallet.com'); $user->wallets()->save( new Wallet(['currency' => 'USD']) ); $this->assertTrue($user->wallets()->count() >= 2); $user->wallets()->each( function ($wallet) { $this->assertTrue($wallet->balance === 0.00); } ); } /** * Verify we can not delete a user wallet that holds balance. */ public function testDeleteWalletWithCredit(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UserWallet3@UserWallet.com' - ] - ); + $user = $this->getTestUser('UserWallet3@UserWallet.com'); $user->wallets()->each( function ($wallet) { $wallet->credit(1.00)->save(); } ); $user->wallets()->each( function ($wallet) { $this->assertFalse($wallet->delete()); } ); } /** * Verify we can not delete a wallet that is the last wallet. */ public function testDeleteLastWallet(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UserWallet4@UserWallet.com' - ] - ); + $user = $this->getTestUser('UserWallet4@UserWallet.com'); $this->assertTrue($user->wallets()->count() == 1); $user->wallets()->each( function ($wallet) { $this->assertFalse($wallet->delete()); } ); } /** * Verify we can remove a wallet that is an additional wallet. */ public function testDeleteAddtWallet(): void { - $user = User::firstOrCreate( - [ - 'email' => 'UserWallet5@UserWallet.com' - ] - ); + $user = $this->getTestUser('UserWallet5@UserWallet.com'); $user->wallets()->save( new Wallet(['currency' => 'USD']) ); $user->wallets()->each( function ($wallet) { if ($wallet->currency == 'USD') { $this->assertNotFalse($wallet->delete()); } } ); } - /** * Verify a wallet can be assigned a controller. */ public function testAddWalletController(): void { - $userA = User::firstOrCreate( - [ - 'email' => 'WalletControllerA@WalletController.com' - ] - ); + $userA = $this->getTestUser('WalletControllerA@WalletController.com'); + $userB = $this->getTestUser('WalletControllerB@WalletController.com'); $userA->wallets()->each( - function ($wallet) { - $userB = User::firstOrCreate( - [ - 'email' => 'WalletControllerB@WalletController.com' - ] - ); - + function ($wallet) use ($userB) { $wallet->addController($userB); } ); - $userB = User::firstOrCreate( - [ - 'email' => 'WalletControllerB@WalletController.com' - ] - ); - $this->assertTrue($userB->accounts()->count() == 1); $aWallet = $userA->wallets()->get(); $bAccount = $userB->accounts()->get(); $this->assertTrue($bAccount[0]->id === $aWallet[0]->id); } /** * Verify controllers can also be removed from wallets. */ public function testRemoveWalletController(): void { - $userA = User::firstOrCreate( - [ - 'email' => 'WalletController2A@WalletController.com' - ] - ); + $userA = $this->getTestUser('WalletController2A@WalletController.com'); + $userB = $this->getTestUser('WalletController2B@WalletController.com'); $userA->wallets()->each( - function ($wallet) { - $userB = User::firstOrCreate( - [ - 'email' => 'WalletController2B@WalletController.com' - ] - ); - + function ($wallet) use ($userB) { $wallet->addController($userB); } ); - $userB = User::firstOrCreate( - [ - 'email' => 'WalletController2B@WalletController.com' - ] - ); + $userB->refresh(); $userB->accounts()->each( - function ($wallet) { - $userB = User::firstOrCreate( - [ - 'email' => 'WalletController2B@WalletController.com' - ] - ); - + function ($wallet) use ($userB) { $wallet->removeController($userB); } ); $this->assertTrue($userB->accounts()->count() == 0); } } diff --git a/src/tests/TestCase.php b/src/tests/TestCase.php index 2932d4a6..37eb44ee 100644 --- a/src/tests/TestCase.php +++ b/src/tests/TestCase.php @@ -1,10 +1,47 @@ $name], $attrib); + } + + /** + * Get User object by email, create it if needed. + * Skip LDAP jobs. + */ + protected function getTestUser($email, $attrib = []) + { + // Disable jobs (i.e. skip LDAP oprations) + Queue::fake(); + return User::firstOrCreate(['email' => $email], $attrib); + } + + /** + * Helper to access protected property of an object + */ + protected static function getObjectProperty($object, $property_name) + { + $reflection = new \ReflectionClass($object); + $property = $reflection->getProperty($property_name); + $property->setAccessible(true); + + return $property->getValue($object); + } }