orchestral/testbench

Duplicate Migrations

Closed this issue · 3 comments

  • Testbench Version: 8.10.2
  • Laravel Version: 10.23
  • PHP Version: 8.1
  • Database Driver & Version: SQLite - latest

Description:

When I run either of

  • testbench workbench:build
  • testbench package:test
  • pest --filter="some test"

I get an error saying that a table already exists

Steps To Reproduce:

I'm trying to do this on my package https://github.com/cjmellor/rating

I have a create_ratings_table.php file within database/migrations.

My testbench.yaml file:

providers:
  - Cjmellor\Rating\RatingServiceProvider
  - Livewire\LivewireServiceProvider

migrations: true
seeders: []

workbench:
  assets:
    - rating-config
    - rating-migrations
  build:
    - asset-publish
    - create-sqlite-table
    - migrate:fresh
  guard: web
  install: true
  start: '/'
  sync: []
  user: 1
  welcome: true

This is the Service Provider

<?php

namespace Cjmellor\Rating;

use Cjmellor\Rating\Livewire\Rating;
use Illuminate\Support\ServiceProvider;
use Livewire\Livewire;

class RatingServiceProvider extends ServiceProvider
{
    public function boot(): void
    {
        $this->publishes([
            __DIR__.'/../config/rating.php' => config_path('rating.php'),
        ], 'rating-config');

        $this->loadViewsFrom(__DIR__.'/../resources/views', 'rating');

        $this->publishes([
            __DIR__.'/../database/migrations/create_ratings_table.php' => database_path('migrations/'.date('Y_m_d_His', time()).'_create_ratings_table.php'),
        ], 'rating-migrations');

        Livewire::component('rating', Rating::class);
    }
}

This is the TestCase.php code:

<?php

namespace Cjmellor\Rating\Tests;

use Illuminate\Foundation\Testing\RefreshDatabase;
use Orchestra\Testbench\Concerns\WithWorkbench;
use Orchestra\Testbench\TestCase as Orchestra;

class TestCase extends Orchestra
{
    use RefreshDatabase;
    use WithWorkbench;

    public function defineEnvironment($app): void
    {
        $app['config']->set('database.default', 'testing');
    }

    public function defineDatabaseMigrations(): void
    {
        $this->loadLaravelMigrations();
    }

}

I ran both

  • testbench workbench:install
  • testbench workbench:build

Which gives me an output:

   INFO  Publishing [rating-config] assets.  

  Copying file [/Users/chris/Dev/Packages/rating/config/rating.php] to [config/rating.php] ................................ DONE

   INFO  Publishing [rating-migrations] assets.  

  Copying file [/Users/chris/Dev/Packages/rating/database/migrations/create_ratings_table.php] to [database/migrations/2023_09_16_002915_create_ratings_table.php]  DONE

   INFO  No publishable resources for tag [laravel-assets].  

  File [database/database.sqlite] already exists ....................................................................... SKIPPED  

  Dropping all tables ................................................................................................. 3ms DONE

   INFO  Preparing database.  

  Creating migration table ............................................................................................ 1ms DONE

   INFO  Running migrations.  

  2014_10_12_000000_testbench_create_users_table ...................................................................... 1ms DONE
  2014_10_12_100000_testbench_create_password_reset_tokens_table ...................................................... 0ms DONE
  2019_08_19_000000_testbench_create_failed_jobs_table ................................................................ 1ms DONE
  2023_09_14_230134_create_ratings_table .............................................................................. 2ms DONE
  2023_09_14_230633_create_ratings_table .............................................................................. 0ms FAIL

   Illuminate\Database\QueryException 

  SQLSTATE[HY000]: General error: 1 table "ratings" already exists (Connection: sqlite, SQL: create table "ratings" ("id" integer primary key autoincrement not null, "rateable_type" varchar not null, "rateable_id" integer not null, "user_id" integer, "rating" integer not null, "created_at" datetime, "updated_at" datetime, foreign key("user_id") references "users"("id") on delete set null))

  at vendor/laravel/framework/src/Illuminate/Database/Connection.php:801
    797▕                     $this->getName(), $query, $this->prepareBindings($bindings), $e
    798▕                 );
    799▕             }
    800▕ 
  ➜ 801▕             throw new QueryException(
    802▕                 $this->getName(), $query, $this->prepareBindings($bindings), $e
    803▕             );
    804▕         }
    805▕     }

It seems the migration is generated twice and trying to run twice.

Cannot for the life of me figure out why.

Thanks for taking the time to look.

database_path('migrations/'.date('Y_m_d_His', time()).'_create_ratings_table.php')

This pattern might work well before Laravel moves to new class extends Migration but you should just set a fixed date.

Hmm, so I just changed it to

__DIR__.'/../workbench/database/migrations/2023_09_16_003400_create_ratings_table.php' => database_path('migrations/0000_00_00_000000_create_ratings_table.php'),

But testbench workbench:build still produces that

   INFO  Running migrations.

  0000_00_00_000000_create_ratings_table .............................................................................. 4ms DONE
  2014_10_12_000000_testbench_create_users_table ...................................................................... 1ms DONE
  2014_10_12_100000_testbench_create_password_reset_tokens_table ...................................................... 0ms DONE
  2019_08_19_000000_testbench_create_failed_jobs_table ................................................................ 1ms DONE
  2023_09_14_230134_create_ratings_table .............................................................................. 1ms FAIL

Not sure where 2023_09_14_230134_create_ratings_table was coming from

I checked vendor/orchestra/testbench-core/laravel/database/migrations and there was a ton of migrations in there with the same name but different dates (probably from the date() method) so I deleted them and now the build runs every time with the fixed date.

Is this the right way to do the migrations in the testbench package, or have I missed anything?

Thanks for the pointer!

This is an issue with any packages using new class extends Migration + publishing migrations in general.

Before this, each migrations has a unique class name and Laravel will throw duplicate class exception if you try publishing package migrations more than once, and it's no longer possible with new class syntax.

You can use loadMigrationsFrom() if you don't need to actually publish migrations or set a fixed date.