diff --git a/app/Console/Commands/MergeUsersByUsername.php b/app/Console/Commands/MergeUsersByUsername.php index 390942708..0c5e966ab 100644 --- a/app/Console/Commands/MergeUsersByUsername.php +++ b/app/Console/Commands/MergeUsersByUsername.php @@ -2,6 +2,7 @@ namespace App\Console\Commands; +use App\Events\UserMerged; use App\Models\User; use Carbon\Carbon; use Illuminate\Console\Command; @@ -51,7 +52,7 @@ class MergeUsersByUsername extends Command $bad_users = User::where('username', '=', trim($parts[0])) ->whereNull('deleted_at') - ->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations') + ->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations','uploads', 'acceptances') ->get(); @@ -105,10 +106,26 @@ class MergeUsersByUsername extends Command $managedLocation->save(); } + foreach ($bad_user->uploads as $upload) { + $this->info('Updating upload log record '.$upload->id.' to user '.$user->id); + $upload->item_id = $user->id; + $upload->save(); + } + + foreach ($bad_user->acceptances as $acceptance) { + $this->info('Updating acceptance log record '.$acceptance->id.' to user '.$user->id); + $acceptance->item_id = $user->id; + $acceptance->save(); + } + // Mark the user as deleted $this->info('Marking the user as deleted'); $bad_user->deleted_at = Carbon::now()->timestamp; $bad_user->save(); + + event(new UserMerged($bad_user, $user, null)); + + } } } diff --git a/app/Events/UserMerged.php b/app/Events/UserMerged.php index b045fdef0..3a7f4d6a2 100644 --- a/app/Events/UserMerged.php +++ b/app/Events/UserMerged.php @@ -15,7 +15,7 @@ class UserMerged * * @return void */ - public function __construct(User $from_user, User $to_user, User $admin) + public function __construct(User $from_user, User $to_user, ?User $admin) { $this->merged_from = $from_user; $this->merged_to = $to_user; diff --git a/app/Http/Controllers/Api/UsersController.php b/app/Http/Controllers/Api/UsersController.php index 3374f1907..6a96fb728 100644 --- a/app/Http/Controllers/Api/UsersController.php +++ b/app/Http/Controllers/Api/UsersController.php @@ -247,10 +247,6 @@ class UsersController extends Controller 'jobtitle', 'username', 'employee_num', - 'assets', - 'accessories', - 'consumables', - 'licenses', 'groups', 'activated', 'created_at', diff --git a/app/Http/Controllers/Assets/AssetsController.php b/app/Http/Controllers/Assets/AssetsController.php index bf3a3ec3a..82c68eb01 100755 --- a/app/Http/Controllers/Assets/AssetsController.php +++ b/app/Http/Controllers/Assets/AssetsController.php @@ -906,6 +906,25 @@ class AssetsController extends Controller $asset->location_id = $request->input('location_id'); } + if (($asset->model->fieldset)) { + foreach ($asset->model->fieldset->fields as $field) { + if ($field->field_encrypted == '1') { + if (Gate::allows('admin')) { + if (is_array($request->input($field->db_column))) { + $asset->{$field->db_column} = Crypt::encrypt(implode(', ', $request->input($field->db_column))); + } else { + $asset->{$field->db_column} = Crypt::encrypt($request->input($field->db_column)); + } + } + } else { + if (is_array($request->input($field->db_column))) { + $asset->{$field->db_column} = implode(', ', $request->input($field->db_column)); + } else { + $asset->{$field->db_column} = $request->input($field->db_column); + } + } + } + } /** * Invoke Watson Validating to check the asset itself and check to make sure it saved correctly. diff --git a/app/Http/Controllers/Users/BulkUsersController.php b/app/Http/Controllers/Users/BulkUsersController.php index d02856060..efac5a8d3 100644 --- a/app/Http/Controllers/Users/BulkUsersController.php +++ b/app/Http/Controllers/Users/BulkUsersController.php @@ -42,7 +42,7 @@ class BulkUsersController extends Controller // Get the list of affected users $user_raw_array = request('ids'); $users = User::whereIn('id', $user_raw_array) - ->with('groups', 'assets', 'licenses', 'accessories')->get(); + ->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations','uploads', 'acceptances')->get(); // bulk edit, display the bulk edit form if ($request->input('bulk_actions') == 'edit') { @@ -317,7 +317,7 @@ class BulkUsersController extends Controller // Get the users $merge_into_user = User::find($request->input('merge_into_id')); - $users_to_merge = User::whereIn('id', $user_ids_to_merge)->with('assets', 'licenses', 'consumables','accessories')->get(); + $users_to_merge = User::whereIn('id', $user_ids_to_merge)->with('assets', 'manager', 'userlog', 'licenses', 'consumables', 'accessories', 'managedLocations','uploads', 'acceptances')->get(); $admin = User::find(Auth::user()->id); // Walk users @@ -344,10 +344,20 @@ class BulkUsersController extends Controller } foreach ($user_to_merge->userlog as $log) { - $log->target_id = $user_to_merge->id; + $log->target_id = $merge_into_user->id; $log->save(); } + foreach ($user_to_merge->uploads as $upload) { + $upload->item_id = $merge_into_user->id; + $upload->save(); + } + + foreach ($user_to_merge->acceptances as $acceptance) { + $acceptance->item_id = $merge_into_user->id; + $acceptance->save(); + } + User::where('manager_id', '=', $user_to_merge->id)->update(['manager_id' => $merge_into_user->id]); foreach ($user_to_merge->managedLocations as $managedLocation) { @@ -356,7 +366,6 @@ class BulkUsersController extends Controller } $user_to_merge->delete(); - //$user_to_merge->save(); event(new UserMerged($user_to_merge, $merge_into_user, $admin)); diff --git a/app/Listeners/LogListener.php b/app/Listeners/LogListener.php index 57eee03f1..b44fcdfcb 100644 --- a/app/Listeners/LogListener.php +++ b/app/Listeners/LogListener.php @@ -111,7 +111,7 @@ class LogListener $logaction->target_type = User::class; $logaction->action_type = 'merged'; $logaction->note = trans('general.merged_log_this_user_from', $to_from_array); - $logaction->user_id = $event->admin->id; + $logaction->user_id = $event->admin->id ?? null; $logaction->save(); // Add a record to the users being merged TO @@ -122,7 +122,7 @@ class LogListener $logaction->item_type = User::class; $logaction->action_type = 'merged'; $logaction->note = trans('general.merged_log_this_user_into', $to_from_array); - $logaction->user_id = $event->admin->id; + $logaction->user_id = $event->admin->id ?? null; $logaction->save(); diff --git a/app/Livewire/PersonalAccessTokens.php b/app/Livewire/PersonalAccessTokens.php index 2d2f56662..69bf1d28a 100644 --- a/app/Livewire/PersonalAccessTokens.php +++ b/app/Livewire/PersonalAccessTokens.php @@ -49,6 +49,6 @@ class PersonalAccessTokens extends Component { //this needs safety (though the scope of auth::user might kind of do it...) //seems like it does, test more - Auth::user()->tokens()->find($tokenId)->delete(); + Auth::user()->tokens()->find($tokenId)?->delete(); } } diff --git a/app/Models/User.php b/app/Models/User.php index 30e32061e..a93eb2656 100644 --- a/app/Models/User.php +++ b/app/Models/User.php @@ -481,8 +481,6 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo /** * Establishes the user -> uploads relationship * - * @todo I don't think we use this? - * * @author A. Gianotto * @since [v3.0] * @return \Illuminate\Database\Eloquent\Relations\Relation @@ -496,6 +494,21 @@ class User extends SnipeModel implements AuthenticatableContract, AuthorizableCo ->orderBy('created_at', 'desc'); } + /** + * Establishes the user -> acceptances relationship + * + * @author A. Gianotto + * @since [v7.0.7] + * @return \Illuminate\Database\Eloquent\Relations\Relation + */ + public function acceptances() + { + return $this->hasMany(\App\Models\Actionlog::class, 'target_id') + ->where('target_type', self::class) + ->where('action_type', '=', 'accepted') + ->orderBy('created_at', 'desc'); + } + /** * Establishes the user -> requested assets relationship * diff --git a/database/factories/ActionlogFactory.php b/database/factories/ActionlogFactory.php index 1a4007888..a88166d14 100644 --- a/database/factories/ActionlogFactory.php +++ b/database/factories/ActionlogFactory.php @@ -105,4 +105,64 @@ class ActionlogFactory extends Factory ]; }); } + + public function filesUploaded() + { + return $this->state(function () { + + return [ + 'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()), + 'action_type' => 'uploaded', + 'item_type' => User::class, + 'filename' => $this->faker->unixTime('now'), + ]; + }); + } + + public function acceptedSignature() + { + return $this->state(function () { + + $asset = Asset::factory()->create(); + + return [ + 'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()), + 'action_type' => 'accepted', + 'item_id' => $asset->id, + 'item_type' => Asset::class, + 'target_type' => User::class, + 'accept_signature' => $this->faker->unixTime('now'), + ]; + }); + } + + public function acceptedEula() + { + return $this->state(function () { + + $asset = Asset::factory()->create(); + + return [ + 'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()), + 'action_type' => 'accepted', + 'item_id' => $asset->id, + 'item_type' => Asset::class, + 'target_type' => User::class, + 'filename' => $this->faker->unixTime('now'), + ]; + }); + } + + public function userUpdated() + { + return $this->state(function () { + + return [ + 'created_at' => $this->faker->dateTimeBetween('-1 years', 'now', date_default_timezone_get()), + 'action_type' => 'update', + 'target_type' => User::class, + 'item_type' => User::class, + ]; + }); + } } diff --git a/database/factories/ConsumableFactory.php b/database/factories/ConsumableFactory.php index d9aec36af..ca3a2faf9 100644 --- a/database/factories/ConsumableFactory.php +++ b/database/factories/ConsumableFactory.php @@ -7,6 +7,7 @@ use App\Models\Company; use App\Models\Consumable; use App\Models\Manufacturer; use App\Models\User; +use Carbon\Carbon; use Illuminate\Database\Eloquent\Factories\Factory; use App\Models\Supplier; @@ -116,4 +117,16 @@ class ConsumableFactory extends Factory $consumable->category->update(['require_acceptance' => 1]); }); } + + public function checkedOutToUser(User $user = null) + { + return $this->afterCreating(function (Consumable $consumable) use ($user) { + $consumable->users()->attach($consumable->id, [ + 'consumable_id' => $consumable->id, + 'created_at' => Carbon::now(), + 'user_id' => User::factory()->create()->id, + 'assigned_to' => $user->id ?? User::factory()->create()->id, + ]); + }); + } } diff --git a/resources/lang/en-US/general.php b/resources/lang/en-US/general.php index 5b1d0e9b3..98748282f 100644 --- a/resources/lang/en-US/general.php +++ b/resources/lang/en-US/general.php @@ -549,6 +549,8 @@ return [ 'license_seats' => ':count License Seat|:count License Seats', 'consumables' => ':count Consumable|:count Consumables', 'components' => ':count Component|:count Components', - ] + ], + 'more_info' => 'More Info', + 'quickscan_bulk_help' => 'Checking this box will edit the asset record to reflect this new location. Leaving it unchecked will simply note the location in the audit log. Note that if this asset is checked out, it will not change the location of the person, asset or location it is checked out to.', ]; diff --git a/resources/views/hardware/quickscan.blade.php b/resources/views/hardware/quickscan.blade.php index e042a6b5d..6124489f7 100644 --- a/resources/views/hardware/quickscan.blade.php +++ b/resources/views/hardware/quickscan.blade.php @@ -52,7 +52,7 @@ {{ trans('admin/hardware/form.asset_location') }} - + diff --git a/resources/views/livewire/personal-access-tokens.blade.php b/resources/views/livewire/personal-access-tokens.blade.php index 848e4ca49..8cae9d264 100644 --- a/resources/views/livewire/personal-access-tokens.blade.php +++ b/resources/views/livewire/personal-access-tokens.blade.php @@ -48,7 +48,8 @@ - + diff --git a/resources/views/partials/more-info.blade.php b/resources/views/partials/more-info.blade.php index 2f6c08b05..71dafb2a0 100644 --- a/resources/views/partials/more-info.blade.php +++ b/resources/views/partials/more-info.blade.php @@ -1,3 +1,3 @@ - + diff --git a/resources/views/users/confirm-merge.blade.php b/resources/views/users/confirm-merge.blade.php index 45635dc4f..f2658cbb6 100644 --- a/resources/views/users/confirm-merge.blade.php +++ b/resources/views/users/confirm-merge.blade.php @@ -66,6 +66,10 @@ {{ trans('general.consumables') }} + + + {{ trans('general.files') }} + @@ -93,16 +97,19 @@ @endforeach - {{ number_format($user->assets()->count()) }} + {{ number_format($user->assets->count()) }} - {{ number_format($user->accessories()->count()) }} + {{ number_format($user->accessories->count()) }} - {{ number_format($user->licenses()->count()) }} + {{ number_format($user->licenses->count()) }} - {{ number_format($user->consumables()->count()) }} + {{ number_format($user->consumables->count()) }} + + + {{ number_format($user->uploads->count()) }} @endforeach diff --git a/tests/Feature/Console/MergeUsersTest.php b/tests/Feature/Console/MergeUsersTest.php new file mode 100644 index 000000000..4c43e6293 --- /dev/null +++ b/tests/Feature/Console/MergeUsersTest.php @@ -0,0 +1,135 @@ +create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Asset::factory()->count(3)->assignedToUser($user1)->create(); + Asset::factory()->count(3)->assignedToUser($user_to_merge_into)->create(); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->assets->count()); + $this->assertEquals(0, $user1->refresh()->assets->count()); + + } + + public function testLicensesAreTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + LicenseSeat::factory()->count(3)->create(['assigned_to' => $user1->id]); + LicenseSeat::factory()->count(3)->create(['assigned_to' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->licenses->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->licenses->count()); + $this->assertEquals(0, $user1->refresh()->licenses->count()); + + } + + public function testAccessoriesTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Accessory::factory()->count(3)->checkedOutToUser($user1)->create(); + Accessory::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create(); + + $this->assertEquals(3, $user_to_merge_into->refresh()->accessories->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->accessories->count()); + $this->assertEquals(0, $user1->refresh()->accessories->count()); + + } + + public function testConsumablesTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Consumable::factory()->count(3)->checkedOutToUser($user1)->create(); + Consumable::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create(); + + $this->assertEquals(3, $user_to_merge_into->refresh()->consumables->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->consumables->count()); + $this->assertEquals(0, $user1->refresh()->consumables->count()); + + } + + public function testFilesAreTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user1->id]); + Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->uploads->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->uploads->count()); + $this->assertEquals(0, $user1->refresh()->uploads->count()); + + } + + public function testAcceptancesAreTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user1->id]); + Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->acceptances->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + $this->assertEquals(6, $user_to_merge_into->refresh()->acceptances->count()); + $this->assertEquals(0, $user1->refresh()->acceptances->count()); + + } + + public function testUserUpdateHistoryIsTransferredOnUserMerge(): void + { + $user1 = User::factory()->create(['username' => 'user1']); + $user_to_merge_into = User::factory()->create(['username' => 'user1@example.com']); + + Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user1->id, 'item_id' => $user1->id]); + Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user_to_merge_into->id, 'item_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->userlog->count()); + + $this->artisan('snipeit:merge-users')->assertExitCode(0); + + // This needs to be more than the otherwise expected because the merge action itself is logged for the two merging users + $this->assertEquals(7, $user_to_merge_into->refresh()->userlog->count()); + $this->assertEquals(1, $user1->refresh()->userlog->count()); + + } + + +} diff --git a/tests/Feature/Users/Api/UserSearchTest.php b/tests/Feature/Users/Api/UserSearchTest.php index 2b1d55797..dc0ffdc80 100644 --- a/tests/Feature/Users/Api/UserSearchTest.php +++ b/tests/Feature/Users/Api/UserSearchTest.php @@ -144,4 +144,17 @@ class UserSearchTest extends TestCase 'User index contains unexpected user from another company' ); } + + public function testUsersIndexWhenInvalidSortFieldIsPassed() + { + $this->markIncompleteIfSqlite('This test is not compatible with SQLite'); + + $this->actingAsForApi(User::factory()->viewUsers()->create()) + ->getJson(route('api.users.index', [ + 'sort' => 'assets', + ])) + ->assertOk() + ->assertStatus(200) + ->json(); + } } diff --git a/tests/Feature/Users/Ui/MergeUsersTest.php b/tests/Feature/Users/Ui/MergeUsersTest.php new file mode 100644 index 000000000..a9ae11171 --- /dev/null +++ b/tests/Feature/Users/Ui/MergeUsersTest.php @@ -0,0 +1,213 @@ +create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Asset::factory()->count(3)->assignedToUser($user1)->create(); + Asset::factory()->count(3)->assignedToUser($user2)->create(); + Asset::factory()->count(3)->assignedToUser($user_to_merge_into)->create(); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->assets->count()); + $this->assertEquals(0, $user1->refresh()->assets->count()); + $this->assertEquals(0, $user2->refresh()->assets->count()); + + } + + public function testLicensesAreTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + LicenseSeat::factory()->count(3)->create(['assigned_to' => $user1->id]); + LicenseSeat::factory()->count(3)->create(['assigned_to' => $user2->id]); + LicenseSeat::factory()->count(3)->create(['assigned_to' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->licenses->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->licenses->count()); + $this->assertEquals(0, $user1->refresh()->licenses->count()); + $this->assertEquals(0, $user2->refresh()->licenses->count()); + + } + + public function testAccessoriesTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Accessory::factory()->count(3)->checkedOutToUser($user1)->create(); + Accessory::factory()->count(3)->checkedOutToUser($user2)->create(); + Accessory::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create(); + + $this->assertEquals(3, $user_to_merge_into->refresh()->accessories->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->accessories->count()); + $this->assertEquals(0, $user1->refresh()->accessories->count()); + $this->assertEquals(0, $user2->refresh()->accessories->count()); + + } + + public function testConsumablesTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Consumable::factory()->count(3)->checkedOutToUser($user1)->create(); + Consumable::factory()->count(3)->checkedOutToUser($user2)->create(); + Consumable::factory()->count(3)->checkedOutToUser($user_to_merge_into)->create(); + + $this->assertEquals(3, $user_to_merge_into->refresh()->consumables->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->consumables->count()); + $this->assertEquals(0, $user1->refresh()->consumables->count()); + $this->assertEquals(0, $user2->refresh()->consumables->count()); + + } + + public function testFilesAreTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user1->id]); + Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user2->id]); + Actionlog::factory()->count(3)->filesUploaded()->create(['item_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->uploads->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->uploads->count()); + $this->assertEquals(0, $user1->refresh()->uploads->count()); + $this->assertEquals(0, $user2->refresh()->uploads->count()); + + } + + public function testAcceptancesAreTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user1->id]); + Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user2->id]); + Actionlog::factory()->count(3)->acceptedSignature()->create(['target_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->acceptances->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + $this->assertEquals(9, $user_to_merge_into->refresh()->acceptances->count()); + $this->assertEquals(0, $user1->refresh()->acceptances->count()); + $this->assertEquals(0, $user2->refresh()->acceptances->count()); + + } + + public function testUserUpdateHistoryIsTransferredOnUserMerge() + { + $user1 = User::factory()->create(); + $user2 = User::factory()->create(); + $user_to_merge_into = User::factory()->create(); + + Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user1->id, 'item_id' => $user1->id]); + Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user2->id, 'item_id' => $user2->id]); + Actionlog::factory()->count(3)->userUpdated()->create(['target_id' => $user_to_merge_into->id, 'item_id' => $user_to_merge_into->id]); + + $this->assertEquals(3, $user_to_merge_into->refresh()->userlog->count()); + + $response = $this->actingAs(User::factory()->editUsers()->viewUsers()->create()) + ->post(route('users.merge.save', $user1->id), + [ + 'ids_to_merge' => [$user1->id, $user2->id], + 'merge_into_id' => $user_to_merge_into->id + ]) + ->assertStatus(302) + ->assertRedirect(route('users.index')); + + $this->followRedirects($response)->assertSee('Success'); + + // This needs to be 2 more than the otherwise expected because the merge action itself is logged for the two merging users + $this->assertEquals(11, $user_to_merge_into->refresh()->userlog->count()); + $this->assertEquals(2, $user1->refresh()->userlog->count()); + $this->assertEquals(2, $user2->refresh()->userlog->count()); + + } + + +} diff --git a/tests/Support/CanSkipTests.php b/tests/Support/CanSkipTests.php index 2a1eec10f..b29fdf88c 100644 --- a/tests/Support/CanSkipTests.php +++ b/tests/Support/CanSkipTests.php @@ -10,4 +10,11 @@ trait CanSkipTests $this->markTestIncomplete($message); } } + + public function markIncompleteIfSqlite($message = 'Test skipped due to database driver being sqlite.') + { + if (config('database.default') === 'sqlite') { + $this->markTestIncomplete($message); + } + } }