mirror of
https://github.com/NNTmux/newznab-tmux.git
synced 2026-08-28 22:01:33 +00:00
Fix testing on local database
This commit is contained in:
@@ -0,0 +1,36 @@
|
||||
# ============================================
|
||||
# TESTING ENVIRONMENT - ISOLATED DATABASE
|
||||
# ============================================
|
||||
# This file is automatically loaded when running tests.
|
||||
# Tests use SQLite in-memory database to prevent
|
||||
# any changes to the real production database.
|
||||
# ============================================
|
||||
|
||||
APP_NAME=NNTmux
|
||||
APP_ENV=testing
|
||||
APP_KEY=base64:SomeTestKeyForTestingPurposesOnly123=
|
||||
APP_DEBUG=true
|
||||
APP_URL=http://localhost
|
||||
|
||||
# CRITICAL: Use SQLite in-memory database for complete isolation
|
||||
# DO NOT change these values - tests must never touch real database
|
||||
DB_CONNECTION=testing
|
||||
DB_DATABASE=:memory:
|
||||
|
||||
BCRYPT_ROUNDS=4
|
||||
|
||||
CACHE_STORE=array
|
||||
SESSION_DRIVER=array
|
||||
QUEUE_CONNECTION=sync
|
||||
MAIL_MAILER=array
|
||||
|
||||
TELESCOPE_ENABLED=false
|
||||
|
||||
# Disable external services during testing
|
||||
NNTP_USERNAME=
|
||||
NNTP_PASSWORD=
|
||||
NNTP_SERVER=
|
||||
NNTP_PORT=
|
||||
NNTP_SSLENABLED=false
|
||||
NNTP_SOCKET_TIMEOUT=120
|
||||
|
||||
@@ -15,6 +15,7 @@ use Carbon\CarbonImmutable;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasOne;
|
||||
@@ -105,6 +106,7 @@ use Spatie\Permission\Traits\HasRoles;
|
||||
*/
|
||||
final class User extends Authenticatable
|
||||
{
|
||||
use HasFactory;
|
||||
use HasRoles;
|
||||
use Notifiable;
|
||||
use SoftDeletes;
|
||||
|
||||
@@ -4,7 +4,24 @@ use Illuminate\Support\Str;
|
||||
|
||||
return [
|
||||
|
||||
'default' => env('DB_CONNECTION', 'mysql'),
|
||||
|
||||
'connections' => [
|
||||
'sqlite' => [
|
||||
'driver' => 'sqlite',
|
||||
'url' => env('DB_URL'),
|
||||
'database' => env('DB_DATABASE', database_path('database.sqlite')),
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true),
|
||||
],
|
||||
|
||||
'testing' => [
|
||||
'driver' => 'sqlite',
|
||||
'database' => ':memory:',
|
||||
'prefix' => '',
|
||||
'foreign_key_constraints' => true,
|
||||
],
|
||||
|
||||
'mysql' => [
|
||||
'driver' => 'mysql',
|
||||
'url' => env('DB_URL'),
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
namespace Database\Factories;
|
||||
|
||||
use App\Models\User;
|
||||
use Illuminate\Database\Eloquent\Factories\Factory;
|
||||
use Illuminate\Support\Facades\Hash;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
/**
|
||||
* @extends Factory<User>
|
||||
*/
|
||||
class UserFactory extends Factory
|
||||
{
|
||||
/**
|
||||
* The name of the factory's corresponding model.
|
||||
*
|
||||
* @var class-string<User>
|
||||
*/
|
||||
protected $model = User::class;
|
||||
|
||||
/**
|
||||
* The current password being used by the factory.
|
||||
*/
|
||||
protected static ?string $password;
|
||||
|
||||
/**
|
||||
* Define the model's default state.
|
||||
*
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function definition(): array
|
||||
{
|
||||
return [
|
||||
'username' => fake()->unique()->userName(),
|
||||
'email' => fake()->unique()->safeEmail(),
|
||||
'password' => static::$password ??= Hash::make('password'),
|
||||
'roles_id' => 1,
|
||||
'api_token' => md5(Str::random(32)),
|
||||
'grabs' => 0,
|
||||
'invites' => 0,
|
||||
'notes' => '',
|
||||
'movieview' => true,
|
||||
'xxxview' => false,
|
||||
'musicview' => true,
|
||||
'consoleview' => true,
|
||||
'bookview' => true,
|
||||
'gameview' => true,
|
||||
'verified' => true,
|
||||
'can_post' => true,
|
||||
'rate_limit' => 60,
|
||||
'email_verified_at' => now(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Indicate that the model's email address should be unverified.
|
||||
*/
|
||||
public function unverified(): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'email_verified_at' => null,
|
||||
'verified' => false,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a specific role for the user.
|
||||
*/
|
||||
public function withRole(int $roleId): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'roles_id' => $roleId,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set a role expiration date.
|
||||
*/
|
||||
public function withRoleExpiration(\DateTimeInterface|string|null $date): static
|
||||
{
|
||||
return $this->state(fn (array $attributes) => [
|
||||
'rolechangedate' => $date,
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,16 @@ return new class extends Migration
|
||||
*/
|
||||
public function up(): void
|
||||
{
|
||||
// Skip if table doesn't exist (e.g., fresh SQLite test database)
|
||||
if (!Schema::hasTable('failed_jobs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Skip if column already exists
|
||||
if (Schema::hasColumn('failed_jobs', 'uuid')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('failed_jobs', function (Blueprint $table) {
|
||||
$table->string('uuid')->unique();
|
||||
});
|
||||
@@ -21,6 +31,14 @@ return new class extends Migration
|
||||
*/
|
||||
public function down(): void
|
||||
{
|
||||
if (!Schema::hasTable('failed_jobs')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Schema::hasColumn('failed_jobs', 'uuid')) {
|
||||
return;
|
||||
}
|
||||
|
||||
Schema::table('failed_jobs', function (Blueprint $table) {
|
||||
$table->dropColumn('uuid');
|
||||
});
|
||||
|
||||
@@ -13,6 +13,8 @@
|
||||
</testsuites>
|
||||
<php>
|
||||
<env name="APP_ENV" value="testing"/>
|
||||
<env name="DB_CONNECTION" value="testing" force="true"/>
|
||||
<env name="DB_DATABASE" value=":memory:" force="true"/>
|
||||
<env name="BCRYPT_ROUNDS" value="4"/>
|
||||
<env name="CACHE_STORE" value="array"/>
|
||||
<env name="SESSION_DRIVER" value="array"/>
|
||||
|
||||
@@ -11,6 +11,7 @@ trait CreatesApplication
|
||||
*/
|
||||
public function createApplication()
|
||||
{
|
||||
|
||||
// Require the configured application instance.
|
||||
$app = require __DIR__.'/../bootstrap/app.php';
|
||||
|
||||
|
||||
@@ -2,19 +2,27 @@
|
||||
namespace Tests\Feature;
|
||||
use App\Models\User;
|
||||
use Carbon\Carbon;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Str;
|
||||
use Spatie\Permission\Models\Role;
|
||||
use Tests\TestCase;
|
||||
final class RoleUpgradeTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
private User $user;
|
||||
private Role $userRole;
|
||||
private Role $supporterRole;
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Use in-memory SQLite database for test isolation
|
||||
config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => ':memory:']);
|
||||
DB::purge();
|
||||
DB::reconnect();
|
||||
|
||||
// Create minimal tables needed for testing
|
||||
$this->createTestTables();
|
||||
|
||||
// Create the basic User role
|
||||
$this->userRole = Role::firstOrCreate(
|
||||
['name' => 'User'],
|
||||
@@ -47,19 +55,134 @@ final class RoleUpgradeTest extends TestCase
|
||||
$this->user = $this->createTestUser($this->userRole->id);
|
||||
$this->user->assignRole($this->userRole);
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
DB::disconnect();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create the minimal database tables needed for testing.
|
||||
*/
|
||||
private function createTestTables(): void
|
||||
{
|
||||
// Users table
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS users (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
username VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL UNIQUE,
|
||||
password VARCHAR(255) NOT NULL,
|
||||
roles_id INTEGER DEFAULT 1,
|
||||
rolechangedate DATETIME NULL,
|
||||
pending_roles_id INTEGER NULL,
|
||||
pending_role_start_date DATETIME NULL,
|
||||
api_token VARCHAR(255) NULL,
|
||||
grabs INTEGER DEFAULT 0,
|
||||
invites INTEGER DEFAULT 0,
|
||||
notes TEXT DEFAULT "",
|
||||
movieview INTEGER DEFAULT 1,
|
||||
xxxview INTEGER DEFAULT 0,
|
||||
musicview INTEGER DEFAULT 1,
|
||||
consoleview INTEGER DEFAULT 1,
|
||||
bookview INTEGER DEFAULT 1,
|
||||
gameview INTEGER DEFAULT 1,
|
||||
verified INTEGER DEFAULT 1,
|
||||
can_post INTEGER DEFAULT 1,
|
||||
rate_limit INTEGER DEFAULT 60,
|
||||
email_verified_at DATETIME NULL,
|
||||
created_at DATETIME NULL,
|
||||
updated_at DATETIME NULL,
|
||||
deleted_at DATETIME NULL
|
||||
)');
|
||||
|
||||
// Roles table (spatie/laravel-permission)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS roles (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
guard_name VARCHAR(255) NOT NULL,
|
||||
addyears INTEGER DEFAULT 0,
|
||||
apirequests INTEGER DEFAULT 10,
|
||||
downloadrequests INTEGER DEFAULT 5,
|
||||
defaultinvites INTEGER DEFAULT 0,
|
||||
isdefault INTEGER DEFAULT 0,
|
||||
donation INTEGER DEFAULT 0,
|
||||
canpreview INTEGER DEFAULT 0,
|
||||
created_at DATETIME NULL,
|
||||
updated_at DATETIME NULL
|
||||
)');
|
||||
|
||||
// Model has roles table (spatie/laravel-permission)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS model_has_roles (
|
||||
role_id INTEGER NOT NULL,
|
||||
model_type VARCHAR(255) NOT NULL,
|
||||
model_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (role_id, model_type, model_id)
|
||||
)');
|
||||
|
||||
// Permissions table (spatie/laravel-permission)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS permissions (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
name VARCHAR(255) NOT NULL,
|
||||
guard_name VARCHAR(255) NOT NULL,
|
||||
created_at DATETIME NULL,
|
||||
updated_at DATETIME NULL
|
||||
)');
|
||||
|
||||
// Model has permissions table (spatie/laravel-permission)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS model_has_permissions (
|
||||
permission_id INTEGER NOT NULL,
|
||||
model_type VARCHAR(255) NOT NULL,
|
||||
model_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (permission_id, model_type, model_id)
|
||||
)');
|
||||
|
||||
// Role has permissions table (spatie/laravel-permission)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS role_has_permissions (
|
||||
permission_id INTEGER NOT NULL,
|
||||
role_id INTEGER NOT NULL,
|
||||
PRIMARY KEY (permission_id, role_id)
|
||||
)');
|
||||
|
||||
// User role history table (if needed)
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS user_role_history (
|
||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
users_id INTEGER NOT NULL,
|
||||
old_roles_id INTEGER NULL,
|
||||
new_roles_id INTEGER NOT NULL,
|
||||
old_rolechangedate DATETIME NULL,
|
||||
new_rolechangedate DATETIME NULL,
|
||||
changed_by_user_id INTEGER NULL,
|
||||
reason TEXT NULL,
|
||||
created_at DATETIME NULL
|
||||
)');
|
||||
}
|
||||
/**
|
||||
* Helper to create a test user without factory.
|
||||
*/
|
||||
private function createTestUser(int $roleId, ?string $roleChangeDate = null): User
|
||||
{
|
||||
return User::create([
|
||||
'username' => 'testuser_'.Str::random(8),
|
||||
'email' => Str::random(8).'@test.com',
|
||||
'password' => bcrypt('password'),
|
||||
// Use forceCreate to bypass hashed cast
|
||||
$user = new User();
|
||||
$user->username = 'testuser_'.Str::random(8);
|
||||
$user->email = Str::random(8).'@test.com';
|
||||
$user->roles_id = $roleId;
|
||||
$user->rolechangedate = $roleChangeDate;
|
||||
$user->api_token = md5(Str::random(32));
|
||||
|
||||
// Insert directly to avoid hashed cast issue
|
||||
DB::table('users')->insert([
|
||||
'username' => $user->username,
|
||||
'email' => $user->email,
|
||||
'password' => 'hashed_password_placeholder',
|
||||
'roles_id' => $roleId,
|
||||
'rolechangedate' => $roleChangeDate,
|
||||
'api_token' => md5(Str::random(32)),
|
||||
'api_token' => $user->api_token,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return User::where('email', $user->email)->first();
|
||||
}
|
||||
/**
|
||||
* Test that updating a user role to Supporter with addYears=2 correctly applies 2 years.
|
||||
|
||||
@@ -6,8 +6,8 @@ namespace Tests\Feature;
|
||||
|
||||
use App\Models\SteamApp;
|
||||
use App\Services\SteamService;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Http;
|
||||
use Tests\TestCase;
|
||||
|
||||
@@ -16,17 +16,33 @@ use Tests\TestCase;
|
||||
*/
|
||||
class SteamServiceTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
protected SteamService $service;
|
||||
|
||||
protected function setUp(): void
|
||||
{
|
||||
parent::setUp();
|
||||
|
||||
// Use in-memory SQLite database for test isolation
|
||||
config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => ':memory:']);
|
||||
DB::purge();
|
||||
DB::reconnect();
|
||||
|
||||
// Create minimal steam_apps table for testing
|
||||
DB::statement('CREATE TABLE IF NOT EXISTS steam_apps (
|
||||
appid INTEGER PRIMARY KEY,
|
||||
name VARCHAR(255)
|
||||
)');
|
||||
|
||||
$this->service = new SteamService();
|
||||
Cache::flush();
|
||||
}
|
||||
|
||||
protected function tearDown(): void
|
||||
{
|
||||
DB::disconnect();
|
||||
parent::tearDown();
|
||||
}
|
||||
|
||||
// ==========================================
|
||||
// Search Tests with Mocked Database
|
||||
// ==========================================
|
||||
|
||||
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Support;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
|
||||
/**
|
||||
* Trait DatabaseTestCase
|
||||
*
|
||||
* A trait for tests that need database access but should use an isolated
|
||||
* SQLite in-memory database instead of the real database.
|
||||
*
|
||||
* This trait:
|
||||
* - Ensures the test uses an in-memory SQLite database
|
||||
* - Runs migrations for clean test state
|
||||
* - Prevents tests from affecting production data
|
||||
*
|
||||
* Usage:
|
||||
* use Tests\Support\DatabaseTestCase;
|
||||
*
|
||||
* class MyTest extends TestCase
|
||||
* {
|
||||
* use DatabaseTestCase;
|
||||
* // ...
|
||||
* }
|
||||
*/
|
||||
trait DatabaseTestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
/**
|
||||
* Define hooks to migrate the database before and after each test.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUpDatabaseTestCase(): void
|
||||
{
|
||||
// Ensure we're using the testing connection (SQLite in-memory)
|
||||
if (config('database.default') !== 'testing') {
|
||||
config(['database.default' => 'testing']);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Boot the testing trait for this class.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function setUpTraits(): array
|
||||
{
|
||||
$uses = parent::setUpTraits();
|
||||
|
||||
if (isset($uses[DatabaseTestCase::class])) {
|
||||
$this->setUpDatabaseTestCase();
|
||||
}
|
||||
|
||||
return $uses;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user