170 lines
7.2 KiB
PHP
170 lines
7.2 KiB
PHP
<?php
|
|
|
|
declare(strict_types=1);
|
|
|
|
require dirname(__DIR__) . '/vendor/autoload.php';
|
|
|
|
use app\adminapi\logic\setting\DesktopWorkstationLogic;
|
|
use app\common\service\DirectUploadService;
|
|
|
|
function desktopUpdateExpect(bool $condition, string $message): void
|
|
{
|
|
if (!$condition) {
|
|
fwrite(STDERR, "FAIL: {$message}\n");
|
|
exit(1);
|
|
}
|
|
}
|
|
|
|
desktopUpdateExpect(
|
|
DesktopWorkstationLogic::normalizeVersion('0.2') === '0.2.0',
|
|
'short versions pad to three segments'
|
|
);
|
|
desktopUpdateExpect(
|
|
DesktopWorkstationLogic::compareVersion('0.1.0', '0.2.0') < 0,
|
|
'0.1.0 is older than 0.2.0'
|
|
);
|
|
desktopUpdateExpect(
|
|
DesktopWorkstationLogic::packageKey('win32', 'amd64') === 'windows_x64',
|
|
'windows/amd64 maps to windows_x64'
|
|
);
|
|
desktopUpdateExpect(
|
|
DesktopWorkstationLogic::packageKey('darwin', 'aarch64') === 'macos_arm64',
|
|
'darwin/arm maps to macos_arm64'
|
|
);
|
|
|
|
$sha = str_repeat('a', 64);
|
|
$config = [
|
|
'enabled' => 1,
|
|
'latest_version' => '0.2.0',
|
|
'min_version' => '0.1.5',
|
|
'force_update' => 0,
|
|
'title' => '医生工作站 0.2.0',
|
|
'notes' => '稳定性更新',
|
|
'packages' => [
|
|
'windows_x64' => [
|
|
'url' => 'https://cdn.example.com/DoctorWorkstation-Setup-Windows-x64-0.2.0.exe',
|
|
'sha256' => $sha,
|
|
'size' => 123,
|
|
'filename' => 'DoctorWorkstation-Setup-Windows-x64-0.2.0.exe',
|
|
'type' => 'inno_setup',
|
|
],
|
|
],
|
|
];
|
|
|
|
$optional = DesktopWorkstationLogic::evaluate($config, '0.1.8', 'windows', 'x64');
|
|
desktopUpdateExpect($optional['has_update'] === true, 'newer published version is an update');
|
|
desktopUpdateExpect($optional['force'] === false, 'force stays off when above min version');
|
|
desktopUpdateExpect($optional['can_install'] === true, 'hashed package can be installed');
|
|
desktopUpdateExpect(
|
|
is_array($optional['package'])
|
|
&& $optional['package']['sha256'] === $sha
|
|
&& $optional['package']['type'] === 'inno_setup',
|
|
'matching Windows installer package is returned with its explicit type'
|
|
);
|
|
|
|
$forcedByMin = DesktopWorkstationLogic::evaluate($config, '0.1.0', 'windows', 'x64');
|
|
desktopUpdateExpect($forcedByMin['force'] === true, 'below min version forces upgrade');
|
|
|
|
$config['force_update'] = 1;
|
|
$forcedAll = DesktopWorkstationLogic::evaluate($config, '0.1.9', 'windows', 'x64');
|
|
desktopUpdateExpect($forcedAll['force'] === true, 'force_update blocks every older client');
|
|
|
|
$current = DesktopWorkstationLogic::evaluate($config, '0.2.0', 'windows', 'x64');
|
|
desktopUpdateExpect($current['has_update'] === false, 'current latest version is not an update');
|
|
|
|
$macosMissing = DesktopWorkstationLogic::evaluate($config, '0.1.0', 'macos', 'arm64');
|
|
desktopUpdateExpect($macosMissing['has_update'] === true, 'other platforms still see the new version');
|
|
desktopUpdateExpect($macosMissing['can_install'] === false, 'missing platform package cannot auto-install');
|
|
desktopUpdateExpect($macosMissing['force'] === false, 'force requires an installable package');
|
|
|
|
$disabled = $config;
|
|
$disabled['enabled'] = 0;
|
|
$quiet = DesktopWorkstationLogic::evaluate($disabled, '0.1.0', 'windows', 'x64');
|
|
desktopUpdateExpect($quiet['has_update'] === false, 'disabled detection never prompts');
|
|
|
|
$controller = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/setting/DesktopWorkstationController.php');
|
|
desktopUpdateExpect(is_string($controller), 'controller source is readable');
|
|
desktopUpdateExpect(
|
|
str_contains($controller, "public array \$notNeedLogin = ['check']")
|
|
&& str_contains($controller, 'public function check()')
|
|
&& str_contains($controller, 'DesktopWorkstationLogic::check('),
|
|
'check action is public and delegates to logic'
|
|
);
|
|
|
|
$adminView = file_get_contents(dirname(__DIR__, 2) . '/admin/src/views/setting/desktop_workstation/index.vue');
|
|
desktopUpdateExpect(is_string($adminView), 'admin view source is readable');
|
|
desktopUpdateExpect(
|
|
str_contains($adminView, 'setting.desktop_workstation/setConfig')
|
|
&& str_contains($adminView, 'force_update')
|
|
&& str_contains($adminView, 'inno_setup')
|
|
&& str_contains($adminView, 'type="desktop_package"')
|
|
&& str_contains($adminView, 'direct'),
|
|
'admin page saves update settings and sends installers through direct upload'
|
|
);
|
|
|
|
$directUploadReflection = new ReflectionClass(DirectUploadService::class);
|
|
$validateExtension = $directUploadReflection->getMethod('validateFileExtension');
|
|
$validateExtension->invoke(
|
|
null,
|
|
DirectUploadService::TYPE_DESKTOP_PACKAGE,
|
|
'uploads/desktop_package/7/' . date('Ymd') . '/package.exe',
|
|
'DoctorWorkstation.EXE'
|
|
);
|
|
$invalidExtensionRejected = false;
|
|
try {
|
|
$validateExtension->invoke(
|
|
null,
|
|
DirectUploadService::TYPE_DESKTOP_PACKAGE,
|
|
'uploads/desktop_package/7/' . date('Ymd') . '/package.php',
|
|
'package.php'
|
|
);
|
|
} catch (Throwable $e) {
|
|
$invalidExtensionRejected = true;
|
|
}
|
|
desktopUpdateExpect($invalidExtensionRejected, 'desktop direct upload rejects non-EXE/ZIP files');
|
|
|
|
$validateObjectKey = $directUploadReflection->getMethod('isAllowedObjectKey');
|
|
$ownedKey = 'uploads/desktop_package/7/' . date('Ymd') . '/package.exe';
|
|
desktopUpdateExpect(
|
|
$validateObjectKey->invoke(null, DirectUploadService::TYPE_DESKTOP_PACKAGE, $ownedKey, 7) === true
|
|
&& $validateObjectKey->invoke(null, DirectUploadService::TYPE_DESKTOP_PACKAGE, $ownedKey, 8) === false,
|
|
'desktop package keys are bound to the issuing admin'
|
|
);
|
|
|
|
$uploadController = file_get_contents(dirname(__DIR__) . '/app/adminapi/controller/UploadController.php');
|
|
$qcloudEngine = file_get_contents(dirname(__DIR__) . '/app/common/service/storage/engine/Qcloud.php');
|
|
$directUploadService = file_get_contents(
|
|
dirname(__DIR__) . '/app/common/service/DirectUploadService.php'
|
|
);
|
|
desktopUpdateExpect(
|
|
is_string($uploadController)
|
|
&& str_contains($uploadController, 'assertDirectUploadPermission')
|
|
&& str_contains($uploadController, 'setting.desktop_workstation/setconfig'),
|
|
'desktop package credentials and confirmation require publish permission'
|
|
);
|
|
desktopUpdateExpect(
|
|
is_string($qcloudEngine)
|
|
&& str_contains($qcloudEngine, 'bool $exactObject = false')
|
|
&& str_contains($qcloudEngine, '$exactObject ? $scope : $scope .'),
|
|
'COS credentials can be restricted to one server-issued object key'
|
|
);
|
|
desktopUpdateExpect(
|
|
is_string($directUploadService)
|
|
&& str_contains($directUploadService, "trim(\$name) !== ''")
|
|
&& str_contains($directUploadService, '$objectKey !== \'\''),
|
|
'desktop credentials remain compatible with uploaders that do not send a filename'
|
|
);
|
|
|
|
$migration = file_get_contents(
|
|
dirname(__DIR__) . '/sql/1.9.20260821/add_desktop_workstation_update_menu.sql'
|
|
);
|
|
desktopUpdateExpect(is_string($migration), 'menu migration is readable');
|
|
desktopUpdateExpect(
|
|
substr_count($migration, "WHERE `perms` = 'setting.desktop_workstation/getConfig'") >= 1
|
|
&& str_contains($migration, 'setting.desktop_workstation/setConfig')
|
|
&& str_contains($migration, 'setting/desktop_workstation/index'),
|
|
'menu registration is idempotent and points at the admin view'
|
|
);
|
|
|
|
echo "Desktop workstation update contract: OK\n";
|