diff --git a/app/Console/Commands/MergeUsersByUsername.php b/app/Console/Commands/MergeUsersByUsername.php index 390942708d..0c5e966ab9 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 b045fdef03..3a7f4d6a2c 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 3374f1907e..6a96fb7282 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 bf3a3ec3a8..82c68eb01a 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 d028560607..efac5a8d33 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 57eee03f17..b44fcdfcb4 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 2d2f56662a..69bf1d28a1 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 30e32061ed..a93eb26561 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 1a4007888c..a88166d14b 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 d9aec36afa..ca3a2faf95 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 5b1d0e9b3c..98748282fa 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 e042a6b5d8..6124489f7b 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 848e4ca499..8cae9d264c 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 2f6c08b058..71dafb2a0e 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 45635dc4f4..f2658cbb68 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 0000000000..4c43e6293d --- /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 2b1d557970..dc0ffdc803 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 0000000000..a9ae11171b --- /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 2a1eec10fc..b29fdf88cd 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); + } + } }