[]`, so building the // DatabaseManager blows up before any expectation is set. $schema = Mockery::mock(); $schema->shouldReceive('hasTable')->with('zp_access_tokens')->andReturn($tableExists); // The column/index work is not under test here: accept the calls and skip the // closures so no Blueprint is needed. $schema->shouldReceive('table')->andReturnNull(); $schema->shouldReceive('hasColumn')->andReturn(true); Schema::swap($schema); $db = Mockery::mock(); $db->shouldReceive('select')->andReturn([]); DB::swap($db); $recreated = false; $builder = Mockery::mock(SchemaBuilder::class); $builder->shouldReceive('createAccessTokensTable') ->andReturnUsing(function () use (&$recreated): void { $recreated = true; }); app()->instance(SchemaBuilder::class, $builder); $install = (new \ReflectionClass(Install::class))->newInstanceWithoutConstructor(); return ['result' => $install->update_sql_30504(), 'recreated' => $recreated]; } public function test_recreates_the_table_when_it_is_missing_instead_of_failing(): void { $run = $this->runMigration(tableExists: false); $this->assertTrue( $run['recreated'], 'A missing zp_access_tokens must be recreated, not left for the ALTER to die on (#3706)' ); $this->assertTrue( $run['result'], 'The migration must succeed on an install that reached 30504 without the table' ); } public function test_leaves_an_existing_table_alone(): void { $run = $this->runMigration(tableExists: true); $this->assertFalse( $run['recreated'], 'An install that already has zp_access_tokens must not have it recreated' ); $this->assertTrue($run['result'], 'The migration must still report success'); } }