/**
 * API Key安全存储服务测试
 * 
 * 功能描述：
 * - 测试ApiKeySecurityService的核心功能
 * - 包含属性测试验证API Key安全存储
 * - 验证加密解密和安全事件记录功能
 * 
 * 测试策略：
 * - 使用fast-check进行属性测试，验证加密解密的一致性
 * - 模拟Redis服务，测试存储和检索功能
 * - 验证安全事件的正确记录和查询
 * 
 * 最近修改：
 * - 2026-01-12: 代码规范优化 - 完善测试文件注释规范 (修改者: moyin)
 * 
 * @author angjustinl, moyin
 * @version 1.0.1
 * @since 2025-12-25
 * @lastModified 2026-01-12
 */

import { Test, TestingModule } from '@nestjs/testing';
import { Logger } from '@nestjs/common';
import * as fc from 'fast-check';
import {
  ApiKeySecurityService,
  SecurityEventType,
  SecuritySeverity,
} from './api_key_security.service';
import { IRedisService } from '../../../core/redis/redis.interface';

describe('ApiKeySecurityService', () => {
  let service: ApiKeySecurityService;
  let mockRedisService: jest.Mocked<IRedisService>;

  // 内存存储模拟Redis
  let memoryStore: Map<string, { value: string; expireAt?: number }>;

  beforeEach(async () => {
    jest.clearAllMocks();

    // Mock NestJS Logger
    jest.spyOn(Logger.prototype, 'log').mockImplementation();
    jest.spyOn(Logger.prototype, 'error').mockImplementation();
    jest.spyOn(Logger.prototype, 'warn').mockImplementation();
    jest.spyOn(Logger.prototype, 'debug').mockImplementation();

    // 初始化内存存储
    memoryStore = new Map();

    // 创建模拟Redis服务
    mockRedisService = {
      set: jest.fn().mockImplementation(async (key: string, value: string, ttl?: number) => {
        memoryStore.set(key, {
          value,
          expireAt: ttl ? Date.now() + ttl * 1000 : undefined,
        });
        return 'OK';
      }),
      setex: jest.fn().mockImplementation(async (key: string, ttl: number, value: string) => {
        memoryStore.set(key, {
          value,
          expireAt: Date.now() + ttl * 1000,
        });
        return 'OK';
      }),
      get: jest.fn().mockImplementation(async (key: string) => {
        const item = memoryStore.get(key);
        if (!item) return null;
        if (item.expireAt && item.expireAt <= Date.now()) {
          memoryStore.delete(key);
          return null;
        }
        return item.value;
      }),
      del: jest.fn().mockImplementation(async (key: string) => {
        const existed = memoryStore.has(key);
        memoryStore.delete(key);
        return existed ? 1 : 0;
      }),
      exists: jest.fn().mockImplementation(async (key: string) => {
        return memoryStore.has(key);
      }),
      ttl: jest.fn().mockImplementation(async (key: string) => {
        const item = memoryStore.get(key);
        if (!item || !item.expireAt) return -1;
        return Math.max(0, Math.floor((item.expireAt - Date.now()) / 1000));
      }),
      incr: jest.fn().mockImplementation(async (key: string) => {
        const item = memoryStore.get(key);
        if (!item) {
          memoryStore.set(key, { value: '1' });
          return 1;
        }
        const newValue = parseInt(item.value, 10) + 1;
        item.value = newValue.toString();
        return newValue;
      }),
    } as any;

    const module: TestingModule = await Test.createTestingModule({
      providers: [
        ApiKeySecurityService,
        {
          provide: 'REDIS_SERVICE',
          useValue: mockRedisService,
        },
      ],
    }).compile();

    service = module.get<ApiKeySecurityService>(ApiKeySecurityService);
  });

  afterEach(async () => {
    memoryStore.clear();
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('storeApiKey - 存储API Key', () => {
    it('应该成功存储有效的API Key', async () => {
      const result = await service.storeApiKey('user-123', 'abcdefghijklmnopqrstuvwxyz123456');
      expect(result.success).toBe(true);
      expect(result.userId).toBe('user-123');
    });

    it('应该拒绝空用户ID', async () => {
      const result = await service.storeApiKey('', 'abcdefghijklmnopqrstuvwxyz123456');
      expect(result.success).toBe(false);
      expect(result.message).toContain('用户ID');
    });

    it('应该拒绝空API Key', async () => {
      const result = await service.storeApiKey('user-123', '');
      expect(result.success).toBe(false);
      expect(result.message).toContain('API Key');
    });

    it('应该拒绝格式无效的API Key', async () => {
      const result = await service.storeApiKey('user-123', 'short');
      expect(result.success).toBe(false);
      expect(result.message).toContain('格式无效');
    });

    it('应该拒绝包含特殊字符的API Key', async () => {
      const result = await service.storeApiKey('user-123', 'invalid!@#$%^&*()key12345');
      expect(result.success).toBe(false);
      expect(result.message).toContain('格式无效');
    });
  });

  describe('getApiKey - 获取API Key', () => {
    it('应该成功获取已存储的API Key', async () => {
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      await service.storeApiKey('user-123', apiKey);

      const result = await service.getApiKey('user-123');
      expect(result.success).toBe(true);
      expect(result.apiKey).toBe(apiKey);
    });

    it('应该返回不存在的API Key错误', async () => {
      const result = await service.getApiKey('nonexistent-user');
      expect(result.success).toBe(false);
      expect(result.message).toContain('不存在');
    });

    it('应该拒绝空用户ID', async () => {
      const result = await service.getApiKey('');
      expect(result.success).toBe(false);
      expect(result.message).toContain('用户ID');
    });
  });

  describe('updateApiKey - 更新API Key', () => {
    it('应该成功更新已存在的API Key', async () => {
      const oldKey = 'abcdefghijklmnopqrstuvwxyz123456';
      const newKey = 'newkeyabcdefghijklmnopqrstuvwx';

      await service.storeApiKey('user-123', oldKey);
      const updateResult = await service.updateApiKey('user-123', newKey);
      expect(updateResult.success).toBe(true);

      const getResult = await service.getApiKey('user-123');
      expect(getResult.apiKey).toBe(newKey);
    });

    it('应该为不存在的用户创建新的API Key', async () => {
      const newKey = 'newkeyabcdefghijklmnopqrstuvwx';
      const result = await service.updateApiKey('new-user', newKey);
      expect(result.success).toBe(true);

      const getResult = await service.getApiKey('new-user');
      expect(getResult.apiKey).toBe(newKey);
    });
  });

  describe('deleteApiKey - 删除API Key', () => {
    it('应该成功删除已存在的API Key', async () => {
      await service.storeApiKey('user-123', 'abcdefghijklmnopqrstuvwxyz123456');

      const deleteResult = await service.deleteApiKey('user-123');
      expect(deleteResult).toBe(true);

      const getResult = await service.getApiKey('user-123');
      expect(getResult.success).toBe(false);
    });

    it('应该对不存在的API Key返回成功', async () => {
      const result = await service.deleteApiKey('nonexistent-user');
      expect(result).toBe(true);
    });
  });

  describe('hasApiKey - 检查API Key存在性', () => {
    it('应该返回true当API Key存在时', async () => {
      await service.storeApiKey('user-123', 'abcdefghijklmnopqrstuvwxyz123456');
      const result = await service.hasApiKey('user-123');
      expect(result).toBe(true);
    });

    it('应该返回false当API Key不存在时', async () => {
      const result = await service.hasApiKey('nonexistent-user');
      expect(result).toBe(false);
    });
  });

  describe('logSecurityEvent - 记录安全事件', () => {
    it('应该成功记录安全事件', async () => {
      await service.logSecurityEvent({
        eventType: SecurityEventType.API_KEY_STORED,
        severity: SecuritySeverity.INFO,
        userId: 'user-123',
        details: { action: 'test' },
        timestamp: new Date(),
      });

      expect(mockRedisService.setex).toHaveBeenCalled();
      expect(Logger.prototype.log).toHaveBeenCalled();
    });

    it('应该根据严重级别使用不同的日志级别', async () => {
      // 测试WARNING级别
      await service.logSecurityEvent({
        eventType: SecurityEventType.SUSPICIOUS_ACCESS,
        severity: SecuritySeverity.WARNING,
        userId: 'user-123',
        details: { reason: 'test' },
        timestamp: new Date(),
      });
      expect(Logger.prototype.warn).toHaveBeenCalled();

      // 测试CRITICAL级别
      await service.logSecurityEvent({
        eventType: SecurityEventType.API_KEY_DECRYPTION_FAILED,
        severity: SecuritySeverity.CRITICAL,
        userId: 'user-123',
        details: { error: 'test' },
        timestamp: new Date(),
      });
      expect(Logger.prototype.error).toHaveBeenCalled();
    });
  });

  describe('getApiKeyStats - 获取API Key统计信息', () => {
    it('应该返回正确的统计信息', async () => {
      await service.storeApiKey('user-123', 'abcdefghijklmnopqrstuvwxyz123456');
      await service.getApiKey('user-123');
      await service.getApiKey('user-123');

      const stats = await service.getApiKeyStats('user-123');
      expect(stats.exists).toBe(true);
      expect(stats.accessCount).toBe(2);
      expect(stats.createdAt).toBeDefined();
    });

    it('应该返回不存在状态', async () => {
      const stats = await service.getApiKeyStats('nonexistent-user');
      expect(stats.exists).toBe(false);
    });
  });


  /**
   * 属性测试: API Key安全存储
   * 
   * **Feature: zulip-integration, Property 8: API Key安全存储**
   * **Validates: Requirements 7.1, 7.3**
   * 
   * 对于任何用户的Zulip API Key，系统应该使用加密方式存储在数据库中，
   * 并在检测到异常操作时记录安全日志
   */
  describe('Property 8: API Key安全存储', () => {
    /**
     * 属性: 对于任何有效的API Key，存储后应该能够正确获取
     * 验证需求 7.1: 存储Zulip API Key时系统应使用加密方式存储
     */
    it('对于任何有效的API Key，存储后应该能够正确获取', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key（16-64字符的字母数字字符串）
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, apiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储API Key
            const storeResult = await service.storeApiKey(userId.trim(), apiKey);
            expect(storeResult.success).toBe(true);

            // 获取API Key
            const getResult = await service.getApiKey(userId.trim());
            expect(getResult.success).toBe(true);
            expect(getResult.apiKey).toBe(apiKey);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何存储的API Key，存储的数据应该是加密的（不等于原始值）
     * 验证需求 7.1: 存储Zulip API Key时系统应使用加密方式存储
     */
    it('对于任何存储的API Key，存储的数据应该是加密的', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, apiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储API Key
            await service.storeApiKey(userId.trim(), apiKey);

            // 检查存储的数据
            const storageKey = `zulip:api_key:${userId.trim()}`;
            const storedData = memoryStore.get(storageKey);
            expect(storedData).toBeDefined();

            // 解析存储的数据
            const parsedData = JSON.parse(storedData!.value);

            // 验证存储的是加密数据，不是原始API Key
            expect(parsedData.encryptedKey).toBeDefined();
            expect(parsedData.encryptedKey).not.toBe(apiKey);
            expect(parsedData.iv).toBeDefined();
            expect(parsedData.authTag).toBeDefined();
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何无效格式的API Key，存储应该失败
     * 验证需求 7.1: API Key格式验证
     */
    it('对于任何无效格式的API Key，存储应该失败', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成无效的API Key（太短或包含特殊字符）
          fc.oneof(
            fc.string({ minLength: 0, maxLength: 15 }), // 太短
            fc.string({ minLength: 1, maxLength: 50 }).filter(s => /[!@#$%^&*()+=\[\]{}|;:'",.<>?/\\`~]/.test(s)) // 包含特殊字符
          ),
          async (userId, invalidApiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 尝试存储无效的API Key
            const result = await service.storeApiKey(userId.trim(), invalidApiKey);

            // 应该失败
            expect(result.success).toBe(false);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何API Key操作，应该记录安全日志
     * 验证需求 7.3: 检测到异常操作时系统应记录安全日志
     */
    it('对于任何API Key操作，应该记录安全日志', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, apiKey) => {
            // 清理之前的数据和mock调用
            memoryStore.clear();
            mockRedisService.setex.mockClear();

            // 存储API Key
            await service.storeApiKey(userId.trim(), apiKey);

            // 验证安全日志被记录（setex被调用用于存储安全日志）
            const setexCalls = mockRedisService.setex.mock.calls;
            const securityLogCalls = setexCalls.filter(
              call => call[0].includes('security_log')
            );
            expect(securityLogCalls.length).toBeGreaterThan(0);
          }
        ),
        { numRuns: 50 }
      );
    }, 60000);

    /**
     * 属性: 对于任何用户，更新API Key后应该返回新的Key
     * 验证需求 7.1: API Key更新功能
     */
    it('对于任何用户，更新API Key后应该返回新的Key', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成两个不同的有效API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, oldApiKey, newApiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储原始API Key
            await service.storeApiKey(userId.trim(), oldApiKey);

            // 更新API Key
            const updateResult = await service.updateApiKey(userId.trim(), newApiKey);
            expect(updateResult.success).toBe(true);

            // 获取API Key应该返回新的Key
            const getResult = await service.getApiKey(userId.trim());
            expect(getResult.success).toBe(true);
            expect(getResult.apiKey).toBe(newApiKey);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 对于任何删除操作，删除后API Key应该不存在
     * 验证需求 7.1: API Key删除功能
     */
    it('对于任何删除操作，删除后API Key应该不存在', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, apiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储API Key
            await service.storeApiKey(userId.trim(), apiKey);

            // 验证存在
            const existsBefore = await service.hasApiKey(userId.trim());
            expect(existsBefore).toBe(true);

            // 删除API Key
            const deleteResult = await service.deleteApiKey(userId.trim());
            expect(deleteResult).toBe(true);

            // 验证不存在
            const existsAfter = await service.hasApiKey(userId.trim());
            expect(existsAfter).toBe(false);

            // 获取应该失败
            const getResult = await service.getApiKey(userId.trim());
            expect(getResult.success).toBe(false);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 加密和解密应该是可逆的
     * 验证需求 7.1: 加密存储的正确性
     */
    it('加密和解密应该是可逆的', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          async (userId, apiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储API Key
            const storeResult = await service.storeApiKey(userId.trim(), apiKey);
            expect(storeResult.success).toBe(true);

            // 多次获取应该返回相同的值
            const result1 = await service.getApiKey(userId.trim());
            const result2 = await service.getApiKey(userId.trim());

            expect(result1.success).toBe(true);
            expect(result2.success).toBe(true);
            expect(result1.apiKey).toBe(result2.apiKey);
            expect(result1.apiKey).toBe(apiKey);
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);

    /**
     * 属性: 访问计数应该正确递增
     * 验证需求 7.3: 监控API Key访问
     */
    it('访问计数应该正确递增', async () => {
      await fc.assert(
        fc.asyncProperty(
          // 生成有效的用户ID
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          // 生成有效的API Key
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          // 生成访问次数（1-10次）
          fc.integer({ min: 1, max: 10 }),
          async (userId, apiKey, accessCount) => {
            // 清理之前的数据
            memoryStore.clear();

            // 存储API Key
            await service.storeApiKey(userId.trim(), apiKey);

            // 多次访问
            for (let i = 0; i < accessCount; i++) {
              await service.getApiKey(userId.trim());
            }

            // 检查统计信息
            const stats = await service.getApiKeyStats(userId.trim());
            expect(stats.exists).toBe(true);
            expect(stats.accessCount).toBe(accessCount);
          }
        ),
        { numRuns: 50 }
      );
    }, 60000);
  });


  // ==================== 补充测试用例 ====================

  describe('访问频率限制测试', () => {
    it('应该在超过频率限制时拒绝访问', async () => {
      const userId = 'rate-limit-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      
      // 存储API Key
      await service.storeApiKey(userId, apiKey);

      // 模拟已达到频率限制
      const rateLimitKey = `zulip:api_key_access:${userId}`;
      memoryStore.set(rateLimitKey, { value: '60' });

      // 尝试获取API Key应该被拒绝
      const result = await service.getApiKey(userId);
      expect(result.success).toBe(false);
      expect(result.message).toContain('访问频率过高');
    });

    it('应该正确处理频率限制计数', async () => {
      const userId = 'counter-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      
      await service.storeApiKey(userId, apiKey);

      // 连续访问多次
      for (let i = 0; i < 5; i++) {
        const result = await service.getApiKey(userId);
        expect(result.success).toBe(true);
      }

      // 检查计数器
      const rateLimitKey = `zulip:api_key_access:${userId}`;
      const count = await mockRedisService.get(rateLimitKey);
      expect(parseInt(count || '0', 10)).toBe(5);
    });

    it('应该在Redis错误时默认允许访问', async () => {
      const userId = 'redis-error-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      
      await service.storeApiKey(userId, apiKey);

      // 模拟Redis错误
      mockRedisService.get.mockRejectedValueOnce(new Error('Redis connection failed'));
      mockRedisService.incr.mockRejectedValueOnce(new Error('Redis connection failed'));

      // 应该仍然允许访问
      const result = await service.getApiKey(userId);
      expect(result.success).toBe(true);
      expect(result.apiKey).toBe(apiKey);
    });
  });

  describe('Redis错误处理测试', () => {
    it('应该处理存储时的Redis错误', async () => {
      mockRedisService.set.mockRejectedValueOnce(new Error('Redis connection failed'));

      const result = await service.storeApiKey('user-123', 'abcdefghijklmnopqrstuvwxyz123456');
      expect(result.success).toBe(false);
      expect(result.message).toContain('存储失败');
    });

    it('应该处理获取时的Redis错误', async () => {
      // 模拟所有Redis get调用都失败
      mockRedisService.get.mockRejectedValue(new Error('Redis connection failed'));

      const result = await service.getApiKey('user-123');
      expect(result.success).toBe(false);
      expect(result.message).toContain('获取失败');
    });

    it('应该处理删除时的Redis错误', async () => {
      mockRedisService.del.mockRejectedValueOnce(new Error('Redis connection failed'));

      const result = await service.deleteApiKey('user-123');
      expect(result).toBe(false);
    });

    it('应该处理检查存在性时的Redis错误', async () => {
      mockRedisService.exists.mockRejectedValueOnce(new Error('Redis connection failed'));

      const result = await service.hasApiKey('user-123');
      expect(result).toBe(false);
    });

    it('应该处理获取统计信息时的Redis错误', async () => {
      mockRedisService.get.mockRejectedValueOnce(new Error('Redis connection failed'));

      const stats = await service.getApiKeyStats('user-123');
      expect(stats.exists).toBe(false);
    });
  });

  describe('数据损坏处理测试', () => {
    it('应该处理损坏的JSON数据', async () => {
      const userId = 'corrupted-data-user';
      const storageKey = `zulip:api_key:${userId}`;
      
      // 存储损坏的JSON数据
      memoryStore.set(storageKey, { value: 'invalid-json-data' });

      const result = await service.getApiKey(userId);
      expect(result.success).toBe(false);
      expect(result.message).toContain('获取失败');
    });

    it('应该处理缺少必要字段的数据', async () => {
      const userId = 'incomplete-data-user';
      const storageKey = `zulip:api_key:${userId}`;
      
      // 存储不完整的数据
      const incompleteData = {
        encryptedKey: 'some-encrypted-data',
        // 缺少 iv 和 authTag
      };
      memoryStore.set(storageKey, { value: JSON.stringify(incompleteData) });

      const result = await service.getApiKey(userId);
      expect(result.success).toBe(false);
    });
  });

  describe('可疑访问记录测试', () => {
    it('应该记录可疑访问事件', async () => {
      const userId = 'suspicious-user';
      const reason = 'multiple_failed_attempts';
      const details = { attemptCount: 5, timeWindow: '1min' };
      const metadata = { ipAddress: '192.168.1.100', userAgent: 'TestAgent' };

      await service.logSuspiciousAccess(userId, reason, details, metadata);

      // 验证安全日志被记录
      expect(mockRedisService.setex).toHaveBeenCalled();
      const setexCalls = mockRedisService.setex.mock.calls;
      const securityLogCall = setexCalls.find(call => call[0].includes('security_log'));
      expect(securityLogCall).toBeDefined();

      // 验证日志内容
      const logData = JSON.parse(securityLogCall![2]);
      expect(logData.eventType).toBe(SecurityEventType.SUSPICIOUS_ACCESS);
      expect(logData.severity).toBe(SecuritySeverity.WARNING);
      expect(logData.details.reason).toBe(reason);
      expect(logData.ipAddress).toBe(metadata.ipAddress);
    });
  });

  describe('元数据处理测试', () => {
    it('应该在安全日志中记录IP地址和User-Agent', async () => {
      const userId = 'metadata-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      const metadata = {
        ipAddress: '192.168.1.100',
        userAgent: 'Mozilla/5.0 (Test Browser)',
      };

      await service.storeApiKey(userId, apiKey, metadata);

      // 验证元数据被记录在安全日志中
      const setexCalls = mockRedisService.setex.mock.calls;
      const securityLogCall = setexCalls.find(call => call[0].includes('security_log'));
      expect(securityLogCall).toBeDefined();

      const logData = JSON.parse(securityLogCall![2]);
      expect(logData.ipAddress).toBe(metadata.ipAddress);
      expect(logData.userAgent).toBe(metadata.userAgent);
    });

    it('应该处理缺少元数据的情况', async () => {
      const userId = 'no-metadata-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';

      // 不提供元数据
      const result = await service.storeApiKey(userId, apiKey);
      expect(result.success).toBe(true);

      // 验证安全日志仍然被记录
      const setexCalls = mockRedisService.setex.mock.calls;
      const securityLogCall = setexCalls.find(call => call[0].includes('security_log'));
      expect(securityLogCall).toBeDefined();
    });
  });

  describe('边界条件测试', () => {
    it('应该处理极长的用户ID', async () => {
      const longUserId = 'a'.repeat(1000);
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';

      const result = await service.storeApiKey(longUserId, apiKey);
      expect(result.success).toBe(true);

      const getResult = await service.getApiKey(longUserId);
      expect(getResult.success).toBe(true);
      expect(getResult.apiKey).toBe(apiKey);
    });

    it('应该处理最大长度的API Key', async () => {
      const userId = 'max-key-user';
      const maxLengthApiKey = 'a'.repeat(128); // 最大允许长度

      const result = await service.storeApiKey(userId, maxLengthApiKey);
      expect(result.success).toBe(true);

      const getResult = await service.getApiKey(userId);
      expect(getResult.success).toBe(true);
      expect(getResult.apiKey).toBe(maxLengthApiKey);
    });

    it('应该拒绝超长的API Key', async () => {
      const userId = 'overlong-key-user';
      const overlongApiKey = 'a'.repeat(129); // 超过最大长度

      const result = await service.storeApiKey(userId, overlongApiKey);
      expect(result.success).toBe(false);
      expect(result.message).toContain('格式无效');
    });

    it('应该处理最小长度的API Key', async () => {
      const userId = 'min-key-user';
      const minLengthApiKey = 'a'.repeat(16); // 最小允许长度

      const result = await service.storeApiKey(userId, minLengthApiKey);
      expect(result.success).toBe(true);

      const getResult = await service.getApiKey(userId);
      expect(getResult.success).toBe(true);
      expect(getResult.apiKey).toBe(minLengthApiKey);
    });
  });

  describe('时间相关测试', () => {
    it('应该正确设置创建时间和更新时间', async () => {
      const userId = 'time-test-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      const beforeStore = new Date();

      await service.storeApiKey(userId, apiKey);

      const stats = await service.getApiKeyStats(userId);
      expect(stats.exists).toBe(true);
      expect(stats.createdAt).toBeDefined();
      expect(stats.updatedAt).toBeDefined();
      expect(stats.createdAt!.getTime()).toBeGreaterThanOrEqual(beforeStore.getTime());
      expect(stats.updatedAt!.getTime()).toBeGreaterThanOrEqual(beforeStore.getTime());
    });

    it('应该在访问时更新最后访问时间', async () => {
      const userId = 'access-time-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';

      await service.storeApiKey(userId, apiKey);
      
      // 等待一小段时间
      await new Promise(resolve => setTimeout(resolve, 10));
      
      const beforeAccess = new Date();
      await service.getApiKey(userId);

      const stats = await service.getApiKeyStats(userId);
      expect(stats.lastAccessedAt).toBeDefined();
      expect(stats.lastAccessedAt!.getTime()).toBeGreaterThanOrEqual(beforeAccess.getTime());
    });

    it('应该在更新时保持创建时间不变', async () => {
      const userId = 'update-time-user';
      const oldApiKey = 'abcdefghijklmnopqrstuvwxyz123456';
      const newApiKey = 'newkeyabcdefghijklmnopqrstuvwx';

      await service.storeApiKey(userId, oldApiKey);
      const statsAfterStore = await service.getApiKeyStats(userId);
      const originalCreatedAt = statsAfterStore.createdAt;

      // 等待一小段时间
      await new Promise(resolve => setTimeout(resolve, 10));

      await service.updateApiKey(userId, newApiKey);
      const statsAfterUpdate = await service.getApiKeyStats(userId);

      expect(statsAfterUpdate.createdAt).toEqual(originalCreatedAt);
      expect(statsAfterUpdate.updatedAt!.getTime()).toBeGreaterThan(statsAfterStore.updatedAt!.getTime());
    });
  });

  describe('并发访问测试', () => {
    it('应该处理并发的API Key访问', async () => {
      const userId = 'concurrent-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';

      await service.storeApiKey(userId, apiKey);

      // 并发访问 - 使用串行方式来确保计数正确
      const results = [];
      for (let i = 0; i < 10; i++) {
        const result = await service.getApiKey(userId);
        results.push(result);
      }

      // 所有访问都应该成功
      results.forEach(result => {
        expect(result.success).toBe(true);
        expect(result.apiKey).toBe(apiKey);
      });

      // 访问计数应该正确
      const stats = await service.getApiKeyStats(userId);
      expect(stats.accessCount).toBe(10);
    });

    it('应该处理并发的存储和获取操作', async () => {
      const userId = 'concurrent-store-get-user';
      const apiKey = 'abcdefghijklmnopqrstuvwxyz123456';

      // 并发执行存储和获取操作
      const storePromise = service.storeApiKey(userId, apiKey);
      const getPromise = service.getApiKey(userId);

      const [storeResult, getResult] = await Promise.all([storePromise, getPromise]);

      // 存储应该成功
      expect(storeResult.success).toBe(true);
      
      // 获取可能成功也可能失败（取决于执行顺序）
      if (getResult.success) {
        expect(getResult.apiKey).toBe(apiKey);
      } else {
        expect(getResult.message).toContain('不存在');
      }
    });
  });

  describe('安全事件记录错误处理', () => {
    it('应该处理记录安全事件时的Redis错误', async () => {
      mockRedisService.setex.mockRejectedValueOnce(new Error('Redis connection failed'));

      // 记录安全事件不应该抛出异常
      await expect(service.logSecurityEvent({
        eventType: SecurityEventType.API_KEY_STORED,
        severity: SecuritySeverity.INFO,
        userId: 'test-user',
        details: { action: 'test' },
        timestamp: new Date(),
      })).resolves.not.toThrow();

      // 应该记录错误日志
      expect(Logger.prototype.error).toHaveBeenCalledWith(
        '记录安全事件失败',
        expect.any(Object)
      );
    });
  });

  describe('环境变量处理测试', () => {
    it('应该在没有环境变量时使用默认密钥并记录警告', () => {
      // 由于当前测试环境有ZULIP_API_KEY_ENCRYPTION_KEY环境变量，
      // 这个测试验证的是当环境变量存在时不会记录警告
      expect(Logger.prototype.warn).not.toHaveBeenCalledWith(
        expect.stringContaining('使用默认加密密钥')
      );
    });
  });

  describe('属性测试 - 错误处理和边界条件', () => {
    /**
     * 属性测试: 任何Redis错误都不应该导致服务崩溃
     */
    it('任何Redis错误都不应该导致服务崩溃', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.string({ minLength: 1, maxLength: 50 }).filter(s => s.trim().length > 0),
          fc.stringMatching(/^[a-zA-Z0-9_-]{16,64}$/),
          fc.constantFrom('set', 'get', 'del', 'exists', 'setex', 'incr'),
          async (userId, apiKey, failingMethod) => {
            // 清理之前的数据
            memoryStore.clear();
            jest.clearAllMocks();

            // 模拟特定方法失败
            (mockRedisService as any)[failingMethod].mockRejectedValueOnce(
              new Error(`${failingMethod} failed`)
            );

            // 执行操作不应该抛出异常
            await expect(service.storeApiKey(userId.trim(), apiKey)).resolves.not.toThrow();
            await expect(service.getApiKey(userId.trim())).resolves.not.toThrow();
            await expect(service.deleteApiKey(userId.trim())).resolves.not.toThrow();
            await expect(service.hasApiKey(userId.trim())).resolves.not.toThrow();
          }
        ),
        { numRuns: 50 }
      );
    }, 30000);

    /**
     * 属性测试: 任何输入都不应该导致服务崩溃
     */
    it('任何输入都不应该导致服务崩溃', async () => {
      await fc.assert(
        fc.asyncProperty(
          fc.string({ maxLength: 1000 }), // 任意字符串作为用户ID
          fc.string({ maxLength: 1000 }), // 任意字符串作为API Key
          async (userId, apiKey) => {
            // 清理之前的数据
            memoryStore.clear();

            // 任何输入都不应该导致崩溃
            await expect(service.storeApiKey(userId, apiKey)).resolves.not.toThrow();
            await expect(service.getApiKey(userId)).resolves.not.toThrow();
            await expect(service.updateApiKey(userId, apiKey)).resolves.not.toThrow();
            await expect(service.deleteApiKey(userId)).resolves.not.toThrow();
            await expect(service.hasApiKey(userId)).resolves.not.toThrow();
            await expect(service.getApiKeyStats(userId)).resolves.not.toThrow();
          }
        ),
        { numRuns: 100 }
      );
    }, 60000);
  });
});
