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.
wx.sstbc.com/tests/Feature/SchoolmateEnrollmentCommand...

122 lines
5.7 KiB

11 hours ago
<?php
namespace Tests\Feature;
use App\Models\User;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
class SchoolmateEnrollmentCommandTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
config(['database.default' => 'sqlite', 'database.connections.sqlite.database' => ':memory:', 'audit.enabled' => false]);
DB::purge('sqlite');
Carbon::setTestNow('2026-10-15 10:00:00');
Schema::create('users', function (Blueprint $table) {
$table->id();
$table->string('name')->default('测试学员');
$table->string('mobile')->nullable();
$table->integer('is_schoolmate')->nullable();
$table->dateTime('schoolmate_time')->nullable();
$table->timestamps();
$table->softDeletes();
});
Schema::create('courses', function (Blueprint $table) {
$table->id();
$table->string('name')->default('测试课程');
$table->date('start_date')->nullable();
$table->integer('auto_schoolmate')->default(0);
$table->softDeletes();
});
Schema::create('course_signs', function (Blueprint $table) {
$table->id();
$table->integer('course_id');
$table->integer('user_id');
$table->integer('status')->default(1);
$table->softDeletes();
});
}
protected function tearDown(): void
{
Carbon::setTestNow();
parent::tearDown();
}
private function course(int $id, ?string $date, int $auto = 0): void
{
DB::table('courses')->insert(['id' => $id, 'start_date' => $date, 'auto_schoolmate' => $auto]);
}
private function sign(int $userId, int $courseId, int $status = 1, ?string $deleted = null): void
{
DB::table('course_signs')->insert(['user_id' => $userId, 'course_id' => $courseId, 'status' => $status, 'deleted_at' => $deleted]);
}
public function test_preview_does_not_write_and_execute_is_idempotent_and_preserves_existing_schoolmates(): void
{
$this->course(1, '2025-06-01');
$this->course(2, '2024-03-01');
DB::table('users')->insert([
['id' => 1, 'is_schoolmate' => null, 'schoolmate_time' => null, 'deleted_at' => null],
['id' => 2, 'is_schoolmate' => 1, 'schoolmate_time' => '2020-01-01 00:00:00', 'deleted_at' => null],
['id' => 3, 'is_schoolmate' => 0, 'schoolmate_time' => null, 'deleted_at' => '2025-01-01 00:00:00'],
['id' => 4, 'is_schoolmate' => 0, 'schoolmate_time' => null, 'deleted_at' => null],
['id' => 5, 'is_schoolmate' => 0, 'schoolmate_time' => null, 'deleted_at' => null],
]);
foreach ([1, 2, 3] as $id) $this->sign($id, 1);
$this->sign(1, 2);
$this->sign(1, 2); // 同人重复报名只补一次
$this->sign(4, 1, 0);
$this->sign(5, 1, 1, '2025-01-01 00:00:00');
$this->artisan('schoolmate:backfill', ['course_ids' => '1,2'])->expectsOutputToContain('发现遗漏 1 人')->assertExitCode(0);
$this->assertNull(DB::table('users')->where('id', 1)->value('is_schoolmate'));
User::withoutEvents(function () {
$this->artisan('schoolmate:backfill', ['course_ids' => '1,2', '--execute' => true])->expectsOutputToContain('实际补齐 1 人')->assertExitCode(0);
});
$this->assertSame('2024-03-01 00:00:00', DB::table('users')->where('id', 1)->value('schoolmate_time'));
$this->assertSame('2020-01-01 00:00:00', DB::table('users')->where('id', 2)->value('schoolmate_time'));
foreach ([3, 4, 5] as $id) $this->assertEquals(0, DB::table('users')->where('id', $id)->value('is_schoolmate'));
$this->artisan('schoolmate:backfill', ['course_ids' => '1,2', '--execute' => true])->expectsOutputToContain('实际补齐 0 人')->assertExitCode(0);
}
public function test_automatic_enrollment_catches_history_and_late_approval_but_respects_course_switch_and_date(): void
{
$this->course(1, '2024-01-01', 1);
$this->course(2, '2026-10-16', 1);
$this->course(3, '2026-10-17', 1);
$this->course(4, '2024-01-01', 0);
foreach ([1, 2, 3, 4, 5] as $id) {
DB::table('users')->insert(['id' => $id, 'is_schoolmate' => 0]);
$this->sign($id, min($id, 4), $id === 5 ? 0 : 1);
}
User::withoutEvents(function () {
$this->artisan('auto_schoolmate')->expectsOutputToContain('共补齐 2 位')->assertExitCode(0);
// 开课后才通过审核的历史课程报名,下次任务仍能处理。
$this->sign(5, 1);
$this->artisan('auto_schoolmate')->expectsOutputToContain('共补齐 1 位')->assertExitCode(0);
});
foreach ([1, 2, 5] as $id) $this->assertEquals(1, DB::table('users')->where('id', $id)->value('is_schoolmate'));
foreach ([3, 4] as $id) $this->assertEquals(0, DB::table('users')->where('id', $id)->value('is_schoolmate'));
}
public function test_invalid_missing_deleted_or_undated_courses_abort_without_partial_updates(): void
{
$this->course(1, '2024-01-01');
$this->course(2, null);
$this->course(3, '2024-01-01');
DB::table('courses')->where('id', 3)->update(['deleted_at' => '2025-01-01']);
DB::table('users')->insert(['id' => 1, 'is_schoolmate' => 0]);
$this->sign(1, 1);
foreach (['1,0', '1,999', '1,2', '1,3'] as $ids) {
$this->artisan('schoolmate:backfill', ['course_ids' => $ids, '--execute' => true])->assertExitCode(1);
}
$this->assertEquals(0, DB::table('users')->where('id', 1)->value('is_schoolmate'));
}
}