CaiWuXiTong/tests/Feature/Console/SyncDingAttendanceTest.php

146 lines
6.1 KiB
PHP

<?php
namespace Tests\Feature\Console;
use App\Services\Auth\FinanceRoster;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Mockery;
use Modules\EcommerceFinance\Services\DingTalkAttendanceClient;
use Tests\TestCase;
class SyncDingAttendanceTest extends TestCase
{
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite',
'database' => ':memory:',
'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
'app.timezone' => 'Asia/Shanghai',
'dingtalk.attendance.enabled' => true,
]);
DB::purge('business_mirror');
Schema::connection('business_mirror')->create('business_mirror_table_states', function (Blueprint $table): void {
$table->string('dataset_key', 191)->primary();
$table->string('source_key', 64);
$table->string('source_object', 191);
$table->string('target_table', 64);
$table->string('status', 24)->default('pending');
$table->string('run_mode', 24)->nullable();
$table->json('cursor')->nullable();
$table->json('high_watermark')->nullable();
$table->dateTime('window_started_at')->nullable();
$table->dateTime('window_ends_at')->nullable();
$table->dateTime('initial_completed_at')->nullable();
$table->dateTime('last_attempted_at')->nullable();
$table->dateTime('last_succeeded_at')->nullable();
$table->unsignedBigInteger('last_upserted_rows')->default(0);
$table->unsignedBigInteger('mirror_row_count')->default(0);
$table->string('last_error_code', 120)->nullable();
$table->timestamps();
});
(require base_path('database/migrations/2026_11_06_090000_create_ding_attendance_records.php'))->up();
}
public function test_command_mirrors_leave_and_rest_attendance_records(): void
{
Carbon::setTestNow(Carbon::parse('2026-11-05 13:00:00', 'Asia/Shanghai'));
try {
$client = Mockery::mock(DingTalkAttendanceClient::class);
$client->shouldReceive('isConfigured')->once()->andReturnTrue();
$client->shouldReceive('listRecords')
->once()
->withArgs(static fn (array $userIds, Carbon $from, Carbon $to): bool =>
$userIds === ['changyi', 'yaoyao']
&& $from->toDateString() === '2026-11-04'
&& $to->toDateString() === '2026-11-04'
)
->andReturn([
'pages' => 1,
'records' => [
[
'id' => 444806944496,
'userId' => 'changyi',
'workDate' => 1793721600000,
'checkType' => 'OnDuty',
'timeResult' => 'NotSigned',
'approveTagName' => '请假',
'approveId' => 9645537212,
'procInstId' => 'proc-changyi',
'sourceType' => 'SYSTEM',
'baseCheckTime' => 1793754000000,
'userCheckTime' => 1793754000000,
],
[
'id' => 446291047276,
'userId' => 'yaoyao',
'workDate' => 1793721600000,
'checkType' => 'OffDuty',
'timeResult' => 'NotSigned',
'approveTagName' => '请假',
'approveId' => 9444644919,
'procInstId' => 'proc-yaoyao',
'sourceType' => 'SYSTEM',
'baseCheckTime' => 1793786400000,
'userCheckTime' => 1793786400000,
],
],
]);
$this->app->instance(DingTalkAttendanceClient::class, $client);
$roster = Mockery::mock(FinanceRoster::class);
$roster->shouldReceive('cachedFinanceMembers')->once()->andReturn([
['userid' => 'changyi', 'name' => '常仪'],
['userid' => 'yaoyao', 'name' => '杳杳'],
]);
$this->app->instance(FinanceRoster::class, $roster);
$this->artisan('business-mirror:sync-ding-attendance', [
'--from' => '2026-11-04',
'--to' => '2026-11-04',
])->expectsOutputToContain('写入/更新 2 条')->assertSuccessful();
$rows = DB::connection('business_mirror')
->table('ding_attendance_records')
->orderBy('record_id')
->get(['record_id', 'user_id', 'user_name', 'work_date', 'approve_tag_name'])
->map(static fn (object $row): array => (array) $row)
->all();
$this->assertSame([
[
'record_id' => '444806944496',
'user_id' => 'changyi',
'user_name' => '常仪',
'work_date' => '2026-11-04',
'approve_tag_name' => '请假',
],
[
'record_id' => '446291047276',
'user_id' => 'yaoyao',
'user_name' => '杳杳',
'work_date' => '2026-11-04',
'approve_tag_name' => '请假',
],
], $rows);
$this->assertSame(
'succeeded',
DB::connection('business_mirror')->table('business_mirror_table_states')
->where('dataset_key', 'dingtalk.attendance.finance_daily_records')
->value('status'),
);
} finally {
Carbon::setTestNow();
}
}
}