update project
This commit is contained in:
@@ -0,0 +1,333 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Integration;
|
||||
|
||||
use Illuminate\Auth\GenericUser;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class AuthTenantIsolationIntegrationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_user_can_access_own_school_workflows(): void
|
||||
{
|
||||
$school = $this->createSchool('Own School', 'OWN');
|
||||
$user = $this->createActorForSchool($school);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
$this->getJson("/api/schools/{$school}")
|
||||
->assertOk();
|
||||
|
||||
$studentResponse = $this->postJson("/api/schools/{$school}/students", [
|
||||
'first_name' => 'Maya',
|
||||
'last_name' => 'Patel',
|
||||
'student_number' => 'OWN-1001',
|
||||
'grade_level' => '7',
|
||||
]);
|
||||
|
||||
$studentResponse->assertCreated();
|
||||
|
||||
$studentId = $studentResponse->json('data.id');
|
||||
|
||||
$this->getJson("/api/schools/{$school}/students/{$studentId}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.student_number', 'OWN-1001');
|
||||
|
||||
$this->postJson("/api/schools/{$school}/attendance/scans", [
|
||||
'student_number' => 'OWN-1001',
|
||||
'scanned_at' => '2026-05-29T08:15:00Z',
|
||||
'source' => 'kiosk',
|
||||
])->assertCreated();
|
||||
|
||||
$this->createTemplate(
|
||||
$school,
|
||||
'attendance_absence',
|
||||
'email',
|
||||
'Hello, {{ student.first_name }} was marked absent.'
|
||||
);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/preview", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $studentId,
|
||||
'channel' => 'email',
|
||||
])->assertOk();
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/send", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $studentId,
|
||||
'channel' => 'email',
|
||||
])->assertAccepted();
|
||||
|
||||
$file = UploadedFile::fake()->createWithContent(
|
||||
'payments.csv',
|
||||
"student_number,amount,paid_at,reference\nOWN-1001,125.00,2026-05-29,PAY-OWN-001\n"
|
||||
);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/finance/payment-files", [
|
||||
'file' => $file,
|
||||
])->assertCreated();
|
||||
|
||||
$this->getJson("/api/schools/{$school}/reports/daily?date=2026-05-29")
|
||||
->assertOk();
|
||||
}
|
||||
|
||||
public function test_user_cannot_access_another_schools_data(): void
|
||||
{
|
||||
$schoolA = $this->createSchool('School A', 'S-A');
|
||||
$schoolB = $this->createSchool('School B', 'S-B');
|
||||
|
||||
$userA = $this->createActorForSchool($schoolA);
|
||||
|
||||
$studentB = $this->createStudent($schoolB, 'OTHER-2001', 'Other');
|
||||
|
||||
$this->createTemplate(
|
||||
$schoolB,
|
||||
'attendance_absence',
|
||||
'email',
|
||||
'Hello, {{ student.first_name }} was marked absent.'
|
||||
);
|
||||
|
||||
$this->actingAs($userA);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->getJson("/api/schools/{$schoolB}")
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->postJson("/api/schools/{$schoolB}/students", [
|
||||
'first_name' => 'Intruder',
|
||||
'last_name' => 'Student',
|
||||
'student_number' => 'BAD-1001',
|
||||
'grade_level' => '8',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->getJson("/api/schools/{$schoolB}/students/{$studentB}")
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->postJson("/api/schools/{$schoolB}/attendance/scans", [
|
||||
'student_number' => 'OTHER-2001',
|
||||
'scanned_at' => '2026-05-29T08:15:00Z',
|
||||
'source' => 'kiosk',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->postJson("/api/schools/{$schoolB}/communications/preview", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $studentB,
|
||||
'channel' => 'email',
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->postJson("/api/schools/{$schoolB}/communications/send", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $studentB,
|
||||
'channel' => 'email',
|
||||
])
|
||||
);
|
||||
|
||||
$file = UploadedFile::fake()->createWithContent(
|
||||
'payments.csv',
|
||||
"student_number,amount,paid_at,reference\nOTHER-2001,125.00,2026-05-29,PAY-BAD-001\n"
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->postJson("/api/schools/{$schoolB}/finance/payment-files", [
|
||||
'file' => $file,
|
||||
])
|
||||
);
|
||||
|
||||
$this->assertTenantBlocked(
|
||||
$this->getJson("/api/schools/{$schoolB}/reports/daily?date=2026-05-29")
|
||||
);
|
||||
|
||||
$this->assertDatabaseMissing('students', [
|
||||
'school_id' => $schoolB,
|
||||
'student_identifier' => 'BAD-1001',
|
||||
]);
|
||||
|
||||
$this->assertDatabaseMissing('payments', [
|
||||
'school_id' => $schoolB,
|
||||
'reference_number' => 'PAY-BAD-001',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_guest_cannot_access_school_workflows(): void
|
||||
{
|
||||
$school = $this->createSchool('Guest Block School', 'GST');
|
||||
|
||||
$student = $this->createStudent($school, 'GUEST-1001', 'Guest');
|
||||
|
||||
$this->createTemplate(
|
||||
$school,
|
||||
'attendance_absence',
|
||||
'email',
|
||||
'Hello, {{ student.first_name }} was marked absent.'
|
||||
);
|
||||
|
||||
$this->getJson("/api/schools/{$school}")
|
||||
->assertUnauthorized();
|
||||
|
||||
$this->postJson("/api/schools/{$school}/students", [
|
||||
'first_name' => 'Guest',
|
||||
'last_name' => 'Student',
|
||||
'student_number' => 'GUEST-2001',
|
||||
'grade_level' => '7',
|
||||
])->assertUnauthorized();
|
||||
|
||||
$this->getJson("/api/schools/{$school}/students/{$student}")
|
||||
->assertUnauthorized();
|
||||
|
||||
$this->postJson("/api/schools/{$school}/attendance/scans", [
|
||||
'student_number' => 'GUEST-1001',
|
||||
'scanned_at' => '2026-05-29T08:15:00Z',
|
||||
'source' => 'kiosk',
|
||||
])->assertUnauthorized();
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/preview", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $student,
|
||||
'channel' => 'email',
|
||||
])->assertUnauthorized();
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/send", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $student,
|
||||
'channel' => 'email',
|
||||
])->assertUnauthorized();
|
||||
|
||||
$file = UploadedFile::fake()->createWithContent(
|
||||
'payments.csv',
|
||||
"student_number,amount,paid_at,reference\nGUEST-1001,125.00,2026-05-29,PAY-GUEST-001\n"
|
||||
);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/finance/payment-files", [
|
||||
'file' => $file,
|
||||
])->assertUnauthorized();
|
||||
|
||||
$this->getJson("/api/schools/{$school}/reports/daily?date=2026-05-29")
|
||||
->assertUnauthorized();
|
||||
}
|
||||
|
||||
private function createSchool(string $name = 'Test School', ?string $code = null): int
|
||||
{
|
||||
return (int) DB::table('schools')->insertGetId([
|
||||
'name' => $name,
|
||||
'code' => $code ?? 'SCH-'.uniqid(),
|
||||
'domain_profile' => 'standard_school',
|
||||
'timezone' => 'UTC',
|
||||
'locale' => 'en',
|
||||
'currency' => 'USD',
|
||||
'active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createStudent(int $school, string $studentNumber, string $firstName = 'Maya'): int
|
||||
{
|
||||
return (int) DB::table('students')->insertGetId([
|
||||
'school_id' => $school,
|
||||
'school_id_number' => $studentNumber,
|
||||
'student_identifier' => $studentNumber,
|
||||
'first_name' => $firstName,
|
||||
'last_name' => 'Patel',
|
||||
'status' => 'active',
|
||||
'profile_metadata' => json_encode(['grade_level' => '7'], JSON_THROW_ON_ERROR),
|
||||
'metadata' => json_encode(['grade_level' => '7'], JSON_THROW_ON_ERROR),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTemplate(int $school, string $key, string $channel, string $body): int
|
||||
{
|
||||
return (int) DB::table('communication_templates')->insertGetId([
|
||||
'school_id' => $school,
|
||||
'template_key' => $key,
|
||||
'version' => '1',
|
||||
'status' => 'active',
|
||||
'channel' => $channel,
|
||||
'message_category' => 'general',
|
||||
'language' => 'en',
|
||||
'subject' => 'Notice for {{ student.first_name }}',
|
||||
'body' => $body,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createActorForSchool(int $school): Authenticatable
|
||||
{
|
||||
$this->ensureAuthTestTablesExist();
|
||||
|
||||
$userId = (int) DB::table('users')->insertGetId([
|
||||
'name' => 'Test User '.$school,
|
||||
'email' => 'user'.$school.'@example.test',
|
||||
'password' => bcrypt('password'),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('school_user')->insert([
|
||||
'school_id' => $school,
|
||||
'user_id' => $userId,
|
||||
'role' => 'admin',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
return new GenericUser([
|
||||
'id' => $userId,
|
||||
'name' => 'Test User '.$school,
|
||||
'email' => 'user'.$school.'@example.test',
|
||||
]);
|
||||
}
|
||||
|
||||
private function ensureAuthTestTablesExist(): void
|
||||
{
|
||||
if (! Schema::hasTable('users')) {
|
||||
Schema::create('users', function ($table): void {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('school_user')) {
|
||||
Schema::create('school_user', function ($table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('school_id')->index();
|
||||
$table->unsignedBigInteger('user_id')->index();
|
||||
$table->string('role')->default('admin');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['school_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private function assertTenantBlocked($response): void
|
||||
{
|
||||
$this->assertContains(
|
||||
$response->getStatusCode(),
|
||||
[403, 404],
|
||||
'Cross-tenant access must return either 403 or 404.'
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,316 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Tests\Feature\Integration;
|
||||
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Http\UploadedFile;
|
||||
use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Auth\GenericUser;
|
||||
use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Tests\TestCase;
|
||||
|
||||
final class SchoolWorkflowIntegrationTest extends TestCase
|
||||
{
|
||||
use RefreshDatabase;
|
||||
|
||||
public function test_school_can_be_created_and_read_through_http(): void
|
||||
{
|
||||
$response = $this->postJson('/api/schools', [
|
||||
'name' => 'North Ridge School',
|
||||
'code' => 'NRS',
|
||||
'timezone' => 'America/New_York',
|
||||
]);
|
||||
|
||||
$response->assertCreated()
|
||||
->assertJsonPath('data.name', 'North Ridge School')
|
||||
->assertJsonPath('data.code', 'NRS');
|
||||
|
||||
$schoolId = (int) $response->json('data.id');
|
||||
|
||||
$this->actingAsSchoolUser($schoolId);
|
||||
|
||||
$this->getJson("/api/schools/{$schoolId}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.timezone', 'America/New_York');
|
||||
|
||||
$this->assertDatabaseHas('schools', [
|
||||
'id' => $schoolId,
|
||||
'code' => 'NRS',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_student_can_be_created_for_school_and_isolated_by_school(): void
|
||||
{
|
||||
$schoolA = $this->createSchool('School A', 'S-A');
|
||||
$schoolB = $this->createSchool('School B', 'S-B');
|
||||
|
||||
$response = $this->postJson("/api/schools/{$schoolA}/students", [
|
||||
'first_name' => 'Maya',
|
||||
'last_name' => 'Patel',
|
||||
'student_number' => 'STU-1001',
|
||||
'grade_level' => '7',
|
||||
]);
|
||||
|
||||
$response->assertCreated()
|
||||
->assertJsonPath('data.first_name', 'Maya')
|
||||
->assertJsonPath('data.student_number', 'STU-1001');
|
||||
|
||||
$studentId = $response->json('data.id');
|
||||
|
||||
$this->getJson("/api/schools/{$schoolA}/students/{$studentId}")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.grade_level', '7');
|
||||
|
||||
$this->getJson("/api/schools/{$schoolB}/students/{$studentId}")
|
||||
->assertNotFound();
|
||||
}
|
||||
|
||||
public function test_attendance_scan_records_once_and_duplicate_scan_is_idempotent(): void
|
||||
{
|
||||
$school = $this->createSchool();
|
||||
$this->actingAsSchoolUser($school);
|
||||
$student = $this->createStudent($school, 'STU-2001');
|
||||
|
||||
$payload = [
|
||||
'student_number' => 'STU-2001',
|
||||
'scanned_at' => '2026-05-29T08:15:00Z',
|
||||
'source' => 'kiosk',
|
||||
];
|
||||
|
||||
$this->postJson("/api/schools/{$school}/attendance/scans", $payload)
|
||||
->assertCreated()
|
||||
->assertJsonPath('data.status', 'recorded');
|
||||
|
||||
$this->postJson("/api/schools/{$school}/attendance/scans", $payload)
|
||||
->assertOk()
|
||||
->assertJsonPath('data.status', 'duplicate');
|
||||
|
||||
$this->assertDatabaseHas('attendance_records', [
|
||||
'school_id' => $school,
|
||||
'subject_type' => 'student',
|
||||
'subject_id' => $student,
|
||||
'status' => 'present',
|
||||
]);
|
||||
|
||||
$this->assertSame(1, DB::table('attendance_records')->where('school_id', $school)->where('subject_id', $student)->count());
|
||||
$this->assertSame(2, DB::table('attendance_scan_logs')->where('school_id', $school)->where('subject_id', $student)->count());
|
||||
}
|
||||
|
||||
public function test_communication_preview_has_no_side_effect_and_send_queues_recipient(): void
|
||||
{
|
||||
$school = $this->createSchool();
|
||||
$this->actingAsSchoolUser($school);
|
||||
$student = $this->createStudent($school, 'STU-3001', 'Maya');
|
||||
$this->createTemplate($school, 'attendance_absence', 'email', 'Hello, {{ student.first_name }} was marked absent.');
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/preview", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $student,
|
||||
'channel' => 'email',
|
||||
])->assertOk()
|
||||
->assertJsonPath('data.body', 'Hello, Maya was marked absent.');
|
||||
|
||||
$this->assertDatabaseCount('communication_messages', 0);
|
||||
$this->assertDatabaseCount('communication_message_recipients', 0);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/communications/send", [
|
||||
'template_key' => 'attendance_absence',
|
||||
'student_id' => $student,
|
||||
'channel' => 'email',
|
||||
])->assertAccepted()
|
||||
->assertJsonPath('data.status', 'queued');
|
||||
|
||||
$this->assertDatabaseHas('communication_messages', [
|
||||
'school_id' => $school,
|
||||
'channel' => 'email',
|
||||
'status' => 'queued',
|
||||
]);
|
||||
$this->assertDatabaseHas('communication_message_recipients', [
|
||||
'school_id' => $school,
|
||||
'recipient_type' => 'student',
|
||||
'recipient_id' => $student,
|
||||
'status' => 'queued',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_payment_file_imports_valid_rows_and_rejects_invalid_rows_without_partial_import(): void
|
||||
{
|
||||
$school = $this->createSchool();
|
||||
$this->actingAsSchoolUser($school);
|
||||
$student = $this->createStudent($school, 'STU-4001');
|
||||
|
||||
$validFile = UploadedFile::fake()->createWithContent(
|
||||
'payments.csv',
|
||||
"student_number,amount,paid_at,reference\nSTU-4001,125.00,2026-05-29,PAY-001\n"
|
||||
);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/finance/payment-files", [
|
||||
'file' => $validFile,
|
||||
])->assertCreated()
|
||||
->assertJsonPath('data.imported_count', 1);
|
||||
|
||||
$this->assertDatabaseHas('payments', [
|
||||
'school_id' => $school,
|
||||
'student_id' => $student,
|
||||
'amount_minor' => 12500,
|
||||
'reference_number' => 'PAY-001',
|
||||
]);
|
||||
|
||||
$invalidFile = UploadedFile::fake()->createWithContent(
|
||||
'bad-payments.csv',
|
||||
"student_number,amount,paid_at,reference\nSTU-4001,80.00,2026-05-29,PAY-002\nMISSING,90.00,2026-05-29,PAY-003\n"
|
||||
);
|
||||
|
||||
$this->postJson("/api/schools/{$school}/finance/payment-files", [
|
||||
'file' => $invalidFile,
|
||||
])->assertUnprocessable();
|
||||
|
||||
$this->assertDatabaseMissing('payments', [
|
||||
'school_id' => $school,
|
||||
'reference_number' => 'PAY-002',
|
||||
]);
|
||||
$this->assertDatabaseMissing('payments', [
|
||||
'school_id' => $school,
|
||||
'reference_number' => 'PAY-003',
|
||||
]);
|
||||
}
|
||||
|
||||
public function test_daily_report_aggregates_attendance_and_finance(): void
|
||||
{
|
||||
$school = $this->createSchool();
|
||||
$this->actingAsSchoolUser($school);
|
||||
$student = $this->createStudent($school, 'STU-5001');
|
||||
|
||||
$this->postJson("/api/schools/{$school}/attendance/scans", [
|
||||
'student_number' => 'STU-5001',
|
||||
'scanned_at' => '2026-05-29T08:15:00Z',
|
||||
'source' => 'kiosk',
|
||||
])->assertCreated();
|
||||
|
||||
DB::table('payments')->insert([
|
||||
'school_id' => $school,
|
||||
'student_id' => $student,
|
||||
'amount_minor' => 12500,
|
||||
'currency' => 'USD',
|
||||
'payment_method' => 'manual',
|
||||
'payment_date' => '2026-05-29',
|
||||
'reference_number' => 'PAY-RPT-001',
|
||||
'status' => 'recorded',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$this->getJson("/api/schools/{$school}/reports/daily?date=2026-05-29")
|
||||
->assertOk()
|
||||
->assertJsonPath('data.attendance.present_count', 1)
|
||||
->assertJsonPath('data.finance.total_payments', 12500);
|
||||
}
|
||||
|
||||
private function createSchool(string $name = 'Test School', ?string $code = null): int
|
||||
{
|
||||
return (int) DB::table('schools')->insertGetId([
|
||||
'name' => $name,
|
||||
'code' => $code ?? 'SCH-'.uniqid(),
|
||||
'domain_profile' => 'standard_school',
|
||||
'timezone' => 'UTC',
|
||||
'locale' => 'en',
|
||||
'currency' => 'USD',
|
||||
'active' => true,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createStudent(int $school, string $studentNumber, string $firstName = 'Maya'): int
|
||||
{
|
||||
return (int) DB::table('students')->insertGetId([
|
||||
'school_id' => $school,
|
||||
'school_id_number' => $studentNumber,
|
||||
'student_identifier' => $studentNumber,
|
||||
'first_name' => $firstName,
|
||||
'last_name' => 'Patel',
|
||||
'status' => 'active',
|
||||
'profile_metadata' => json_encode(['grade_level' => '7'], JSON_THROW_ON_ERROR),
|
||||
'metadata' => json_encode(['grade_level' => '7'], JSON_THROW_ON_ERROR),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function createTemplate(int $school, string $key, string $channel, string $body): int
|
||||
{
|
||||
return (int) DB::table('communication_templates')->insertGetId([
|
||||
'school_id' => $school,
|
||||
'template_key' => $key,
|
||||
'version' => '1',
|
||||
'status' => 'active',
|
||||
'channel' => $channel,
|
||||
'message_category' => 'general',
|
||||
'language' => 'en',
|
||||
'subject' => 'Notice for {{ student.first_name }}',
|
||||
'body' => $body,
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
}
|
||||
|
||||
private function actingAsSchoolUser(int $school): Authenticatable
|
||||
{
|
||||
$this->ensureAuthTestTablesExist();
|
||||
|
||||
$userId = (int) DB::table('users')->insertGetId([
|
||||
'name' => 'Workflow User '.$school,
|
||||
'email' => 'workflow'.$school.'@example.test',
|
||||
'password' => bcrypt('password'),
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
DB::table('school_user')->insert([
|
||||
'school_id' => $school,
|
||||
'user_id' => $userId,
|
||||
'role' => 'admin',
|
||||
'created_at' => now(),
|
||||
'updated_at' => now(),
|
||||
]);
|
||||
|
||||
$user = new GenericUser([
|
||||
'id' => $userId,
|
||||
'name' => 'Workflow User '.$school,
|
||||
'email' => 'workflow'.$school.'@example.test',
|
||||
]);
|
||||
|
||||
$this->actingAs($user);
|
||||
|
||||
return $user;
|
||||
}
|
||||
|
||||
private function ensureAuthTestTablesExist(): void
|
||||
{
|
||||
if (! Schema::hasTable('users')) {
|
||||
Schema::create('users', function ($table): void {
|
||||
$table->id();
|
||||
$table->string('name');
|
||||
$table->string('email')->unique();
|
||||
$table->string('password');
|
||||
$table->rememberToken();
|
||||
$table->timestamps();
|
||||
});
|
||||
}
|
||||
|
||||
if (! Schema::hasTable('school_user')) {
|
||||
Schema::create('school_user', function ($table): void {
|
||||
$table->id();
|
||||
$table->unsignedBigInteger('school_id')->index();
|
||||
$table->unsignedBigInteger('user_id')->index();
|
||||
$table->string('role')->default('admin');
|
||||
$table->timestamps();
|
||||
|
||||
$table->unique(['school_id', 'user_id']);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user