create(['role' => 'user']); $this->withToken(JWTAuth::fromUser($user)) ->getJson('/api/admin/banks') ->assertForbidden() ->assertJsonPath('message', '权限不足'); } public function test_teacher_only_lists_owned_questions(): void { $owner = User::factory()->create(['role' => 'teacher']); $otherTeacher = User::factory()->create(['role' => 'teacher']); $ownedBank = $this->bankFor($owner, '自己的题库'); $otherBank = $this->bankFor($otherTeacher, '别人的题库'); app(QuestionImportService::class)->importJsonText($ownedBank, $owner, file_get_contents(base_path('question.json'))); app(QuestionImportService::class)->importJsonText($otherBank, $otherTeacher, file_get_contents(base_path('question.json'))); $response = $this->withToken(JWTAuth::fromUser($owner)) ->getJson('/api/admin/questions') ->assertOk(); $bankIds = collect($response->json('data.items'))->pluck('question_bank_id')->unique()->values()->all(); $this->assertSame([$ownedBank->id], $bankIds); } public function test_teacher_cannot_import_into_another_teachers_bank(): void { $owner = User::factory()->create(['role' => 'teacher']); $otherTeacher = User::factory()->create(['role' => 'teacher']); $otherBank = $this->bankFor($otherTeacher, '别人的题库'); $this->withToken(JWTAuth::fromUser($owner)) ->postJson('/api/admin/questions', [ 'question_bank_id' => $otherBank->id, 'content' => '不能越权新增', 'type' => 'single', 'options' => [ ['text' => 'A', 'correct' => true], ['text' => 'B', 'correct' => false], ], ]) ->assertForbidden(); } public function test_teacher_without_user_permission_cannot_manage_users(): void { $teacher = User::factory()->create(['role' => 'teacher']); $this->withToken(JWTAuth::fromUser($teacher)) ->getJson('/api/admin/users') ->assertForbidden() ->assertJsonPath('message', '权限不足'); } public function test_admin_can_update_user_status_and_password(): void { $admin = User::query()->where('role', 'admin')->firstOrFail(); $target = User::factory()->create(['role' => 'user', 'is_active' => true]); $this->withToken(JWTAuth::fromUser($admin)) ->putJson("/api/admin/users/{$target->id}", [ 'name' => '新姓名', 'role' => 'teacher', 'is_active' => false, 'password' => 'newpass123', ]) ->assertOk() ->assertJsonPath('data.name', '新姓名') ->assertJsonPath('data.role', 'teacher') ->assertJsonPath('data.is_active', false); $this->assertDatabaseHas('users', [ 'id' => $target->id, 'name' => '新姓名', 'role' => 'teacher', 'is_active' => false, ]); } public function test_admin_can_read_and_sync_role_permissions(): void { $admin = User::query()->where('role', 'admin')->firstOrFail(); $permission = Permission::query()->where('code', 'banks')->firstOrFail(); $token = JWTAuth::fromUser($admin); $this->withToken($token) ->getJson('/api/admin/permissions') ->assertOk() ->assertJsonStructure(['data' => ['permissions', 'role_permissions']]); $this->withToken($token) ->putJson('/api/admin/roles/user/permissions', [ 'permission_ids' => [$permission->id], ]) ->assertOk(); $this->assertDatabaseHas('role_permissions', [ 'role' => 'user', 'permission_id' => $permission->id, ]); $this->assertDatabaseHas('operation_logs', [ 'action' => 'role.permissions_updated', 'target_type' => 'role', ]); } public function test_teacher_can_view_and_update_owned_paper_questions(): void { $teacher = User::factory()->create(['role' => 'teacher']); $bank = $this->bankFor($teacher, '试卷题库'); app(QuestionImportService::class)->importJsonText($bank, $teacher, file_get_contents(base_path('question.json'))); $questions = $bank->questions()->take(2)->get(); $paper = Paper::create([ 'owner_id' => $teacher->id, 'question_bank_id' => $bank->id, 'title' => '原试卷', 'is_active' => true, ]); $paper->questions()->attach($questions[0]->id, ['score' => 3, 'sort' => 0]); $token = JWTAuth::fromUser($teacher); $this->withToken($token) ->getJson("/api/admin/papers/{$paper->id}") ->assertOk() ->assertJsonPath('data.questions.0.id', $questions[0]->id) ->assertJsonPath('data.questions.0.pivot.score', '3.00'); $this->withToken($token) ->putJson("/api/admin/papers/{$paper->id}", [ 'title' => '更新试卷', 'is_active' => false, 'questions' => [ ['id' => $questions[1]->id, 'score' => 4], ], ]) ->assertOk() ->assertJsonPath('data.title', '更新试卷') ->assertJsonPath('data.is_active', false) ->assertJsonPath('data.questions_count', 1); $this->assertDatabaseHas('paper_questions', [ 'paper_id' => $paper->id, 'question_id' => $questions[1]->id, 'score' => 4, ]); $this->assertDatabaseMissing('paper_questions', [ 'paper_id' => $paper->id, 'question_id' => $questions[0]->id, ]); } private function bankFor(User $user, string $name): QuestionBank { return QuestionBank::create([ 'owner_id' => $user->id, 'name' => $name, 'visibility' => 'private', 'is_active' => true, ]); } }