84 lines
4.1 KiB
PHP
84 lines
4.1 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
use app\adminapi\logic\firstvisit\FirstVisitConversionLogic;
|
|
use app\adminapi\logic\stats\ConversionLogic;
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
function conversionFanDetailExpect(bool $condition, string $message): void
|
|
{
|
|
if (!$condition) {
|
|
throw new RuntimeException($message);
|
|
}
|
|
}
|
|
|
|
$firstVisitReflection = new ReflectionClass(FirstVisitConversionLogic::class);
|
|
|
|
$conversionSource = file_get_contents((new ReflectionClass(ConversionLogic::class))->getFileName());
|
|
conversionFanDetailExpect(is_string($conversionSource), 'Unable to read ConversionLogic source');
|
|
$buildDetailRows = (new ReflectionClass(ConversionLogic::class))->getMethod('buildFanDetailRows');
|
|
$buildDetailRows->setAccessible(true);
|
|
$detailRows = $buildDetailRows->invoke(null, [
|
|
['user_id' => 'staff-a', 'external_userid' => 'readded', 'add_time' => 100],
|
|
['user_id' => 'staff-a', 'external_userid' => 'readded', 'add_time' => 200],
|
|
['user_id' => 'staff-b', 'external_userid' => 'deleted', 'add_time' => 300],
|
|
], [
|
|
['user_id' => 'staff-a', 'external_userid' => 'readded', 'add_time' => 200],
|
|
]);
|
|
conversionFanDetailExpect(
|
|
count($detailRows) === 2
|
|
&& (int) ($detailRows[0]['add_time'] ?? 0) === 100
|
|
&& empty($detailRows[0]['is_deleted'])
|
|
&& !empty($detailRows[1]['is_deleted']),
|
|
'Delete/re-add details must retain the first counted add time and final deletion state'
|
|
);
|
|
conversionFanDetailExpect(
|
|
str_contains($conversionSource, 'self::loadFanDetailRows($startTimestamp, $endTimestamp, $mediaChannel, $adminIds)')
|
|
&& str_contains($conversionSource, 'MIN(e.event_time) AS add_time')
|
|
&& str_contains($conversionSource, "->where('e.event_time', 'between', [\$startTimestamp, \$endTimestamp])")
|
|
&& str_contains($conversionSource, 'EXISTS (SELECT 1 FROM `')
|
|
&& str_contains($conversionSource, 'MAX(event_time) AS delete_time'),
|
|
'Aggregate and detail results must share the same distinct fan-pair loader and expose event times'
|
|
);
|
|
conversionFanDetailExpect(
|
|
str_contains($conversionSource, "'external_userid' => \$externalUserId")
|
|
&& str_contains($conversionSource, "'customer_name'")
|
|
&& str_contains($conversionSource, "'wecom_staff_name'")
|
|
&& str_contains($conversionSource, "'is_deleted'")
|
|
&& str_contains($conversionSource, "'delete_time'"),
|
|
'Fan detail response must expose customer, employee, add and deletion fields'
|
|
);
|
|
|
|
$firstVisitSource = file_get_contents($firstVisitReflection->getFileName());
|
|
conversionFanDetailExpect(
|
|
is_string($firstVisitSource)
|
|
&& !str_contains($firstVisitSource, '$dashboard = self::overview($params, $adminId, $adminInfo);')
|
|
&& str_contains($firstVisitSource, 'resolveFanDetailEntity(')
|
|
&& str_contains($firstVisitSource, 'resolveFanDetailMemberDeptId(')
|
|
&& str_contains($firstVisitSource, "['channel_dept_ids']")
|
|
&& str_contains($firstVisitSource, "preg_match('/^M([1-9]\\d*)_(-?\\d+)$/', \$entityId")
|
|
&& str_contains($firstVisitSource, "MediaChannelService::getCurrentTagChannelByCode"),
|
|
'Fan detail must authorize the exact structural entity without recomputing overview and retain current-tag channel scope'
|
|
);
|
|
conversionFanDetailExpect(
|
|
str_contains($conversionSource, '$targetWorkWechatUserIds')
|
|
&& str_contains($conversionSource, 'fanDetailChannelDeptIds($mediaChannel)')
|
|
&& str_contains($conversionSource, 'filterEntitiesByChannelDeptScope(')
|
|
&& str_contains($conversionSource, 'NOT EXISTS (SELECT 1 FROM `')
|
|
&& str_contains($conversionSource, "':wecom:'")
|
|
&& str_contains($conversionSource, '$matched = $pairs;'),
|
|
'Fan detail must narrow the event query to the clicked employees/WeCom user before sorting and pagination'
|
|
);
|
|
|
|
$controllerSource = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/firstvisit/ConversionController.php');
|
|
conversionFanDetailExpect(
|
|
is_string($controllerSource)
|
|
&& str_contains($controllerSource, 'public function fansDetail()')
|
|
&& str_contains($controllerSource, 'FirstVisitConversionLogic::fansDetail('),
|
|
'First-visit conversion controller must expose the fansDetail endpoint'
|
|
);
|
|
|
|
echo "FirstVisitConversionFanDetailTest passed\n";
|