set('database.connections.legacy', config('database.connections.' . config('database.default'))); DB::purge('legacy'); Schema::connection('legacy')->dropIfExists('legacy_users'); Schema::connection('legacy')->create('legacy_users', function (Blueprint $table): void { $table->unsignedBigInteger('user_id')->primary(); $table->string('uname')->nullable(); $table->string('email')->nullable(); $table->string('real_name')->nullable(); $table->timestamp('joinDate')->nullable(); $table->timestamp('LastVisit')->nullable(); $table->unsignedTinyInteger('active')->default(1); $table->unsignedTinyInteger('should_migrate')->default(0); }); }); afterEach(function (): void { Schema::connection('legacy')->dropIfExists('legacy_users'); }); it('passes when every legacy should_migrate user exists in the new users table', function (): void { DB::table('users')->insert([ [ 'id' => 101, 'username' => 'alpha', 'email' => 'alpha@example.test', 'password' => bcrypt('secret'), 'created_at' => now(), 'updated_at' => now(), ], [ 'id' => 102, 'username' => 'beta', 'email' => 'beta@example.test', 'password' => bcrypt('secret'), 'created_at' => now(), 'updated_at' => now(), ], ]); DB::connection('legacy')->table('legacy_users')->insert([ ['user_id' => 101, 'uname' => 'alpha', 'email' => 'alpha@example.test', 'should_migrate' => 1], ['user_id' => 102, 'uname' => 'beta', 'email' => 'beta@example.test', 'should_migrate' => 1], ['user_id' => 103, 'uname' => 'gamma', 'email' => 'gamma@example.test', 'should_migrate' => 0], ]); $code = Artisan::call('users:audit-missing-migrated', [ '--legacy-users-table' => 'legacy_users', '--chunk' => 1, ]); $output = Artisan::output(); expect($code)->toBe(0) ->and($output)->toContain('Scanning legacy.legacy_users for should_migrate=1 and checking') ->and($output)->toContain('Done. scanned=2 existing=2 missing=0') ->and($output)->not->toContain('[missing]'); }); it('fails and outputs legacy users that are missing in the new users table', function (): void { DB::table('users')->insert([ 'id' => 201, 'username' => 'present-user', 'email' => 'present@example.test', 'password' => bcrypt('secret'), 'created_at' => now(), 'updated_at' => now(), ]); DB::connection('legacy')->table('legacy_users')->insert([ ['user_id' => 201, 'uname' => 'present-user', 'email' => 'present@example.test', 'should_migrate' => 1], ['user_id' => 202, 'uname' => 'missing-user', 'email' => 'missing@example.test', 'should_migrate' => 1], ['user_id' => 203, 'uname' => null, 'email' => null, 'should_migrate' => 1], ]); $code = Artisan::call('users:audit-missing-migrated', [ '--legacy-users-table' => 'legacy_users', '--chunk' => 2, ]); $output = Artisan::output(); expect($code)->toBe(1) ->and($output)->toContain('[missing] id=202 uname=@missing-user email=') ->and($output)->toContain('[missing] id=203 uname=(none) email=(none)') ->and($output)->toContain('Done. scanned=3 existing=1 missing=2'); }); it('can write missing users to a transaction wrapped sql file', function (): void { $sqlPath = base_path('test-results/audit-missing-migrated-users.sql'); @unlink($sqlPath); DB::table('users')->insert([ [ 'id' => 301, 'username' => 'present-user', 'email' => 'present@example.test', 'password' => bcrypt('secret'), 'created_at' => now(), 'updated_at' => now(), ], [ 'id' => 999, 'username' => 'missing-user', 'email' => 'missing@example.test', 'password' => bcrypt('secret'), 'created_at' => now(), 'updated_at' => now(), ], ]); DB::connection('legacy')->table('legacy_users')->insert([ [ 'user_id' => 301, 'uname' => 'present-user', 'email' => 'present@example.test', 'real_name' => 'Present User', 'joinDate' => '2024-01-01 10:00:00', 'LastVisit' => '2024-01-02 11:00:00', 'active' => 1, 'should_migrate' => 1, ], [ 'user_id' => 302, 'uname' => 'missing-user', 'email' => 'missing@example.test', 'real_name' => 'Legacy Missing', 'joinDate' => '2023-05-01 08:30:00', 'LastVisit' => '2023-05-03 09:45:00', 'active' => 0, 'should_migrate' => 1, ], ]); $code = Artisan::call('users:audit-missing-migrated', [ '--legacy-users-table' => 'legacy_users', '--sql-output' => $sqlPath, ]); $output = Artisan::output(); $sql = file_get_contents($sqlPath); expect($code)->toBe(1) ->and($output)->toContain('SQL export written to ' . $sqlPath . ' with 1 INSERT statement(s).') ->and($sql)->not->toBeFalse() ->and($sql)->toContain('START TRANSACTION;') ->and($sql)->toContain('INSERT INTO `users`') ->and($sql)->toContain("302, 'tmpu302'") ->and($sql)->toContain("'missing+1@example.test'") ->and($sql)->toContain("'Legacy Missing'") ->and($sql)->toContain('COMMIT;'); @unlink($sqlPath); });