mirror of
https://github.com/kuaifan/dootask.git
synced 2026-06-25 16:52:20 +00:00
- composer: framework ^13.0、php ^8.3、laravel-s ~3.8.0、predis ^2.3、 phpunit ^11.5、tinker ^3、excel ^3.1.69、captcha ^3.5、avatar ^6.5、 ldaprecord-laravel ^4、pinyin ^5.3、notify 锁 ~1.28.0; 移除 fideloper/proxy、fruitcake/laravel-cors、facade/ignition、 laravel/sail、madnest/madzipper、手动钉的 symfony/mailer; symfony/console 锁 ^7.4(LaravelS Portal 与 console 8 的 configure(): void 类型断言不兼容) - $dates 移除:AbstractModel 改 getCasts() 合并默认 datetime 列, 3 个子模型改 $casts - Carbon 3:4 处 diffInSeconds 补 absolute 参数并取整 - LdapRecord v4:config use_ssl/use_tls→use_tls/use_starttls(env 变量名不变), LdapUser::$objectClasses 补类型声明 - Madzipper→原生 ZipArchive(Base::zipAddFiles,4 处调用) - pinyin v5 静态 API(Base::getFirstCharter/cn2pinyin) - laravolt/avatar 6.5:PatchedAvatar 修上游纵向对齐 bug (intervention 4.1.3 枚举无 middle),avatar 响应改 response()->file() - TrustProxies 改框架内置基类,CORS 改 Illuminate\Http\Middleware\HandleCors - Symfony Console 8 兼容:ManticoreSyncLock::handleSignal 新签名, pcntl 回调解耦 - 非 Swoole 运行时守卫:AbstractTask::task / PushTask::push / AbstractData(swoole table),artisan/测试上下文不再炸 Target class [swoole] does not exist - Laravel 11+ change() 丢修饰符:2023_12_07 与 2025_08_10 迁移重申 nullable/default/comment(修复 fresh 安装) - Setting/Ihttp 缺键访问加 ?? 守卫(PHP 8 警告在测试中转异常) - phpunit.xml 迁移 11 schema;UserImportParseTest 改为自建部门数据 验证:8.4 容器内 migrate:fresh --seed 213 全过;php artisan test 145 passed/1 skipped;LaravelS(Swoole 6.2.1) /health 200、登录、 token 认证、WebSocket 握手、Task 投递、头像、图片裁剪冒烟全过 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
148 lines
6.1 KiB
PHP
148 lines
6.1 KiB
PHP
<?php
|
||
|
||
namespace Tests\Unit;
|
||
|
||
use App\Models\User;
|
||
use App\Models\UserDepartment;
|
||
use Illuminate\Foundation\Testing\DatabaseTransactions;
|
||
use Tests\TestCase;
|
||
|
||
class UserImportParseTest extends TestCase
|
||
{
|
||
use DatabaseTransactions;
|
||
|
||
public function test_parse_skips_header_and_empty_rows()
|
||
{
|
||
$sheet = [
|
||
['邮箱', '昵称', '初始密码'], // 表头,应跳过
|
||
['a@test.local', '张三', 'Abc123456'],
|
||
['', '', ''], // 空行,应跳过
|
||
['b@test.local', '李四', 'Xyz123456'],
|
||
];
|
||
|
||
$rows = User::parseImportRows($sheet);
|
||
|
||
$this->assertCount(2, $rows);
|
||
$this->assertSame('a@test.local', $rows[0]['email']);
|
||
$this->assertSame('张三', $rows[0]['nickname']);
|
||
$this->assertSame('Abc123456', $rows[0]['password']);
|
||
$this->assertSame(2, $rows[0]['line']);
|
||
$this->assertSame(4, $rows[1]['line']);
|
||
}
|
||
|
||
public function test_parse_trims_cells()
|
||
{
|
||
$sheet = [
|
||
['邮箱', '昵称', '初始密码'],
|
||
[' a@test.local ', ' 张三 ', ' Abc123456 '],
|
||
];
|
||
|
||
$rows = User::parseImportRows($sheet);
|
||
|
||
$this->assertSame('a@test.local', $rows[0]['email']);
|
||
$this->assertSame('张三', $rows[0]['nickname']);
|
||
$this->assertSame('Abc123456', $rows[0]['password']);
|
||
}
|
||
|
||
public function test_validate_passes_for_valid_row()
|
||
{
|
||
$row = ['email' => 'ok@test.local', 'nickname' => '张三', 'password' => 'Abc123456'];
|
||
$this->assertNull(User::validateImportRow($row));
|
||
}
|
||
|
||
public function test_validate_requires_all_fields()
|
||
{
|
||
$this->assertSame('邮箱、昵称、初始密码均为必填', User::validateImportRow(['email' => '', 'nickname' => '张三', 'password' => 'Abc123456']));
|
||
$this->assertSame('邮箱、昵称、初始密码均为必填', User::validateImportRow(['email' => 'a@test.local', 'nickname' => '', 'password' => 'Abc123456']));
|
||
$this->assertSame('邮箱、昵称、初始密码均为必填', User::validateImportRow(['email' => 'a@test.local', 'nickname' => '张三', 'password' => '']));
|
||
}
|
||
|
||
public function test_validate_rejects_bad_email()
|
||
{
|
||
$this->assertSame('邮箱格式不正确', User::validateImportRow(['email' => 'not-an-email', 'nickname' => '张三', 'password' => 'Abc123456']));
|
||
}
|
||
|
||
public function test_validate_rejects_bad_nickname_length()
|
||
{
|
||
$this->assertSame('昵称需为2-20个字', User::validateImportRow(['email' => 'a@test.local', 'nickname' => '王', 'password' => 'Abc123456']));
|
||
$this->assertSame('昵称需为2-20个字', User::validateImportRow(['email' => 'a@test.local', 'nickname' => str_repeat('字', 21), 'password' => 'Abc123456']));
|
||
}
|
||
|
||
public function test_validate_rejects_short_password()
|
||
{
|
||
$this->assertNotNull(User::validateImportRow(['email' => 'a@test.local', 'nickname' => '张三', 'password' => '123']));
|
||
}
|
||
|
||
public function test_assert_valid_profession_passes_for_empty_and_normal()
|
||
{
|
||
// 空职位允许(可选字段),2/20 字边界与正常值允许;不抛异常即通过
|
||
User::assertValidProfession('');
|
||
User::assertValidProfession('工程'); // 恰好 2 字
|
||
User::assertValidProfession('工程师');
|
||
User::assertValidProfession(str_repeat('字', 20)); // 恰好 20 字
|
||
$this->assertTrue(true);
|
||
}
|
||
|
||
public function test_assert_valid_profession_rejects_too_short()
|
||
{
|
||
$this->expectException(\App\Exceptions\ApiException::class);
|
||
$this->expectExceptionMessage('职位/职称不可以少于2个字');
|
||
User::assertValidProfession('A');
|
||
}
|
||
|
||
public function test_assert_valid_profession_rejects_too_long()
|
||
{
|
||
$this->expectException(\App\Exceptions\ApiException::class);
|
||
$this->expectExceptionMessage('职位/职称最多只能设置20个字');
|
||
User::assertValidProfession(str_repeat('字', 21));
|
||
}
|
||
|
||
public function test_assert_valid_departments_normalizes_ids()
|
||
{
|
||
// 空/非数组 → 返回空数组
|
||
$this->assertSame([], User::assertValidDepartments([]));
|
||
$this->assertSame([], User::assertValidDepartments('not-array'));
|
||
// 去重 + 转 int + 过滤非正数(存在性校验会查库,需用真实部门 ID)
|
||
$deptA = UserDepartment::createInstance(['name' => 'ImportParseDeptA_' . uniqid()]);
|
||
$deptA->save();
|
||
$deptB = UserDepartment::createInstance(['name' => 'ImportParseDeptB_' . uniqid()]);
|
||
$deptB->save();
|
||
$a = $deptA->id;
|
||
$b = $deptB->id;
|
||
$this->assertSame([$a, $b], User::assertValidDepartments([(string)$a, $a, $b, 0, -1]));
|
||
}
|
||
|
||
public function test_assert_valid_departments_rejects_over_limit()
|
||
{
|
||
// 超过 10 个(count 校验在查库之前)→ 抛异常
|
||
$this->expectException(\App\Exceptions\ApiException::class);
|
||
$this->expectExceptionMessage('最多只可加入10个部门');
|
||
User::assertValidDepartments(range(1, 11));
|
||
}
|
||
|
||
public function test_parse_reads_profession_column()
|
||
{
|
||
$sheet = [
|
||
['邮箱', '昵称', '初始密码', '职位'],
|
||
['a@test.local', '张三', 'Abc123456', '工程师'],
|
||
['b@test.local', '李四', 'Xyz123456'], // 无职位列 → profession 为空
|
||
];
|
||
$rows = User::parseImportRows($sheet);
|
||
$this->assertCount(2, $rows);
|
||
$this->assertSame('工程师', $rows[0]['profession']);
|
||
$this->assertSame('', $rows[1]['profession']);
|
||
}
|
||
|
||
public function test_validate_passes_for_empty_profession()
|
||
{
|
||
$row = ['email' => 'a@test.local', 'nickname' => '张三', 'password' => 'Abc123456', 'profession' => ''];
|
||
$this->assertNull(User::validateImportRow($row));
|
||
}
|
||
|
||
public function test_validate_rejects_bad_profession()
|
||
{
|
||
$row = ['email' => 'a@test.local', 'nickname' => '张三', 'password' => 'Abc123456', 'profession' => 'A'];
|
||
$this->assertSame('职位/职称不可以少于2个字', User::validateImportRow($row));
|
||
}
|
||
}
|