174 lines
7.9 KiB
JavaScript
174 lines
7.9 KiB
JavaScript
'use strict';
|
|
|
|
const assert = require('node:assert/strict');
|
|
const fs = require('node:fs');
|
|
const path = require('node:path');
|
|
const test = require('node:test');
|
|
|
|
const root = path.resolve(__dirname, '..', '..');
|
|
const read = (file) => fs.readFileSync(path.join(root, file), 'utf8');
|
|
|
|
test('download settings section exists on both schema sides with matching defaults', () => {
|
|
const php = read('src/Admin/Schema/SchemaPermissions.php');
|
|
const vue = read('assets/admin/src/schema/sections/permissions/index.js');
|
|
|
|
// PHP side is the authoritative contract.
|
|
assert.match(php, /'download' => \[/);
|
|
assert.match(php, /'key'\s+=> 'download'/);
|
|
assert.match(php, /'option_key' => 'aksrt_download_config'/);
|
|
|
|
// Field parity: same four keys with the same defaults on both ends.
|
|
const fields = [
|
|
['enabled', 'true', 'enabled: true'],
|
|
['url_alias', "'download'", "url_alias: 'download'"],
|
|
['default_max_uses', '1', 'default_max_uses: 1'],
|
|
['ttl_minutes', '10', 'ttl_minutes: 10'],
|
|
];
|
|
for (const [key, phpDefault, vueDefault] of fields) {
|
|
const phpField = new RegExp(`'key' => '${key}'[^\\]]*'default' => ${phpDefault}`);
|
|
assert.match(php, phpField, `PHP default mismatch for ${key}`);
|
|
assert.match(vue, new RegExp(`${vueDefault}`), `Vue default mismatch for ${key}`);
|
|
}
|
|
|
|
// The link rules depend on the master switch on both ends.
|
|
assert.match(php, /'title' => '下载总开关'/);
|
|
assert.match(php, /'dependsOn' => \['key' => 'enabled', 'value' => true\]/);
|
|
assert.match(vue, /dependsOn: \{ key: 'enabled', value: true \}/);
|
|
|
|
// Vue mirrors labels so admins see the same wording as validation errors.
|
|
assert.match(vue, /download: \{/);
|
|
assert.match(vue, /下载链接 URL 别名/);
|
|
assert.match(vue, /下载链接默认使用次数/);
|
|
assert.match(vue, /下载链接默认有效期限(分钟)/);
|
|
});
|
|
|
|
test('the download settings page is registered under the permissions menu', () => {
|
|
const menu = read('assets/admin/src/constants/sections/permissions.js');
|
|
assert.match(menu, /schemaPage\(\{\s*key: 'permissions-download'/);
|
|
assert.match(menu, /route: '\/permissions\/download'/);
|
|
assert.match(menu, /canonicalDomain: 'permissions',\s*section: 'download',/);
|
|
|
|
const settings = read('src/Settings/Settings.php');
|
|
assert.match(settings, /'aksrt_download_config'/);
|
|
});
|
|
|
|
test('invalid url aliases are rejected server-side with admin-facing messages', () => {
|
|
const provider = read('src/Admin/SchemaProvider.php');
|
|
assert.match(provider, /\$domain === 'permissions' && \$section === 'download'/);
|
|
assert.match(provider, /URL 别名不能为空/);
|
|
assert.match(provider, /\/\^\[A-Za-z0-9_-\]\+\$\//);
|
|
assert.match(provider, /禁止中文、空格和 \/ \\\\ \? # & 等特殊符号/);
|
|
assert.match(provider, /reserved_download_aliases/);
|
|
assert.match(provider, /'wp-json', 'wp-admin', 'wp-content', 'wp-includes'/);
|
|
});
|
|
|
|
test('Download.php derives endpoint, ttl and max uses from configuration', () => {
|
|
const dl = read('src/Content/Download.php');
|
|
|
|
// Dead hard-coded values are gone.
|
|
assert.doesNotMatch(dl, /const ENDPOINT/);
|
|
assert.doesNotMatch(dl, /const TOKEN_TTL/);
|
|
assert.doesNotMatch(dl, /aksrt-dl/);
|
|
|
|
// Rewrite regex and URL building share the configured alias.
|
|
assert.match(dl, /preg_quote\(\$this->config\(\)\['endpoint'\], '\/'\)/);
|
|
assert.match(dl, /home_url\('\/' \. \$config\['endpoint'\] \. '\/' \. \$token\)/);
|
|
assert.match(dl, /add_action\('init', \[\$this, 'maybe_flush_rewrite'\], 99\)/);
|
|
assert.match(dl, /flush_rewrite_rules\(false\)/);
|
|
assert.match(dl, /aksrt_dl_rewrite_version/);
|
|
|
|
// TTL and per-token max uses come from the settings section.
|
|
assert.match(dl, /'expires_in'\s+=> \$config\['ttl'\]/);
|
|
assert.match(dl, /'max_uses'\s+=> \(int\) \$config\['max_uses'\]/);
|
|
});
|
|
|
|
test('usage is counted only after full delivery, never for probes', () => {
|
|
const dl = read('src/Content/Download.php');
|
|
|
|
// Probe detection covers HEAD requests and speculative prefetches, which
|
|
// validate availability but consume nothing.
|
|
assert.match(dl, /private function is_probe_request\(\): bool/);
|
|
assert.match(dl, /REQUEST_METHOD.*=== 'HEAD'/);
|
|
assert.match(dl, /HTTP_SEC_PURPOSE/);
|
|
assert.match(dl, /'prefetch'/);
|
|
assert.match(dl, /!\$is_probe\) \{\s*\$this->consume_token/);
|
|
|
|
// Local streaming splits headers from body and aborts stop consumption.
|
|
assert.match(dl, /private function send_download_headers\(string \$path\): void/);
|
|
assert.match(dl, /private function stream_body\(string \$path\): bool/);
|
|
assert.match(dl, /if \(connection_aborted\(\)\) \{/);
|
|
assert.match(dl, /return !connection_aborted\(\);/);
|
|
|
|
// Atomic cap so concurrent finishes can not overshoot max_uses.
|
|
assert.match(dl, /SET used_count = used_count \+ 1 WHERE token = %s AND used_count < max_uses/);
|
|
|
|
// Expired / exhausted links stay a dead end instead of re-issuing silently.
|
|
assert.match(dl, /status_header\(410\)/);
|
|
assert.match(dl, /请回到资源页重新获取/);
|
|
assert.doesNotMatch(dl, /fail_410\(\);\s*issue_token/);
|
|
});
|
|
|
|
test('expired links render a themed flat 410 page that follows the theme color', () => {
|
|
const dl = read('src/Content/Download.php');
|
|
|
|
// Full theme chrome instead of a bare wp_die page.
|
|
assert.match(dl, /private function render_expired_page\(\): void/);
|
|
assert.match(dl, /get_header\(\)/);
|
|
assert.match(dl, /get_footer\(\)/);
|
|
|
|
// wp_die remains only as the safety net when output already started.
|
|
assert.match(dl, /headers_sent\(\)/);
|
|
|
|
// Flat hero keyed to the runtime theme variables (light + html.dark).
|
|
assert.match(dl, /var\(--color-primary-soft/);
|
|
assert.match(dl, /var\(--color-primary/);
|
|
assert.match(dl, /var\(--key-color/);
|
|
assert.match(dl, /var\(--muted-4-color/);
|
|
|
|
// Theme button system and FA5 icon form only (never "fa fa-").
|
|
assert.match(dl, /class="btn-primary"/);
|
|
assert.match(dl, /class="btn-outline"/);
|
|
assert.match(dl, /fa fas fa-hourglass-end/);
|
|
assert.doesNotMatch(dl, /\bfa fa-/);
|
|
|
|
// The 410 page is a utility rewrite; the header component must never match
|
|
// a page type for it (custom rewrites retain the is_home flag).
|
|
const slider = read('inc/helpers/homepage/header-components.php');
|
|
assert.match(slider, /class_exists\('\\AKSRT\\Content\\Download'\)/);
|
|
assert.match(slider, /get_query_var\(\\AKSRT\\Content\\Download::QUERY_VAR\)/);
|
|
});
|
|
|
|
test('token generation retries on primary-key collisions', () => {
|
|
const dl = read('src/Content/Download.php');
|
|
assert.match(dl, /PRIMARY KEY\(token\) already guarantees uniqueness/);
|
|
assert.match(dl, /for \(\$attempt = 0; \$attempt < 3; \$attempt\+\+\)/);
|
|
});
|
|
|
|
test('master switch off puts the token system to sleep and renders direct links', () => {
|
|
const dl = read('src/Content/Download.php');
|
|
const box = read('templates/parts/resource-download-box.php');
|
|
const pay = read('inc/helpers/aksrt-pay.php');
|
|
|
|
// The switch is a public runtime check; missing key means enabled.
|
|
assert.match(dl, /public function is_enabled\(\): bool/);
|
|
assert.match(dl, /!isset\(\$cfg\['enabled'\]\) \|\| !empty\(\$cfg\['enabled'\]\)/);
|
|
|
|
// Rewrite, REST routes and token serving all bail out when disabled.
|
|
assert.match(dl, /if \(!\$this->is_enabled\(\)\) \{\s*return;\s*\}/);
|
|
assert.match(dl, /Master switch off: the token system is retired/);
|
|
|
|
// The unused policy endpoint stays removed.
|
|
assert.doesNotMatch(dl, /download\/policy/);
|
|
assert.doesNotMatch(dl, /rest_policy/);
|
|
|
|
// Direct mode renders anchors to the raw source URL instead of token buttons.
|
|
assert.match(box, /\$direct_download = class_exists\('\\AKSRT\\Content\\Download'\)/);
|
|
assert.match(box, /elseif \(\$direct_download\) \{/);
|
|
assert.match(box, /href="<\?php echo esc_url\(\$btn_href\); \?>/);
|
|
assert.match(box, /data-clipboard-text="<\?php echo esc_attr\(\$extract_code\); \?>/);
|
|
assert.match(pay, /if \(\$direct_download\) \{\s*\$buttons \.= '<a class="mr10 but /);
|
|
|
|
// Dead standalone-page helper stays removed.
|
|
assert.doesNotMatch(pay, /aksrt_is_download_alone_page_enabled/);
|
|
});
|