CaiWuXiTong/tests/Unit/EcommerceFinance/FollowUpstreamColumnsTest.php
renshen 401dfb089a 店铺账号密码:上游新增字段自动跟随(镜像补列 + 页面自动多出这一列)
新增 business-mirror:follow-upstream-columns:先把数据字典刷新到最新,再把上游
视图里新增、当前数据集还没同步的字段追加进镜像定义并立刻建列,下一次常规同步
就会带回数据。只做新增,不删除/不改写已有字段;字段名带 password/secret/token/
cipher/credential 等敏感关键字的一律跳过。已登记到发布 cron(每天 06:40)。

页面侧:config/upstream_mirrors.php 的 auto_follow_columns 用「页面 => 数据集」
登记,FollowedUpstreamColumns 服务把 followed_columns 变成表格列;这类列只展示、
不生成查询条件(后端还没有对应筛选口径)。

数据集自带的数据变动仍然由每分钟的 business-mirror:sync-tables 增量同步。
2026-10-08 13:19:53 +08:00

99 lines
3.6 KiB
PHP

<?php
namespace Tests\Unit\EcommerceFinance;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Schema;
use Tests\TestCase;
class FollowUpstreamColumnsTest extends TestCase
{
private const DATASET = 'demo.accounts';
protected function setUp(): void
{
parent::setUp();
config([
'database.connections.business_mirror' => [
'driver' => 'sqlite', 'database' => ':memory:', 'prefix' => '',
],
'upstream_mirrors.connection' => 'business_mirror',
'upstream_mirrors.datasets' => [],
'upstream_mirrors.auto_follow_columns' => ['demo/page' => self::DATASET],
]);
DB::purge('business_mirror');
DB::table('business_mirror_datasets')->insert([
'dataset_key' => self::DATASET,
'source_key' => 'demo',
'definition' => json_encode([
'source_key' => 'demo', 'schema' => 'external', 'object' => 'accounts_v1',
'target' => 'demo_accounts', 'keys' => ['id'], 'columns' => ['id', 'name'],
], JSON_THROW_ON_ERROR),
'sync_enabled' => true,
'initial_sync_approved' => true,
'created_at' => now(),
'updated_at' => now(),
]);
$this->insertCatalogColumn('id', 1);
$this->insertCatalogColumn('name', 2);
}
private function insertCatalogColumn(string $column, int $position): void
{
DB::table('upstream_schema_columns')->insert([
'source_key' => 'demo',
'schema_name' => 'external',
'object_name' => 'accounts_v1',
'object_type' => 'view',
'column_name' => $column,
'ordinal_position' => $position,
'data_type' => 'varchar',
'max_length' => 191,
'synchronized_at' => now(),
]);
}
public function test_it_adds_new_upstream_columns_and_skips_sensitive_ones(): void
{
$this->insertCatalogColumn('platform', 3);
$this->insertCatalogColumn('passwordCiphertext', 4);
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
$definition = json_decode(
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
true,
);
$this->assertSame(['id', 'name', 'platform'], $definition['columns']);
$this->assertSame(['platform'], $definition['followed_columns']);
$this->assertTrue(Schema::connection('business_mirror')->hasColumn('demo_accounts', 'platform'));
$this->assertFalse(Schema::connection('business_mirror')->hasColumn('demo_accounts', 'passwordCiphertext'));
}
public function test_it_is_idempotent_when_there_is_nothing_new(): void
{
$this->insertCatalogColumn('platform', 3);
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
$definition = json_decode(
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
true,
);
$this->assertSame(['id', 'name', 'platform'], $definition['columns']);
// 再跑一次不会重复追加,也不会重复记录。
$this->artisan('business-mirror:follow-upstream-columns')->assertSuccessful();
$again = json_decode(
(string) DB::table('business_mirror_datasets')->where('dataset_key', self::DATASET)->value('definition'),
true,
);
$this->assertSame(['id', 'name', 'platform'], $again['columns']);
$this->assertSame(['platform'], $again['followed_columns']);
}
}