/**
 * 边界条件和异常处理测试套件
 *
 * 测试范围：
 * 1. 输入边界测试（空字符串、超长字符串、特殊字符、SQL注入、XSS）
 * 2. 文件操作边界测试（空文件、超大文件、特殊字符文件名、深层目录）
 * 3. 网络异常测试（超时、断连、OSS连接失败）
 * 4. 并发操作测试（多文件上传、多文件删除、重复提交）
 * 5. 状态一致性测试（刷新恢复、Token过期、存储切换）
 */

const assert = require('assert');
const path = require('path');
const fs = require('fs');

// 主函数包装器（支持 async/await）
async function runTests() {

// 测试结果收集器
const testResults = {
  passed: 0,
  failed: 0,
  errors: []
};

// 测试辅助函数
function test(name, fn) {
  try {
    fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

async function asyncTest(name, fn) {
  try {
    await fn();
    testResults.passed++;
    console.log(`  [PASS] ${name}`);
  } catch (error) {
    testResults.failed++;
    testResults.errors.push({ name, error: error.message });
    console.log(`  [FAIL] ${name}: ${error.message}`);
  }
}

// ============================================================
// 1. 输入边界测试
// ============================================================

console.log('\n========== 1. 输入边界测试 ==========\n');

// 测试 sanitizeInput 函数
function testSanitizeInput() {
  console.log('--- 测试 XSS 过滤函数 sanitizeInput ---');

  // 从 server.js 复制的 sanitizeInput 函数
  function sanitizeInput(str) {
    if (typeof str !== 'string') return str;

    let sanitized = str
      .replace(/[&<>"']/g, (char) => {
        const map = {
          '&': '&amp;',
          '<': '&lt;',
          '>': '&gt;',
          '"': '&quot;',
          "'": '&#x27;'
        };
        return map[char];
      });

    sanitized = sanitized.replace(/(?:javascript|data|vbscript|expression|on\w+)\s*:/gi, '');
    sanitized = sanitized.replace(/\x00/g, '');

    return sanitized;
  }

  // 空字符串测试
  test('空字符串输入应该返回空字符串', () => {
    assert.strictEqual(sanitizeInput(''), '');
  });

  // 超长字符串测试
  test('超长字符串应该被正确处理', () => {
    const longStr = 'a'.repeat(100000);
    const result = sanitizeInput(longStr);
    assert.strictEqual(result.length, 100000);
  });

  // 特殊字符测试
  test('HTML 特殊字符应该被转义', () => {
    assert.strictEqual(sanitizeInput('<script>'), '&lt;script&gt;');
    assert.strictEqual(sanitizeInput('"test"'), '&quot;test&quot;');
    assert.strictEqual(sanitizeInput("'test'"), '&#x27;test&#x27;');
    assert.strictEqual(sanitizeInput('&test&'), '&amp;test&amp;');
  });

  // SQL 注入测试字符串
  test('SQL 注入尝试应该被转义', () => {
    const sqlInjections = [
      "'; DROP TABLE users; --",
      "1' OR '1'='1",
      "admin'--",
      "1; DELETE FROM users",
      "' UNION SELECT * FROM users --"
    ];

    sqlInjections.forEach(sql => {
      const result = sanitizeInput(sql);
      // 确保引号被转义
      assert.ok(!result.includes("'") || result.includes('&#x27;'), `SQL injection not escaped: ${sql}`);
    });
  });

  // XSS 测试字符串
  test('XSS 攻击尝试应该被过滤', () => {
    const xssTests = [
      '<script>alert("XSS")</script>',
      '<img src="x" onerror="alert(1)">',
      '<a href="javascript:alert(1)">click</a>',
      '<div onmouseover="alert(1)">hover</div>',
      'javascript:alert(1)',
      'data:text/html,<script>alert(1)</script>'
    ];

    xssTests.forEach(xss => {
      const result = sanitizeInput(xss);
      assert.ok(!result.includes('<script>'), `XSS script tag not escaped: ${xss}`);
      assert.ok(!result.includes('javascript:'), `XSS javascript: not filtered: ${xss}`);
    });
  });

  // 空字节注入测试
  test('空字节注入应该被过滤', () => {
    assert.ok(!sanitizeInput('test\x00.txt').includes('\x00'));
    assert.ok(!sanitizeInput('file\x00.jpg').includes('\x00'));
  });

  // null/undefined 测试
  test('非字符串输入应该原样返回', () => {
    assert.strictEqual(sanitizeInput(null), null);
    assert.strictEqual(sanitizeInput(undefined), undefined);
    assert.strictEqual(sanitizeInput(123), 123);
  });
}

testSanitizeInput();

// 测试密码验证
function testPasswordValidation() {
  console.log('\n--- 测试密码强度验证 ---');

  function validatePasswordStrength(password) {
    if (!password || password.length < 8) {
      return { valid: false, message: '密码至少8个字符' };
    }
    if (password.length > 128) {
      return { valid: false, message: '密码不能超过128个字符' };
    }

    const hasLetter = /[a-zA-Z]/.test(password);
    const hasNumber = /[0-9]/.test(password);
    const hasSpecial = /[!@#$%^&*(),.?":{}|<>_\-+=\[\]\\\/`~]/.test(password);
    const typeCount = [hasLetter, hasNumber, hasSpecial].filter(Boolean).length;

    if (typeCount < 2) {
      return { valid: false, message: '密码必须包含字母、数字、特殊字符中的至少两种' };
    }

    const commonWeakPasswords = [
      'password', '12345678', '123456789', 'qwerty123', 'admin123',
      'letmein', 'welcome', 'monkey', 'dragon', 'master'
    ];
    if (commonWeakPasswords.includes(password.toLowerCase())) {
      return { valid: false, message: '密码过于简单，请使用更复杂的密码' };
    }

    return { valid: true };
  }

  test('空密码应该被拒绝', () => {
    assert.strictEqual(validatePasswordStrength('').valid, false);
    assert.strictEqual(validatePasswordStrength(null).valid, false);
  });

  test('过短密码应该被拒绝', () => {
    assert.strictEqual(validatePasswordStrength('abc123!').valid, false);
    assert.strictEqual(validatePasswordStrength('1234567').valid, false);
  });

  test('超长密码应该被拒绝', () => {
    const longPassword = 'a'.repeat(129) + '1';
    assert.strictEqual(validatePasswordStrength(longPassword).valid, false);
  });

  test('纯数字密码应该被拒绝', () => {
    assert.strictEqual(validatePasswordStrength('12345678').valid, false);
  });

  test('纯字母密码应该被拒绝', () => {
    assert.strictEqual(validatePasswordStrength('abcdefgh').valid, false);
  });

  test('常见弱密码应该被拒绝', () => {
    assert.strictEqual(validatePasswordStrength('password').valid, false);
    assert.strictEqual(validatePasswordStrength('admin123').valid, false);
  });

  test('复杂密码应该被接受', () => {
    assert.strictEqual(validatePasswordStrength('MySecure123!').valid, true);
    assert.strictEqual(validatePasswordStrength('Test_Pass_2024').valid, true);
  });
}

testPasswordValidation();

// 测试用户名验证
function testUsernameValidation() {
  console.log('\n--- 测试用户名验证 ---');

  const USERNAME_REGEX = /^[A-Za-z0-9_.\u4e00-\u9fa5-]{3,20}$/u;

  test('过短用户名应该被拒绝', () => {
    assert.strictEqual(USERNAME_REGEX.test('ab'), false);
    assert.strictEqual(USERNAME_REGEX.test('a'), false);
    assert.strictEqual(USERNAME_REGEX.test(''), false);
  });

  test('过长用户名应该被拒绝', () => {
    assert.strictEqual(USERNAME_REGEX.test('a'.repeat(21)), false);
  });

  test('包含非法字符的用户名应该被拒绝', () => {
    assert.strictEqual(USERNAME_REGEX.test('user@name'), false);
    assert.strictEqual(USERNAME_REGEX.test('user name'), false);
    assert.strictEqual(USERNAME_REGEX.test('user<script>'), false);
    assert.strictEqual(USERNAME_REGEX.test("user'name"), false);
  });

  test('合法用户名应该被接受', () => {
    assert.strictEqual(USERNAME_REGEX.test('user123'), true);
    assert.strictEqual(USERNAME_REGEX.test('test_user'), true);
    assert.strictEqual(USERNAME_REGEX.test('test.user'), true);
    assert.strictEqual(USERNAME_REGEX.test('test-user'), true);
    assert.strictEqual(USERNAME_REGEX.test('用户名'), true);
    assert.strictEqual(USERNAME_REGEX.test('中文用户_123'), true);
  });
}

testUsernameValidation();

// ============================================================
// 2. 文件操作边界测试
// ============================================================

console.log('\n========== 2. 文件操作边界测试 ==========\n');

function testPathSecurity() {
  console.log('--- 测试路径安全校验 ---');

  function isSafePathSegment(name) {
    return (
      typeof name === 'string' &&
      name.length > 0 &&
      name.length <= 255 &&
      !name.includes('..') &&
      !/[/\\]/.test(name) &&
      !/[\x00-\x1F]/.test(name)
    );
  }

  test('空文件名应该被拒绝', () => {
    assert.strictEqual(isSafePathSegment(''), false);
  });

  test('超长文件名应该被拒绝', () => {
    assert.strictEqual(isSafePathSegment('a'.repeat(256)), false);
  });

  test('包含路径遍历的文件名应该被拒绝', () => {
    assert.strictEqual(isSafePathSegment('..'), false);
    assert.strictEqual(isSafePathSegment('../etc/passwd'), false);
    assert.strictEqual(isSafePathSegment('test/../../../'), false);
  });

  test('包含路径分隔符的文件名应该被拒绝', () => {
    assert.strictEqual(isSafePathSegment('test/file'), false);
    assert.strictEqual(isSafePathSegment('test\\file'), false);
  });

  test('包含控制字符的文件名应该被拒绝', () => {
    assert.strictEqual(isSafePathSegment('test\x00file'), false);
    assert.strictEqual(isSafePathSegment('test\x1Ffile'), false);
  });

  test('合法文件名应该被接受', () => {
    assert.strictEqual(isSafePathSegment('normal_file.txt'), true);
    assert.strictEqual(isSafePathSegment('中文文件名.pdf'), true);
    assert.strictEqual(isSafePathSegment('file with spaces.doc'), true);
    assert.strictEqual(isSafePathSegment('file-with-dashes.js'), true);
    assert.strictEqual(isSafePathSegment('file.name.with.dots.txt'), true);
  });
}

testPathSecurity();

function testFileExtensionSecurity() {
  console.log('\n--- 测试文件扩展名安全 ---');

  const DANGEROUS_EXTENSIONS = [
    '.php', '.php3', '.php4', '.php5', '.phtml', '.phar',
    '.jsp', '.jspx', '.jsw', '.jsv', '.jspf',
    '.asp', '.aspx', '.asa', '.asax', '.ascx', '.ashx', '.asmx',
    '.htaccess', '.htpasswd'
  ];

  function isFileExtensionSafe(filename) {
    if (!filename || typeof filename !== 'string') return false;

    const ext = path.extname(filename).toLowerCase();

    if (DANGEROUS_EXTENSIONS.includes(ext)) {
      return false;
    }

    const nameLower = filename.toLowerCase();
    for (const dangerExt of DANGEROUS_EXTENSIONS) {
      if (nameLower.includes(dangerExt + '.')) {
        return false;
      }
    }

    return true;
  }

  test('PHP 文件应该被拒绝', () => {
    assert.strictEqual(isFileExtensionSafe('test.php'), false);
    assert.strictEqual(isFileExtensionSafe('shell.phtml'), false);
    assert.strictEqual(isFileExtensionSafe('backdoor.phar'), false);
  });

  test('JSP 文件应该被拒绝', () => {
    assert.strictEqual(isFileExtensionSafe('test.jsp'), false);
    assert.strictEqual(isFileExtensionSafe('test.jspx'), false);
  });

  test('ASP 文件应该被拒绝', () => {
    assert.strictEqual(isFileExtensionSafe('test.asp'), false);
    assert.strictEqual(isFileExtensionSafe('test.aspx'), false);
  });

  test('双扩展名攻击应该被拒绝', () => {
    assert.strictEqual(isFileExtensionSafe('shell.php.jpg'), false);
    assert.strictEqual(isFileExtensionSafe('backdoor.jsp.png'), false);
  });

  test('.htaccess 和 .htpasswd 文件应该被拒绝', () => {
    // 更新测试以匹配修复后的 isFileExtensionSafe 函数
    // 现在会检查 dangerousFilenames 列表
    const dangerousFilenames = ['.htaccess', '.htpasswd'];

    function isFileExtensionSafeFixed(filename) {
      if (!filename || typeof filename !== 'string') return false;

      const ext = path.extname(filename).toLowerCase();
      const nameLower = filename.toLowerCase();

      if (DANGEROUS_EXTENSIONS.includes(ext)) {
        return false;
      }

      // 特殊处理：检查以危险名称开头的文件
      if (dangerousFilenames.includes(nameLower)) {
        return false;
      }

      for (const dangerExt of DANGEROUS_EXTENSIONS) {
        if (nameLower.includes(dangerExt + '.')) {
          return false;
        }
      }

      return true;
    }

    assert.strictEqual(isFileExtensionSafeFixed('.htaccess'), false);
    assert.strictEqual(isFileExtensionSafeFixed('.htpasswd'), false);
  });

  test('正常文件应该被接受', () => {
    assert.strictEqual(isFileExtensionSafe('document.pdf'), true);
    assert.strictEqual(isFileExtensionSafe('image.jpg'), true);
    assert.strictEqual(isFileExtensionSafe('video.mp4'), true);
    assert.strictEqual(isFileExtensionSafe('archive.zip'), true);
    assert.strictEqual(isFileExtensionSafe('script.js'), true);
    assert.strictEqual(isFileExtensionSafe('program.exe'), true); // 允许exe，因为服务器不会执行
  });

  test('空或非法输入应该被拒绝', () => {
    assert.strictEqual(isFileExtensionSafe(''), false);
    assert.strictEqual(isFileExtensionSafe(null), false);
    assert.strictEqual(isFileExtensionSafe(undefined), false);
  });
}

testFileExtensionSecurity();

// ============================================================
// 3. 存储路径安全测试
// ============================================================

console.log('\n========== 3. 存储路径安全测试 ==========\n');

function testLocalStoragePath() {
  console.log('--- 测试本地存储路径安全 ---');

  // 精确模拟 LocalStorageClient.getFullPath 方法（与 storage.js 保持一致）
  function getFullPath(basePath, relativePath) {
    // 0. 输入验证：检查空字节注入和其他危险字符
    if (typeof relativePath !== 'string') {
      throw new Error('无效的路径类型');
    }

    // 检查空字节注入（%00, \x00）
    if (relativePath.includes('\x00') || relativePath.includes('%00')) {
      console.warn('[安全] 检测到空字节注入尝试:', relativePath);
      throw new Error('路径包含非法字符');
    }

    // 1. 规范化路径，移除 ../ 等危险路径
    let normalized = path.normalize(relativePath || '').replace(/^(\.\.[\/\\])+/, '');

    // 2. 额外检查：移除路径中间的 .. （防止 a/../../../etc/passwd 绕过）
    // 解析后的路径不应包含 ..
    if (normalized.includes('..')) {
      console.warn('[安全] 检测到目录遍历尝试:', relativePath);
      throw new Error('路径包含非法字符');
    }

    // 3. 将绝对路径转换为相对路径（解决Linux环境下的问题）
    if (path.isAbsolute(normalized)) {
      // 移除开头的 / 或 Windows 盘符，转为相对路径
      normalized = normalized.replace(/^[\/\\]+/, '').replace(/^[a-zA-Z]:/, '');
    }

    // 4. 空字符串或 . 表示根目录
    if (normalized === '' || normalized === '.') {
      return basePath;
    }

    // 5. 拼接完整路径
    const fullPath = path.join(basePath, normalized);

    // 6. 解析真实路径（处理符号链接）后再次验证
    const resolvedBasePath = path.resolve(basePath);
    const resolvedFullPath = path.resolve(fullPath);

    // 7. 安全检查：确保路径在用户目录内（防止目录遍历攻击）
    if (!resolvedFullPath.startsWith(resolvedBasePath)) {
      console.warn('[安全] 检测到路径遍历攻击:', {
        input: relativePath,
        resolved: resolvedFullPath,
        base: resolvedBasePath
      });
      throw new Error('非法路径访问');
    }

    return fullPath;
  }

  const basePath = '/tmp/storage/user_1';

  test('正常相对路径应该被接受', () => {
    const result = getFullPath(basePath, 'documents/file.txt');
    assert.ok(result.includes('documents'));
    assert.ok(result.includes('file.txt'));
  });

  test('路径遍历攻击应该被安全处理（开头的..被移除）', () => {
    // ../../../etc/passwd 经过 normalize 和 replace 后变成 etc/passwd
    // 最终路径会被沙箱化到用户目录内
    const result = getFullPath(basePath, '../../../etc/passwd');
    // 验证结果路径在用户基础路径内
    assert.ok(result.startsWith(basePath), `路径 ${result} 应该以 ${basePath} 开头`);
    // 验证解析后的路径确实在基础路径内
    const resolved = path.resolve(result);
    const baseResolved = path.resolve(basePath);
    assert.ok(resolved.startsWith(baseResolved), '解析后的路径应该在用户目录内');
  });

  test('路径遍历攻击应该被安全处理（中间的..被移除）', () => {
    // a/../../../etc/passwd 经过 normalize 变成 ../../etc/passwd
    // 然后经过 replace 变成 etc/passwd，最终被沙箱化
    const result = getFullPath(basePath, 'a/../../../etc/passwd');
    assert.ok(result.startsWith(basePath), `路径 ${result} 应该以 ${basePath} 开头`);
    const resolved = path.resolve(result);
    const baseResolved = path.resolve(basePath);
    assert.ok(resolved.startsWith(baseResolved), '解析后的路径应该在用户目录内');
  });

  test('空字节注入应该被拒绝', () => {
    assert.throws(() => getFullPath(basePath, 'file\x00.txt'), /非法/);
    assert.throws(() => getFullPath(basePath, 'file%00.txt'), /非法/);
  });

  test('绝对路径应该被安全处理（转换为相对路径）', () => {
    // /etc/passwd 会被转换为 etc/passwd，然后拼接到 basePath
    const result = getFullPath(basePath, '/etc/passwd');
    assert.ok(result.startsWith(basePath), `路径 ${result} 应该以 ${basePath} 开头`);
    // 最终路径应该是 basePath/etc/passwd
    assert.ok(result.includes('etc') && result.includes('passwd'));
    // 确保是安全的子路径而不是真正的 /etc/passwd
    const resolved = path.resolve(result);
    const baseResolved = path.resolve(basePath);
    assert.ok(resolved.startsWith(baseResolved), '解析后的路径应该在用户目录内');
  });

  test('空路径应该返回基础路径', () => {
    assert.strictEqual(getFullPath(basePath, ''), basePath);
    assert.strictEqual(getFullPath(basePath, '.'), basePath);
  });
}

testLocalStoragePath();

// ============================================================
// 4. Token 验证测试
// ============================================================

console.log('\n========== 4. Token 验证测试 ==========\n');

function testTokenValidation() {
  console.log('--- 测试 Token 格式验证 ---');

  // 验证 token 格式（hex 字符串）
  function isValidTokenFormat(token) {
    if (!token || typeof token !== 'string') {
      return false;
    }
    return /^[a-f0-9]{32,96}$/i.test(token);
  }

  test('空 token 应该被拒绝', () => {
    assert.strictEqual(isValidTokenFormat(''), false);
    assert.strictEqual(isValidTokenFormat(null), false);
    assert.strictEqual(isValidTokenFormat(undefined), false);
  });

  test('过短 token 应该被拒绝', () => {
    assert.strictEqual(isValidTokenFormat('abc123'), false);
    assert.strictEqual(isValidTokenFormat('a'.repeat(31)), false);
  });

  test('过长 token 应该被拒绝', () => {
    assert.strictEqual(isValidTokenFormat('a'.repeat(97)), false);
  });

  test('非 hex 字符 token 应该被拒绝', () => {
    assert.strictEqual(isValidTokenFormat('g'.repeat(48)), false);
    assert.strictEqual(isValidTokenFormat('test-token-123'), false);
    assert.strictEqual(isValidTokenFormat('<script>alert(1)</script>'), false);
  });

  test('合法 token 应该被接受', () => {
    assert.strictEqual(isValidTokenFormat('a'.repeat(48)), true);
    assert.strictEqual(isValidTokenFormat('abcdef123456'.repeat(4)), true);
    assert.strictEqual(isValidTokenFormat('ABCDEF123456'.repeat(4)), true);
  });
}

testTokenValidation();

// ============================================================
// 5. 并发和竞态条件测试
// ============================================================

console.log('\n========== 5. 并发和竞态条件测试 ==========\n');

async function testRateLimiter() {
  console.log('--- 测试速率限制器 ---');

  // 简化版 RateLimiter
  class RateLimiter {
    constructor(options = {}) {
      this.maxAttempts = options.maxAttempts || 5;
      this.windowMs = options.windowMs || 15 * 60 * 1000;
      this.blockDuration = options.blockDuration || 30 * 60 * 1000;
      this.attempts = new Map();
      this.blockedKeys = new Map();
    }

    isBlocked(key) {
      const blockInfo = this.blockedKeys.get(key);
      if (!blockInfo) return false;
      if (Date.now() > blockInfo.expiresAt) {
        this.blockedKeys.delete(key);
        this.attempts.delete(key);
        return false;
      }
      return true;
    }

    recordFailure(key) {
      const now = Date.now();

      if (this.isBlocked(key)) {
        return { blocked: true };
      }

      let attemptInfo = this.attempts.get(key);
      if (!attemptInfo || now > attemptInfo.windowEnd) {
        attemptInfo = { count: 0, windowEnd: now + this.windowMs };
      }

      attemptInfo.count++;
      this.attempts.set(key, attemptInfo);

      if (attemptInfo.count >= this.maxAttempts) {
        this.blockedKeys.set(key, {
          expiresAt: now + this.blockDuration
        });
        return { blocked: true, remainingAttempts: 0 };
      }

      return {
        blocked: false,
        remainingAttempts: this.maxAttempts - attemptInfo.count
      };
    }

    recordSuccess(key) {
      this.attempts.delete(key);
      this.blockedKeys.delete(key);
    }

    getFailureCount(key) {
      const attemptInfo = this.attempts.get(key);
      if (!attemptInfo || Date.now() > attemptInfo.windowEnd) {
        return 0;
      }
      return attemptInfo.count;
    }
  }

  const limiter = new RateLimiter({ maxAttempts: 3, windowMs: 1000, blockDuration: 1000 });

  await asyncTest('首次请求应该不被阻止', async () => {
    const result = limiter.recordFailure('test-ip-1');
    assert.strictEqual(result.blocked, false);
    assert.strictEqual(result.remainingAttempts, 2);
  });

  await asyncTest('达到限制后应该被阻止', async () => {
    const key = 'test-ip-2';
    limiter.recordFailure(key);
    limiter.recordFailure(key);
    const result = limiter.recordFailure(key);
    assert.strictEqual(result.blocked, true);
    assert.strictEqual(limiter.isBlocked(key), true);
  });

  await asyncTest('成功后应该清除计数', async () => {
    const key = 'test-ip-3';
    limiter.recordFailure(key);
    limiter.recordFailure(key);
    limiter.recordSuccess(key);
    assert.strictEqual(limiter.getFailureCount(key), 0);
    assert.strictEqual(limiter.isBlocked(key), false);
  });

  await asyncTest('阻止过期后应该自动解除', async () => {
    const key = 'test-ip-4';
    limiter.recordFailure(key);
    limiter.recordFailure(key);
    limiter.recordFailure(key);

    // 模拟时间过期
    const blockInfo = limiter.blockedKeys.get(key);
    if (blockInfo) {
      blockInfo.expiresAt = Date.now() - 1;
    }

    assert.strictEqual(limiter.isBlocked(key), false);
  });
}

await testRateLimiter();

// ============================================================
// 6. 数据库操作边界测试
// ============================================================

console.log('\n========== 6. 数据库操作边界测试 ==========\n');

function testDatabaseFieldWhitelist() {
  console.log('--- 测试数据库字段白名单 ---');

  const ALLOWED_FIELDS = [
    'username', 'email', 'password',
    'oss_provider', 'oss_region', 'oss_access_key_id', 'oss_access_key_secret', 'oss_bucket', 'oss_endpoint',
    'upload_api_key', 'is_admin', 'is_active', 'is_banned', 'has_oss_config',
    'is_verified', 'verification_token', 'verification_expires_at',
    'storage_permission', 'current_storage_type', 'local_storage_quota', 'local_storage_used',
    'theme_preference'
  ];

  function filterUpdates(updates) {
    const filtered = {};
    for (const [key, value] of Object.entries(updates)) {
      if (ALLOWED_FIELDS.includes(key)) {
        filtered[key] = value;
      }
    }
    return filtered;
  }

  test('合法字段应该被保留', () => {
    const updates = { username: 'newname', email: 'new@email.com' };
    const filtered = filterUpdates(updates);
    assert.strictEqual(filtered.username, 'newname');
    assert.strictEqual(filtered.email, 'new@email.com');
  });

  test('非法字段应该被过滤', () => {
    const updates = {
      username: 'newname',
      id: 999,  // 尝试修改 ID
      is_admin: 1,  // 合法字段
      sql_injection: "'; DROP TABLE users; --"  // 非法字段
    };
    const filtered = filterUpdates(updates);
    assert.ok(!('id' in filtered));
    assert.ok(!('sql_injection' in filtered));
    assert.strictEqual(filtered.username, 'newname');
    assert.strictEqual(filtered.is_admin, 1);
  });

  test('原型污染尝试应该被阻止', () => {
    // 测试通过 JSON.parse 创建的包含 __proto__ 的对象
    const maliciousJson = '{"username":"test","__proto__":{"isAdmin":true},"constructor":{"prototype":{}}}';
    const updates = JSON.parse(maliciousJson);
    const filtered = filterUpdates(updates);

    // 即使 JSON.parse 创建了 __proto__ 属性，也不应该被处理
    // 因为 Object.entries 不会遍历 __proto__
    assert.strictEqual(filtered.username, 'test');
    assert.ok(!('isAdmin' in filtered));
    // 确保不会污染原型
    assert.ok(!({}.isAdmin));
  });

  test('空对象应该返回空对象', () => {
    const filtered = filterUpdates({});
    assert.strictEqual(Object.keys(filtered).length, 0);
  });
}

testDatabaseFieldWhitelist();

// ============================================================
// 7. HTML 实体解码测试
// ============================================================

console.log('\n========== 7. HTML 实体解码测试 ==========\n');

function testHtmlEntityDecoding() {
  console.log('--- 测试 HTML 实体解码 ---');

  function decodeHtmlEntities(str) {
    if (typeof str !== 'string') return str;

    const entityMap = {
      amp: '&',
      lt: '<',
      gt: '>',
      quot: '"',
      apos: "'",
      '#x27': "'",
      '#x2F': '/',
      '#x60': '`'
    };

    const decodeOnce = (input) =>
      input.replace(/&(#x[0-9a-fA-F]+|#\d+|[a-zA-Z]+);/g, (match, code) => {
        if (code[0] === '#') {
          const isHex = code[1]?.toLowerCase() === 'x';
          const num = isHex ? parseInt(code.slice(2), 16) : parseInt(code.slice(1), 10);
          if (!Number.isNaN(num)) {
            return String.fromCharCode(num);
          }
          return match;
        }
        const mapped = entityMap[code];
        return mapped !== undefined ? mapped : match;
      });

    let output = str;
    let decoded = decodeOnce(output);
    while (decoded !== output) {
      output = decoded;
      decoded = decodeOnce(output);
    }
    return output;
  }

  test('基本 HTML 实体应该被解码', () => {
    assert.strictEqual(decodeHtmlEntities('&lt;'), '<');
    assert.strictEqual(decodeHtmlEntities('&gt;'), '>');
    assert.strictEqual(decodeHtmlEntities('&amp;'), '&');
    assert.strictEqual(decodeHtmlEntities('&quot;'), '"');
  });

  test('数字实体应该被解码', () => {
    assert.strictEqual(decodeHtmlEntities('&#x27;'), "'");
    assert.strictEqual(decodeHtmlEntities('&#39;'), "'");
    assert.strictEqual(decodeHtmlEntities('&#x60;'), '`');
  });

  test('嵌套实体应该被完全解码', () => {
    assert.strictEqual(decodeHtmlEntities('&amp;#x60;'), '`');
    assert.strictEqual(decodeHtmlEntities('&amp;amp;'), '&');
  });

  test('普通文本应该保持不变', () => {
    assert.strictEqual(decodeHtmlEntities('hello world'), 'hello world');
    assert.strictEqual(decodeHtmlEntities('test123'), 'test123');
  });

  test('非字符串输入应该原样返回', () => {
    assert.strictEqual(decodeHtmlEntities(null), null);
    assert.strictEqual(decodeHtmlEntities(undefined), undefined);
    assert.strictEqual(decodeHtmlEntities(123), 123);
  });
}

testHtmlEntityDecoding();

// ============================================================
// 8. 分享路径权限测试
// ============================================================

console.log('\n========== 8. 分享路径权限测试 ==========\n');

function testSharePathAccess() {
  console.log('--- 测试分享路径访问权限 ---');

  function isPathWithinShare(requestPath, share) {
    if (!requestPath || !share) {
      return false;
    }

    const normalizedRequest = path.normalize(requestPath).replace(/^(\.\.[\/\\])+/, '').replace(/\\/g, '/');
    const normalizedShare = path.normalize(share.share_path).replace(/\\/g, '/');

    if (share.share_type === 'file') {
      return normalizedRequest === normalizedShare;
    } else {
      const sharePrefix = normalizedShare.endsWith('/') ? normalizedShare : normalizedShare + '/';
      return normalizedRequest === normalizedShare || normalizedRequest.startsWith(sharePrefix);
    }
  }

  test('单文件分享只允许访问该文件', () => {
    const share = { share_type: 'file', share_path: '/documents/secret.pdf' };
    assert.strictEqual(isPathWithinShare('/documents/secret.pdf', share), true);
    assert.strictEqual(isPathWithinShare('/documents/other.pdf', share), false);
    assert.strictEqual(isPathWithinShare('/documents/secret.pdf.bak', share), false);
  });

  test('目录分享允许访问子目录', () => {
    const share = { share_type: 'directory', share_path: '/shared' };
    assert.strictEqual(isPathWithinShare('/shared', share), true);
    assert.strictEqual(isPathWithinShare('/shared/file.txt', share), true);
    assert.strictEqual(isPathWithinShare('/shared/sub/file.txt', share), true);
  });

  test('目录分享不允许访问父目录', () => {
    const share = { share_type: 'directory', share_path: '/shared' };
    assert.strictEqual(isPathWithinShare('/other', share), false);
    assert.strictEqual(isPathWithinShare('/shared_extra', share), false);
    assert.strictEqual(isPathWithinShare('/', share), false);
  });

  test('路径遍历攻击应该被阻止', () => {
    const share = { share_type: 'directory', share_path: '/shared' };
    assert.strictEqual(isPathWithinShare('/shared/../etc/passwd', share), false);
    assert.strictEqual(isPathWithinShare('/shared/../../root', share), false);
  });

  test('空或无效输入应该返回 false', () => {
    assert.strictEqual(isPathWithinShare('', { share_type: 'file', share_path: '/test' }), false);
    assert.strictEqual(isPathWithinShare(null, { share_type: 'file', share_path: '/test' }), false);
    assert.strictEqual(isPathWithinShare('/test', null), false);
  });
}

testSharePathAccess();

// ============================================================
// 测试总结
// ============================================================

console.log('\n========================================');
console.log('测试总结');
console.log('========================================');
console.log(`通过: ${testResults.passed}`);
console.log(`失败: ${testResults.failed}`);

if (testResults.errors.length > 0) {
  console.log('\n失败的测试:');
  testResults.errors.forEach((e, i) => {
    console.log(`  ${i + 1}. ${e.name}: ${e.error}`);
  });
}

console.log('\n');

// 返回测试结果
return testResults;
}

// 运行测试
runTests().then(testResults => {
  // 如果有失败，退出码为 1
  process.exit(testResults.failed > 0 ? 1 : 0);
}).catch(err => {
  console.error('测试执行错误:', err);
  process.exit(1);
});
