diff --git a/.env.testing b/.env.testing
index e0b6eaa..989e21a 100644
--- a/.env.testing
+++ b/.env.testing
@@ -1,11 +1,8 @@
APP_ENV=testing
APP_KEY=base64:+7T2RuonhTIij1yLp3rTOv2uQlYJh0TQulu20MlCA+s=
-DB_CONNECTION=mysql
-DB_HOST=db
-DB_DATABASE=testing
-DB_USERNAME=incr_user
-DB_PASSWORD=incr_password
+DB_CONNECTION=sqlite
+DB_DATABASE=:memory:
SESSION_DRIVER=array
CACHE_STORE=array
diff --git a/.forgejo/workflows/ci.yml b/.forgejo/workflows/ci.yml
index 5a58da1..790dc78 100644
--- a/.forgejo/workflows/ci.yml
+++ b/.forgejo/workflows/ci.yml
@@ -10,17 +10,11 @@ jobs:
ci:
runs-on: docker
container:
- image: forge.lvl0.xyz/lvl0/incr-ci:php8.3-2
-
- services:
- db:
- image: mysql:8.0
- env:
- MYSQL_DATABASE: testing
- MYSQL_USER: incr_user
- MYSQL_PASSWORD: incr_password
- MYSQL_ROOT_PASSWORD: root_password
+ image: forge.lvl0.xyz/lvl0/incr-ci:php8.3-3
+ # No service container: on this runner a job container cannot reach one
+ # (the service starts fine but lands on a different network). Tests run
+ # against sqlite in memory instead — see .env.testing.
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
@@ -37,22 +31,6 @@ jobs:
- name: Prepare environment
run: cp .env.testing .env
- - name: Wait for MySQL
- run: |
- for i in $(seq 1 30); do
- if php -r 'exit(@fsockopen("db", 3306) ? 0 : 1);'; then
- echo "MySQL is up after ${i} attempt(s)"
- exit 0
- fi
- echo "Waiting for MySQL... ($i/30)"
- sleep 2
- done
- echo "MySQL never became reachable on db:3306" >&2
- exit 1
-
- - name: Run migrations
- run: php artisan migrate --force
-
- name: Lint
run: vendor/bin/pint --test
diff --git a/.forgejo/workflows/images.yml b/.forgejo/workflows/images.yml
index a098759..9d83591 100644
--- a/.forgejo/workflows/images.yml
+++ b/.forgejo/workflows/images.yml
@@ -20,7 +20,7 @@ jobs:
include:
- name: incr-ci
file: docker/build/Dockerfile.ci
- version: php8.3-2
+ version: php8.3-3
steps:
- uses: https://data.forgejo.org/actions/checkout@v4
diff --git a/database/migrations/0001_01_01_000000_create_assets_table.php b/database/migrations/0001_01_01_000000_create_assets_table.php
deleted file mode 100644
index a27ba44..0000000
--- a/database/migrations/0001_01_01_000000_create_assets_table.php
+++ /dev/null
@@ -1,25 +0,0 @@
-id();
- $table->string('symbol')->unique();
- $table->string('full_name')->nullable();
- $table->timestamps();
-
- $table->index('symbol');
- });
- }
-
- public function down(): void
- {
- Schema::dropIfExists('assets');
- }
-};
diff --git a/database/migrations/0001_01_01_000001_create_users_table.php b/database/migrations/0001_01_01_000001_create_users_table.php
deleted file mode 100644
index 4f7f9b6..0000000
--- a/database/migrations/0001_01_01_000001_create_users_table.php
+++ /dev/null
@@ -1,53 +0,0 @@
-id();
- $table->string('name');
- $table->string('email')->unique();
- $table->timestamp('email_verified_at')->nullable();
- $table->string('password');
- $table->foreignId('asset_id')->nullable()->constrained()->onDelete('set null');
- $table->boolean('price_tracking_enabled')->default(false);
- $table->rememberToken();
- $table->timestamps();
-
- $table->index('asset_id');
- });
-
- Schema::create('password_reset_tokens', function (Blueprint $table) {
- $table->string('email')->primary();
- $table->string('token');
- $table->timestamp('created_at')->nullable();
- });
-
- Schema::create('sessions', function (Blueprint $table) {
- $table->string('id')->primary();
- $table->foreignId('user_id')->nullable()->index();
- $table->string('ip_address', 45)->nullable();
- $table->text('user_agent')->nullable();
- $table->longText('payload');
- $table->integer('last_activity')->index();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('users');
- Schema::dropIfExists('password_reset_tokens');
- Schema::dropIfExists('sessions');
- }
-};
diff --git a/database/migrations/2025_07_10_152716_create_asset_prices_table.php b/database/migrations/2025_07_10_152716_create_asset_prices_table.php
deleted file mode 100644
index 36653db..0000000
--- a/database/migrations/2025_07_10_152716_create_asset_prices_table.php
+++ /dev/null
@@ -1,28 +0,0 @@
-id();
- $table->foreignId('asset_id')->constrained()->onDelete('cascade');
- $table->date('date');
- $table->decimal('price', 10, 4);
- $table->timestamps();
-
- $table->unique(['asset_id', 'date']);
- $table->index('asset_id');
- $table->index('date');
- });
- }
-
- public function down(): void
- {
- Schema::dropIfExists('asset_prices');
- }
-};
diff --git a/database/migrations/2025_07_12_221324_create_milestones_table.php b/database/migrations/2025_07_12_221324_create_milestones_table.php
deleted file mode 100644
index da034cc..0000000
--- a/database/migrations/2025_07_12_221324_create_milestones_table.php
+++ /dev/null
@@ -1,29 +0,0 @@
-id();
- $table->integer('target');
- $table->string('description');
- $table->timestamps();
- });
- }
-
- /**
- * Reverse the migrations.
- */
- public function down(): void
- {
- Schema::dropIfExists('milestones');
- }
-};
diff --git a/database/migrations/2026_05_02_000001_create_trackers_table.php b/database/migrations/2026_05_02_000001_create_trackers_table.php
deleted file mode 100644
index 6b3afcf..0000000
--- a/database/migrations/2026_05_02_000001_create_trackers_table.php
+++ /dev/null
@@ -1,50 +0,0 @@
-id();
- $table->foreignId('user_id')->constrained()->cascadeOnDelete();
- $table->foreignId('asset_id')->nullable()->constrained()->nullOnDelete();
- $table->string('label');
- $table->string('unit');
- $table->boolean('price_tracking_enabled')->default(false);
- $table->timestamps();
- });
-
- // Migrate existing users: create one tracker per user from their current asset_id + price_tracking_enabled
- DB::table('users')->orderBy('id')->each(function (object $user) {
- DB::table('trackers')->insert([
- 'user_id' => $user->id,
- 'asset_id' => $user->asset_id,
- 'label' => 'Portfolio',
- 'unit' => 'shares',
- 'price_tracking_enabled' => $user->price_tracking_enabled ?? false,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- });
- }
-
- public function down(): void
- {
- // Restore asset_id and price_tracking_enabled back onto users before dropping trackers
- DB::table('trackers')->orderBy('id')->each(function (object $tracker) {
- DB::table('users')
- ->where('id', $tracker->user_id)
- ->update([
- 'asset_id' => $tracker->asset_id,
- 'price_tracking_enabled' => $tracker->price_tracking_enabled,
- ]);
- });
-
- Schema::dropIfExists('trackers');
- }
-};
diff --git a/database/migrations/2026_05_02_000002_create_entries_table.php b/database/migrations/2026_05_02_000002_create_entries_table.php
deleted file mode 100644
index b6fbec3..0000000
--- a/database/migrations/2026_05_02_000002_create_entries_table.php
+++ /dev/null
@@ -1,30 +0,0 @@
-id();
- $table->foreignId('tracker_id')->constrained()->cascadeOnDelete();
- $table->date('date');
- $table->decimal('quantity', 12, 6);
- $table->decimal('unit_price', 12, 4)->nullable();
- $table->decimal('total_cost', 12, 2)->nullable();
- $table->timestamps();
-
- $table->index(['tracker_id', 'date']);
- });
- }
-
- public function down(): void
- {
- Schema::dropIfExists('entries');
- }
-};
diff --git a/database/migrations/2026_05_02_000003_add_tracker_to_milestones_drop_from_users.php b/database/migrations/2026_05_02_000003_add_tracker_to_milestones_drop_from_users.php
deleted file mode 100644
index b1cbc2a..0000000
--- a/database/migrations/2026_05_02_000003_add_tracker_to_milestones_drop_from_users.php
+++ /dev/null
@@ -1,44 +0,0 @@
-foreignId('tracker_id')->nullable()->after('id')->constrained()->cascadeOnDelete();
- });
-
- // Backfill tracker_id on milestones
- $trackerId = DB::table('trackers')->value('id');
- if ($trackerId) {
- DB::table('milestones')->update(['tracker_id' => $trackerId]);
- }
-
- Schema::table('milestones', function (Blueprint $table) {
- $table->unsignedBigInteger('tracker_id')->nullable(false)->change();
- });
-
- Schema::table('users', function (Blueprint $table) {
- $table->dropForeign(['asset_id']);
- $table->dropColumn(['asset_id', 'price_tracking_enabled']);
- });
- }
-
- public function down(): void
- {
- Schema::table('users', function (Blueprint $table) {
- $table->foreignId('asset_id')->nullable()->constrained()->nullOnDelete();
- $table->boolean('price_tracking_enabled')->default(false);
- });
-
- Schema::table('milestones', function (Blueprint $table) {
- $table->dropForeign(['tracker_id']);
- $table->dropColumn('tracker_id');
- });
- }
-};
diff --git a/database/migrations/2026_08_15_000001_drop_milestones_table.php b/database/migrations/2026_08_15_000001_drop_milestones_table.php
deleted file mode 100644
index 7461c21..0000000
--- a/database/migrations/2026_08_15_000001_drop_milestones_table.php
+++ /dev/null
@@ -1,26 +0,0 @@
-id();
- $table->foreignId('tracker_id')->constrained()->cascadeOnDelete();
- $table->integer('target');
- $table->string('description');
- $table->timestamps();
- });
- }
-};
diff --git a/database/migrations/2026_08_15_000002_add_count_to_trackers_drop_entries.php b/database/migrations/2026_08_15_000002_add_count_to_trackers_drop_entries.php
deleted file mode 100644
index f7940ab..0000000
--- a/database/migrations/2026_08_15_000002_add_count_to_trackers_drop_entries.php
+++ /dev/null
@@ -1,60 +0,0 @@
-unsignedInteger('count')->default(0)->after('unit');
- });
-
- if (Schema::hasTable('entries')) {
- DB::table('trackers')->orderBy('id')->each(function (object $tracker): void {
- $total = (float) DB::table('entries')->where('tracker_id', $tracker->id)->sum('quantity');
-
- DB::table('trackers')
- ->where('id', $tracker->id)
- ->update(['count' => max(0, (int) round($total))]);
- });
- }
-
- Schema::dropIfExists('entries');
- }
-
- // Lossy by design: per-entry dates and prices cannot be rebuilt from a scalar.
- public function down(): void
- {
- Schema::create('entries', function (Blueprint $table): void {
- $table->id();
- $table->foreignId('tracker_id')->constrained()->cascadeOnDelete();
- $table->date('date');
- $table->decimal('quantity', 12, 6);
- $table->decimal('unit_price', 12, 4)->nullable();
- $table->decimal('total_cost', 12, 2)->nullable();
- $table->timestamps();
-
- $table->index(['tracker_id', 'date']);
- });
-
- DB::table('trackers')->where('count', '>', 0)->orderBy('id')->each(function (object $tracker): void {
- DB::table('entries')->insert([
- 'tracker_id' => $tracker->id,
- 'date' => now()->toDateString(),
- 'quantity' => $tracker->count,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- });
-
- Schema::table('trackers', function (Blueprint $table): void {
- $table->dropColumn('count');
- });
- }
-};
diff --git a/database/migrations/2026_08_15_000003_drop_assets_and_pricing.php b/database/migrations/2026_08_15_000003_drop_assets_and_pricing.php
deleted file mode 100644
index 00bdd1a..0000000
--- a/database/migrations/2026_08_15_000003_drop_assets_and_pricing.php
+++ /dev/null
@@ -1,51 +0,0 @@
-dropForeign(['asset_id']);
- $table->dropColumn(['asset_id', 'price_tracking_enabled']);
- });
-
- Schema::dropIfExists('asset_prices');
- Schema::dropIfExists('assets');
- }
-
- // Lossy by design: recorded symbols and prices cannot be recovered.
- public function down(): void
- {
- Schema::create('assets', function (Blueprint $table): void {
- $table->id();
- $table->string('symbol')->unique();
- $table->string('full_name')->nullable();
- $table->timestamps();
-
- $table->index('symbol');
- });
-
- Schema::create('asset_prices', function (Blueprint $table): void {
- $table->id();
- $table->foreignId('asset_id')->constrained()->onDelete('cascade');
- $table->date('date');
- $table->decimal('price', 10, 4);
- $table->timestamps();
-
- $table->unique(['asset_id', 'date']);
- $table->index('asset_id');
- $table->index('date');
- });
-
- Schema::table('trackers', function (Blueprint $table): void {
- $table->foreignId('asset_id')->nullable()->constrained()->nullOnDelete();
- $table->boolean('price_tracking_enabled')->default(false);
- });
- }
-};
diff --git a/database/migrations/2026_08_15_000004_drop_users_and_sessions.php b/database/migrations/2026_08_15_000004_drop_users_and_sessions.php
deleted file mode 100644
index 3fca6fa..0000000
--- a/database/migrations/2026_08_15_000004_drop_users_and_sessions.php
+++ /dev/null
@@ -1,57 +0,0 @@
-dropForeign(['user_id']);
- $table->dropColumn('user_id');
- });
-
- Schema::dropIfExists('sessions');
- Schema::dropIfExists('password_reset_tokens');
- Schema::dropIfExists('users');
- }
-
- // Lossy by design: the app has no users, so nothing is restored into these tables.
- public function down(): void
- {
- Schema::create('users', function (Blueprint $table): void {
- $table->id();
- $table->string('name');
- $table->string('email')->unique();
- $table->timestamp('email_verified_at')->nullable();
- $table->string('password');
- $table->rememberToken();
- $table->timestamps();
- });
-
- Schema::create('password_reset_tokens', function (Blueprint $table): void {
- $table->string('email')->primary();
- $table->string('token');
- $table->timestamp('created_at')->nullable();
- });
-
- Schema::create('sessions', function (Blueprint $table): void {
- $table->string('id')->primary();
- $table->foreignId('user_id')->nullable()->index();
- $table->string('ip_address', 45)->nullable();
- $table->text('user_agent')->nullable();
- $table->longText('payload');
- $table->integer('last_activity')->index();
- });
-
- // Nullable, unlike the original: existing trackers have no user to point at,
- // so a NOT NULL foreign key cannot be added back.
- Schema::table('trackers', function (Blueprint $table): void {
- $table->foreignId('user_id')->nullable()->after('id')->constrained()->cascadeOnDelete();
- });
- }
-};
diff --git a/database/migrations/2026_08_16_000000_create_trackers_table.php b/database/migrations/2026_08_16_000000_create_trackers_table.php
new file mode 100644
index 0000000..c946f53
--- /dev/null
+++ b/database/migrations/2026_08_16_000000_create_trackers_table.php
@@ -0,0 +1,37 @@
+id();
+ $table->string('label');
+ $table->string('unit');
+ $table->unsignedInteger('count')->default(0);
+ $table->timestamps();
+ });
+ }
+
+ public function down(): void
+ {
+ Schema::dropIfExists('trackers');
+ }
+};
diff --git a/docker/build/Dockerfile.ci b/docker/build/Dockerfile.ci
index 02e53f2..c7b1348 100644
--- a/docker/build/Dockerfile.ci
+++ b/docker/build/Dockerfile.ci
@@ -8,13 +8,16 @@
# Debian-based rather than alpine: ffr hit repeated DNS resolution timeouts
# against codeload.github.com on the alpine build.
#
-# pdo_mysql, not sqlite: the migration tests query information_schema and
-# exercise MySQL-specific DDL, so CI runs against a real mysql:8.0 service.
+# pdo_sqlite: tests run against sqlite in memory (see .env.testing). Service
+# containers are not reachable from job containers on this runner, so CI must
+# not depend on one. pdo_mysql is kept so artisan can talk to a real database
+# when run manually inside this image.
FROM php:8.3-cli
COPY --from=mlocati/php-extension-installer:2 /usr/bin/install-php-extensions /usr/local/bin/
RUN install-php-extensions \
+ pdo_sqlite \
pdo_mysql \
mbstring \
dom \
diff --git a/phpstan.neon b/phpstan.neon
index 7564db0..f091dc1 100644
--- a/phpstan.neon
+++ b/phpstan.neon
@@ -11,11 +11,3 @@ parameters:
excludePaths:
- bootstrap/*.php
- storage/*
-
- ignoreErrors:
- # Migration files return an anonymous class; the base Migration declares neither up() nor down().
- # An interface on the migration is not an option: it would live under autoload-dev, which
- # production omits (`composer install --no-dev`), so every migration would fatal on deploy.
- -
- message: '#Call to an undefined method Illuminate\\Database\\Migrations\\Migration::(up|down)\(\)#'
- path: tests/Feature/*MigrationTest.php
diff --git a/phpunit.xml b/phpunit.xml
index 15ab25c..01bf6f9 100644
--- a/phpunit.xml
+++ b/phpunit.xml
@@ -22,7 +22,6 @@
-
diff --git a/tests/Feature/CountBackfillMigrationTest.php b/tests/Feature/CountBackfillMigrationTest.php
deleted file mode 100644
index 8b826d4..0000000
--- a/tests/Feature/CountBackfillMigrationTest.php
+++ /dev/null
@@ -1,145 +0,0 @@
-migration()->down();
- }
-
- private function makeTracker(): int
- {
- return DB::table('trackers')->insertGetId([
- 'label' => 'Counter',
- 'unit' => 'units',
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- }
-
- private function addEntry(int $trackerId, string $quantity): void
- {
- DB::table('entries')->insert([
- 'tracker_id' => $trackerId,
- 'date' => now()->toDateString(),
- 'quantity' => $quantity,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
- }
-
- private function countFor(int $trackerId): int
- {
- return (int) DB::table('trackers')->where('id', $trackerId)->value('count');
- }
-
- public function test_backfill_sums_entries_into_count(): void
- {
- $this->revertToLedger();
-
- $trackerId = $this->makeTracker();
- $this->addEntry($trackerId, '10');
- $this->addEntry($trackerId, '15');
-
- $this->migration()->up();
-
- $this->assertSame(25, $this->countFor($trackerId));
- }
-
- public function test_backfill_rounds_fractional_totals(): void
- {
- $this->revertToLedger();
-
- $trackerId = $this->makeTracker();
- $this->addEntry($trackerId, '1.4');
- $this->addEntry($trackerId, '1.2');
-
- $this->migration()->up();
-
- $this->assertSame(3, $this->countFor($trackerId));
- }
-
- public function test_backfill_clamps_negative_totals_to_zero(): void
- {
- $this->revertToLedger();
-
- $trackerId = $this->makeTracker();
- $this->addEntry($trackerId, '5');
- $this->addEntry($trackerId, '-9');
-
- $this->migration()->up();
-
- $this->assertSame(0, $this->countFor($trackerId));
- }
-
- public function test_tracker_without_entries_defaults_to_zero(): void
- {
- $this->revertToLedger();
-
- $trackerId = $this->makeTracker();
-
- $this->migration()->up();
-
- $this->assertSame(0, $this->countFor($trackerId));
- }
-
- public function test_up_drops_the_entries_table(): void
- {
- $this->revertToLedger();
- $this->assertTrue(Schema::hasTable('entries'));
-
- $this->migration()->up();
-
- $this->assertFalse(Schema::hasTable('entries'));
- $this->assertTrue(Schema::hasColumn('trackers', 'count'));
- }
-
- // The two down() tests start from the migrated schema, so they do not call revertToLedger().
- public function test_down_recreates_entries_from_count(): void
- {
- $trackerId = $this->makeTracker();
- DB::table('trackers')->where('id', $trackerId)->update(['count' => 7]);
-
- $this->migration()->down();
-
- $this->assertTrue(Schema::hasTable('entries'));
- $this->assertFalse(Schema::hasColumn('trackers', 'count'));
-
- $entries = DB::table('entries')->where('tracker_id', $trackerId)->get();
-
- $this->assertCount(1, $entries);
- $this->assertSame(7.0, (float) $entries->first()->quantity);
- }
-
- public function test_down_writes_no_entry_for_a_zero_count(): void
- {
- $trackerId = $this->makeTracker();
-
- $this->migration()->down();
-
- $this->assertSame(0, DB::table('entries')->where('tracker_id', $trackerId)->count());
- }
-}
diff --git a/tests/Feature/DropAssetsMigrationTest.php b/tests/Feature/DropAssetsMigrationTest.php
deleted file mode 100644
index da17be1..0000000
--- a/tests/Feature/DropAssetsMigrationTest.php
+++ /dev/null
@@ -1,68 +0,0 @@
-assertFalse(Schema::hasTable('assets'));
- $this->assertFalse(Schema::hasTable('asset_prices'));
- $this->assertFalse(Schema::hasColumn('trackers', 'asset_id'));
- $this->assertFalse(Schema::hasColumn('trackers', 'price_tracking_enabled'));
- }
-
- public function test_down_restores_the_tables_and_columns(): void
- {
- $this->migration()->down();
-
- $this->assertTrue(Schema::hasTable('assets'));
- $this->assertTrue(Schema::hasTable('asset_prices'));
- $this->assertTrue(Schema::hasColumn('trackers', 'asset_id'));
- $this->assertTrue(Schema::hasColumn('trackers', 'price_tracking_enabled'));
- }
-
- public function test_up_drops_them_again_respecting_foreign_keys(): void
- {
- $this->migration()->down();
- $this->migration()->up();
-
- $this->assertFalse(Schema::hasTable('assets'));
- $this->assertFalse(Schema::hasTable('asset_prices'));
- $this->assertFalse(Schema::hasColumn('trackers', 'asset_id'));
- $this->assertFalse(Schema::hasColumn('trackers', 'price_tracking_enabled'));
- }
-
- public function test_dropping_assets_does_not_disturb_the_counter(): void
- {
- $this->migration()->down();
-
- $trackerId = DB::table('trackers')->insertGetId([
- 'label' => 'Counter',
- 'unit' => 'units',
- 'count' => 31,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
-
- $this->migration()->up();
-
- $this->assertSame(31, (int) DB::table('trackers')->where('id', $trackerId)->value('count'));
- }
-}
diff --git a/tests/Feature/DropUsersMigrationTest.php b/tests/Feature/DropUsersMigrationTest.php
deleted file mode 100644
index f642a1e..0000000
--- a/tests/Feature/DropUsersMigrationTest.php
+++ /dev/null
@@ -1,86 +0,0 @@
-assertFalse(Schema::hasTable('users'));
- $this->assertFalse(Schema::hasTable('sessions'));
- $this->assertFalse(Schema::hasTable('password_reset_tokens'));
- $this->assertFalse(Schema::hasColumn('trackers', 'user_id'));
- }
-
- public function test_down_restores_the_tables_and_column(): void
- {
- $this->migration()->down();
-
- $this->assertTrue(Schema::hasTable('users'));
- $this->assertTrue(Schema::hasTable('sessions'));
- $this->assertTrue(Schema::hasTable('password_reset_tokens'));
- $this->assertTrue(Schema::hasColumn('trackers', 'user_id'));
- }
-
- public function test_restored_user_id_is_nullable_so_existing_trackers_survive(): void
- {
- DB::table('trackers')->insert([
- 'label' => 'Counter',
- 'unit' => 'units',
- 'count' => 3,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
-
- $this->migration()->down();
-
- $nullable = DB::selectOne(
- 'select is_nullable from information_schema.columns
- where table_schema = database() and table_name = ? and column_name = ?',
- ['trackers', 'user_id']
- );
-
- $this->assertSame('YES', $nullable->is_nullable ?? $nullable->IS_NULLABLE);
- }
-
- public function test_up_drops_them_again_respecting_foreign_keys(): void
- {
- $this->migration()->down();
- $this->migration()->up();
-
- $this->assertFalse(Schema::hasTable('users'));
- $this->assertFalse(Schema::hasColumn('trackers', 'user_id'));
- }
-
- public function test_the_counter_survives_the_round_trip(): void
- {
- $trackerId = DB::table('trackers')->insertGetId([
- 'label' => 'Counter',
- 'unit' => 'units',
- 'count' => 77,
- 'created_at' => now(),
- 'updated_at' => now(),
- ]);
-
- $this->migration()->down();
- $this->migration()->up();
-
- $this->assertSame(77, (int) DB::table('trackers')->where('id', $trackerId)->value('count'));
- }
-}