CaiWuXiTong/tests/Unit/EcommerceFinance/AccountMappingResyncDispatcherTest.php

128 lines
5.9 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Modules\EcommerceFinance\Services\AccountMappingResyncDispatcher;
use Modules\EcommerceFinance\Services\AccountMappingStore;
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use ReflectionClass;
use Tests\TestCase;
class AccountMappingResyncDispatcherTest extends TestCase
{
public function test_identical_and_display_only_changes_do_not_require_receipt_rebuilds(): void
{
$rule = ['platform' => '京东', 'item' => '测试货款', 'memo' => '收入', 'basis' => '收入', 'feeMemo' => '说明'];
$this->assertFalse(AccountMappingStore::requiresReceiptResync($rule, $rule));
$this->assertFalse(AccountMappingStore::requiresReceiptResync($rule, array_replace($rule, ['feeMemo' => '更新说明'])));
$this->assertTrue(AccountMappingStore::requiresReceiptResync($rule, array_replace($rule, ['memo' => '退款'])));
$this->assertTrue(AccountMappingStore::requiresReceiptResync($rule, array_replace($rule, ['platform' => '抖音'])));
}
public function test_only_existing_platform_month_pairs_are_enqueued(): void
{
config(['upstream_pipeline.batch_worker_enabled' => true]);
$schema = Schema::connection('business_mirror');
$schema->dropIfExists('store_receipt_order_match_rows');
$schema->create('store_receipt_order_match_rows', function (Blueprint $table): void {
$table->string('platform');
$table->date('report_month');
});
if (! Schema::hasTable(AccountMappingResyncDispatcher::JOBS_TABLE)) {
Schema::create(AccountMappingResyncDispatcher::JOBS_TABLE, function (Blueprint $table): void {
$table->increments('id');
$table->string('platform');
$table->string('month');
$table->string('status');
$table->text('message')->nullable();
$table->timestamps();
});
}
DB::table(AccountMappingResyncDispatcher::JOBS_TABLE)->delete();
DB::connection('business_mirror')->table('store_receipt_order_match_rows')->insert([
['platform' => '淘系支付宝', 'report_month' => '2026-09-01'],
['platform' => '淘系微信', 'report_month' => '2026-10-01'],
]);
$this->assertSame(['jobs' => 2], app(AccountMappingResyncDispatcher::class)->dispatchFor(['淘宝']));
$this->assertSame(['2026-09'], DB::table(AccountMappingResyncDispatcher::JOBS_TABLE)->where('platform', 'taobao-alipay')->pluck('month')->all());
$this->assertSame(['2026-10'], DB::table(AccountMappingResyncDispatcher::JOBS_TABLE)->where('platform', 'taobao-wechat')->pluck('month')->all());
}
public function test_reimporting_unchanged_rules_does_not_invalidate_history(): void
{
$originalStorage = $this->app->storagePath();
$directory = sys_get_temp_dir() . '/mapping-resync-' . bin2hex(random_bytes(6));
\Illuminate\Support\Facades\File::makeDirectory($directory . '/app', 0777, true);
$this->app->useStoragePath($directory);
try {
$store = app(AccountMappingStore::class);
$store->reset();
$rows = [['platform' => '京东', 'item' => '测试资源优化项目', 'memo' => '收入']];
$this->assertSame(['京东'], $store->importRows($rows, true)['resync_platforms']);
$same = $store->importRows($rows, true);
$this->assertSame(0, $same['updated']);
$this->assertSame([], $same['resync_platforms']);
$this->assertSame([], $store->importRows([array_replace($rows[0], ['feeMemo' => '更新说明'])], true)['resync_platforms']);
$this->assertSame(['京东'], $store->importRows([array_replace($rows[0], ['basis' => AccountMappingStore::BASIS_IN])], true)['resync_platforms']);
} finally {
$this->app->useStoragePath($originalStorage);
\Illuminate\Support\Facades\File::deleteDirectory($directory);
(new \ReflectionProperty(AccountMappingStore::class, 'allMemo'))->setValue(null);
}
}
public function test_production_leaves_queue_processing_to_the_limited_batch_worker(): void
{
config(['upstream_pipeline.batch_worker_enabled' => true]);
$dispatcher = $this->recordingDispatcher();
$dispatcher->spawnWorker();
$this->assertSame(0, $dispatcher->launches);
}
public function test_local_development_still_launches_an_immediate_worker(): void
{
config(['upstream_pipeline.batch_worker_enabled' => false]);
$dispatcher = $this->recordingDispatcher();
$dispatcher->spawnWorker();
$this->assertSame(1, $dispatcher->launches);
}
private function recordingDispatcher(): AccountMappingResyncDispatcher
{
return new class extends AccountMappingResyncDispatcher {
public int $launches = 0;
protected function launchWorker(): void
{
$this->launches++;
}
};
}
public function test_all_taobao_family_platforms_trigger_the_matching_receipt_sources(): void
{
$reflection = new ReflectionClass(AccountMappingResyncDispatcher::class);
$map = $reflection->getConstant('PLATFORM_MAP');
$this->assertSame(
[['taobao-alipay', '淘系支付宝'], ['taobao-wechat', '淘系微信']],
$map['淘宝']
);
$this->assertSame(
[['taobao-alipay', '淘系支付宝'], ['taobao-wechat', '淘系微信']],
$map['天猫']
);
$this->assertSame([['taobao-alipay', '淘系支付宝']], $map['阿里巴巴']);
}
public function test_platform_without_receipt_sync_is_a_noop(): void
{
$result = app(AccountMappingResyncDispatcher::class)->dispatchFor(['唯品会JX']);
$this->assertSame(['jobs' => 0], $result);
}
}