You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
293 lines
11 KiB
293 lines
11 KiB
|
2 days ago
|
<?php
|
||
|
|
|
||
|
|
namespace Tests\Feature;
|
||
|
|
|
||
|
|
use App\Models\Admin;
|
||
|
|
use App\Models\User;
|
||
|
|
use Illuminate\Database\Schema\Blueprint;
|
||
|
|
use Illuminate\Support\Facades\Auth;
|
||
|
|
use Illuminate\Support\Facades\DB;
|
||
|
|
use Illuminate\Support\Facades\Schema;
|
||
|
|
use Tests\TestCase;
|
||
|
|
|
||
|
|
class MobileCoursePublicDataTest extends TestCase
|
||
|
|
{
|
||
|
|
protected function setUp(): void
|
||
|
|
{
|
||
|
|
parent::setUp();
|
||
|
|
|
||
|
|
config([
|
||
|
|
'database.default' => 'sqlite',
|
||
|
|
'database.connections.sqlite.database' => ':memory:',
|
||
|
|
]);
|
||
|
|
DB::purge('sqlite');
|
||
|
|
$this->createSchema();
|
||
|
|
$this->seedCourseData();
|
||
|
|
}
|
||
|
|
|
||
|
|
protected function tearDown(): void
|
||
|
|
{
|
||
|
|
Auth::guard('mobile')->forgetUser();
|
||
|
|
Auth::guard('admin')->forgetUser();
|
||
|
|
DB::disconnect('sqlite');
|
||
|
|
|
||
|
|
parent::tearDown();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_anonymous_user_sees_only_published_public_courses(): void
|
||
|
|
{
|
||
|
|
$response = $this->getJson('/api/mobile/course/course');
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$data = $response->json('data');
|
||
|
|
$this->assertNoSensitiveKeys($response->json());
|
||
|
|
|
||
|
|
$this->assertCount(1, $data);
|
||
|
|
$this->assertSame(150, $data[0]['id']);
|
||
|
|
$this->assertSame('张老师', $data[0]['teacher']['name']);
|
||
|
|
$this->assertArrayNotHasKey('mobile', $data[0]['teacher']);
|
||
|
|
$this->assertArrayNotHasKey('admin_id', $data[0]);
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_regular_mobile_user_cannot_read_unpublished_course_details(): void
|
||
|
|
{
|
||
|
|
$user = new User();
|
||
|
|
$user->forceFill(['id' => 200]);
|
||
|
|
Auth::guard('mobile')->setUser($user);
|
||
|
|
|
||
|
|
$response = $this->getJson('/api/mobile/course/course-detail?course_id=151');
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_registered_mobile_user_still_receives_safe_course_detail(): void
|
||
|
|
{
|
||
|
|
$user = new User();
|
||
|
|
$user->forceFill(['id' => 201]);
|
||
|
|
Auth::guard('mobile')->setUser($user);
|
||
|
|
DB::table('course_signs')->insert([
|
||
|
|
'id' => 1,
|
||
|
|
'course_id' => 150,
|
||
|
|
'user_id' => 201,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$response = $this->getJson('/api/mobile/course/course-detail?course_id=150');
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$this->assertNoSensitiveKeys($response->json());
|
||
|
|
$this->assertSame('公开课程', $response->json('name'));
|
||
|
|
$this->assertSame('张老师', $response->json('teacher.name'));
|
||
|
|
$this->assertStringEndsWith('/course/public.jpg', $response->json('publicize.0.url'));
|
||
|
|
$this->assertStringEndsWith('/course/group.jpg', $response->json('qun_image.url'));
|
||
|
|
$this->assertArrayNotHasKey('folder', $response->json('publicize.0'));
|
||
|
|
$this->assertArrayNotHasKey('name', $response->json('qun_image'));
|
||
|
|
$this->assertArrayNotHasKey('mobile', $response->json('teacher'));
|
||
|
|
$this->assertArrayNotHasKey('admin_id', $response->json());
|
||
|
|
$this->assertArrayNotHasKey('department_id', $response->json());
|
||
|
|
$this->assertArrayNotHasKey('deleted_at', $response->json());
|
||
|
|
$this->assertArrayNotHasKey('admin_id', $response->json('course_forms.0'));
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_admin_user_does_not_expand_public_mobile_response(): void
|
||
|
|
{
|
||
|
|
$admin = new Admin();
|
||
|
|
$admin->forceFill(['id' => 1]);
|
||
|
|
Auth::guard('admin')->setUser($admin);
|
||
|
|
|
||
|
|
$response = $this->getJson('/api/mobile/course/course-detail-pc?course_id=150');
|
||
|
|
|
||
|
|
$response->assertOk();
|
||
|
|
$this->assertNoSensitiveKeys($response->json());
|
||
|
|
$this->assertArrayNotHasKey('mobile', $response->json('teacher'));
|
||
|
|
$this->assertArrayNotHasKey('admin_id', $response->json());
|
||
|
|
$this->assertArrayNotHasKey('teacher_id', $response->json());
|
||
|
|
}
|
||
|
|
|
||
|
|
public function test_deleted_virtual_and_not_started_courses_are_not_publicly_addressable(): void
|
||
|
|
{
|
||
|
|
foreach ([152, 153, 154] as $courseId) {
|
||
|
|
$response = $this->getJson('/api/mobile/course/course-detail?course_id=' . $courseId);
|
||
|
|
|
||
|
|
$response->assertNotFound();
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private function createSchema(): void
|
||
|
|
{
|
||
|
|
Schema::create('courses', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->integer('admin_id')->nullable();
|
||
|
|
$table->integer('department_id')->nullable();
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->integer('image_id')->nullable();
|
||
|
|
$table->integer('qun_image_id')->nullable();
|
||
|
|
$table->date('start_date')->nullable();
|
||
|
|
$table->date('end_date')->nullable();
|
||
|
|
$table->integer('type')->nullable();
|
||
|
|
$table->text('content')->nullable();
|
||
|
|
$table->text('publicize_content')->nullable();
|
||
|
|
$table->json('publicize_ids')->nullable();
|
||
|
|
$table->tinyInteger('is_fee')->nullable();
|
||
|
|
$table->integer('total')->nullable();
|
||
|
|
$table->string('url')->nullable();
|
||
|
|
$table->string('url_title')->nullable();
|
||
|
|
$table->integer('teacher_id')->nullable();
|
||
|
|
$table->tinyInteger('status')->default(0);
|
||
|
|
$table->tinyInteger('course_status')->default(0);
|
||
|
|
$table->tinyInteger('sign_status')->default(0);
|
||
|
|
$table->boolean('is_virtual')->default(0);
|
||
|
|
$table->boolean('show_txl')->default(1);
|
||
|
|
$table->boolean('show_mobile')->default(1);
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('teachers', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->string('mobile')->nullable();
|
||
|
|
$table->text('introduce')->nullable();
|
||
|
|
$table->string('remark')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('course_types', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->string('wait_tip')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('course_forms', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->integer('course_id')->nullable();
|
||
|
|
$table->integer('admin_id')->nullable();
|
||
|
|
$table->integer('department_id')->nullable();
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->string('field')->nullable();
|
||
|
|
$table->string('edit_input')->nullable();
|
||
|
|
$table->string('rule')->nullable();
|
||
|
|
$table->integer('sort')->default(1);
|
||
|
|
$table->string('help')->nullable();
|
||
|
|
$table->text('select_item')->nullable();
|
||
|
|
$table->boolean('need_fill')->default(0);
|
||
|
|
$table->boolean('belong_user')->default(0);
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('uploads', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->string('folder')->nullable();
|
||
|
|
$table->string('name')->nullable();
|
||
|
|
$table->string('original_name')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
});
|
||
|
|
|
||
|
|
DB::table('uploads')->insert([
|
||
|
|
['id' => 21, 'folder' => 'course', 'name' => 'public.jpg'],
|
||
|
|
['id' => 22, 'folder' => 'course', 'name' => 'group.jpg'],
|
||
|
|
]);
|
||
|
|
|
||
|
|
Schema::create('course_signs', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->integer('course_id')->nullable();
|
||
|
|
$table->integer('user_id')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('course_content_evaluations', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->integer('course_id')->nullable();
|
||
|
|
$table->string('course_content_id')->nullable();
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
|
||
|
|
Schema::create('course_content_evaluation_asks', function (Blueprint $table) {
|
||
|
|
$table->increments('id');
|
||
|
|
$table->integer('course_content_evaluation_id')->nullable();
|
||
|
|
$table->integer('course_content_id')->nullable();
|
||
|
|
$table->integer('sort')->default(1);
|
||
|
|
$table->timestamps();
|
||
|
|
$table->softDeletes();
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
private function seedCourseData(): void
|
||
|
|
{
|
||
|
|
DB::table('teachers')->insert([
|
||
|
|
'id' => 11,
|
||
|
|
'name' => '张老师',
|
||
|
|
'mobile' => '13800138000',
|
||
|
|
'introduce' => '公开介绍',
|
||
|
|
'remark' => '内部备注',
|
||
|
|
]);
|
||
|
|
DB::table('course_types')->insert(['id' => 3, 'name' => '管理类']);
|
||
|
|
DB::table('course_forms')->insert([
|
||
|
|
'id' => 1,
|
||
|
|
'course_id' => 150,
|
||
|
|
'admin_id' => 9,
|
||
|
|
'department_id' => 10,
|
||
|
|
'name' => '姓名',
|
||
|
|
'field' => 'name',
|
||
|
|
'edit_input' => 'input',
|
||
|
|
'rule' => 'required',
|
||
|
|
'sort' => 1,
|
||
|
|
'help' => '请填写姓名',
|
||
|
|
'need_fill' => 1,
|
||
|
|
'belong_user' => 1,
|
||
|
|
]);
|
||
|
|
|
||
|
|
$base = [
|
||
|
|
'name' => '公开课程',
|
||
|
|
'start_date' => '2026-08-01',
|
||
|
|
'end_date' => '2026-08-02',
|
||
|
|
'type' => 3,
|
||
|
|
'content' => '公开介绍',
|
||
|
|
'publicize_content' => '宣传介绍',
|
||
|
|
'teacher_id' => 11,
|
||
|
|
'status' => 1,
|
||
|
|
'course_status' => 20,
|
||
|
|
'sign_status' => 10,
|
||
|
|
'is_virtual' => 0,
|
||
|
|
'show_txl' => 1,
|
||
|
|
'show_mobile' => 1,
|
||
|
|
'is_fee' => 0,
|
||
|
|
'total' => 30,
|
||
|
|
'url' => 'https://example.test/review',
|
||
|
|
'url_title' => '精彩回顾',
|
||
|
|
'publicize_ids' => json_encode([21]),
|
||
|
|
'qun_image_id' => 22,
|
||
|
|
'deleted_at' => null,
|
||
|
|
];
|
||
|
|
|
||
|
|
DB::table('courses')->insert([
|
||
|
|
array_merge($base, ['id' => 150]),
|
||
|
|
array_merge($base, ['id' => 151, 'status' => 0]),
|
||
|
|
array_merge($base, ['id' => 152, 'is_virtual' => 1]),
|
||
|
|
array_merge($base, ['id' => 153, 'sign_status' => 30]),
|
||
|
|
array_merge($base, ['id' => 154, 'deleted_at' => now()]),
|
||
|
|
]);
|
||
|
|
}
|
||
|
|
|
||
|
|
private function assertNoSensitiveKeys($value): void
|
||
|
|
{
|
||
|
|
$forbidden = [
|
||
|
|
'mobile', 'idcard', 'id_card', 'identity_card', 'remark',
|
||
|
|
'admin_id', 'department_id', 'teacher_id', 'deleted_at',
|
||
|
|
'password', 'remember_token',
|
||
|
|
];
|
||
|
|
|
||
|
|
if (is_array($value)) {
|
||
|
|
foreach ($value as $key => $item) {
|
||
|
|
$this->assertNotContains(strtolower((string) $key), $forbidden);
|
||
|
|
$this->assertNoSensitiveKeys($item);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|