CaiWuXiTong/tests/Unit/EcommerceFinance/DingTalkAttendanceClientTest.php

102 lines
3.6 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Illuminate\Http\Client\Request;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Cache;
use Illuminate\Support\Facades\Http;
use Modules\EcommerceFinance\Exceptions\DingTalkAttendanceException;
use Modules\EcommerceFinance\Services\DingTalkAttendanceClient;
use Tests\TestCase;
class DingTalkAttendanceClientTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
Cache::flush();
config([
'dingtalk.client_id' => 'test-client',
'dingtalk.client_secret' => 'test-secret',
'dingtalk.app_token_url' => 'https://example.test/token',
'dingtalk.timeout' => 3,
'dingtalk.attendance.page_size' => 2,
'dingtalk.attendance.max_pages' => 10,
]);
}
public function test_attendance_records_are_read_across_pages_and_deduplicated(): void
{
Http::fake(function (Request $request) {
if (str_starts_with($request->url(), 'https://example.test/token')) {
return Http::response([
'errcode' => 0,
'access_token' => 'test-token',
]);
}
$offset = (int) ($request->data()['offset'] ?? 0);
if ($offset === 0) {
return Http::response([
'errcode' => 0,
'errmsg' => 'ok',
'recordresult' => [
['id' => 1, 'userId' => 'user-a', 'workDate' => 1793721600000],
['id' => 2, 'userId' => 'user-a', 'workDate' => 1793721600000],
],
]);
}
return Http::response([
'errcode' => 0,
'errmsg' => 'ok',
'recordresult' => [
['id' => 2, 'userId' => 'user-a', 'workDate' => 1793721600000],
],
]);
});
$result = app(DingTalkAttendanceClient::class)->listRecords(
['user-a'],
Carbon::parse('2026-11-04 00:00:00', 'Asia/Shanghai'),
Carbon::parse('2026-11-04 23:59:59', 'Asia/Shanghai'),
);
$this->assertSame(2, $result['pages']);
$this->assertSame([1, 2], array_column($result['records'], 'id'));
Http::assertSentCount(3);
Http::assertSent(static fn (Request $request): bool =>
str_contains($request->url(), '/attendance/list?access_token=test-token')
&& ($request->data()['userIdList'] ?? []) === ['user-a']
&& ($request->data()['limit'] ?? null) === 2
);
}
public function test_missing_attendance_permission_has_a_stable_error_code(): void
{
Http::fake([
'https://example.test/token*' => Http::response([
'errcode' => 0,
'access_token' => 'test-token',
]),
'https://oapi.dingtalk.com/attendance/list*' => Http::response([
'errcode' => 60011,
'errmsg' => 'requiredScopes=[qyapi_attendance_isv_query_result,qyapi_get_attendance_data]',
]),
]);
try {
app(DingTalkAttendanceClient::class)->listRecords(
['user-a'],
Carbon::parse('2026-11-04 00:00:00', 'Asia/Shanghai'),
Carbon::parse('2026-11-04 23:59:59', 'Asia/Shanghai'),
);
$this->fail('缺少考勤查询权限时应抛出异常。');
} catch (DingTalkAttendanceException $exception) {
$this->assertSame('dingtalk_attendance_permission_required', $exception->errorCode);
}
}
}