#!/usr/bin/env python3 """ InsightFlow Phase 8 - Multi-Tenant SaaS Test Script 多租户 SaaS 架构测试脚本 """ import sys import os sys.path.insert(0, os.path.dirname(os.path.abspath(__file__))) from tenant_manager import ( get_tenant_manager, TenantManager, Tenant, TenantDomain, TenantBranding, TenantMember, TenantStatus, TenantTier, TenantRole, DomainStatus, TenantContext ) def test_tenant_management(): """测试租户管理功能""" print("=" * 60) print("测试租户管理功能") print("=" * 60) # 使用测试数据库 test_db = "test_tenant.db" if os.path.exists(test_db): os.remove(test_db) manager = get_tenant_manager(test_db) # 1. 创建租户 print("\n1. 创建租户...") try: tenant = manager.create_tenant( name="Test Company", owner_id="user_001", tier="pro", description="A test tenant for validation", settings={"theme": "dark"} ) print(f" ✓ 租户创建成功: {tenant.id}") print(f" - 名称: {tenant.name}") print(f" - Slug: {tenant.slug}") print(f" - 层级: {tenant.tier}") print(f" - 状态: {tenant.status}") print(f" - 资源限制: {tenant.resource_limits}") except Exception as e: print(f" ✗ 租户创建失败: {e}") import traceback traceback.print_exc() return False # 2. 获取租户 print("\n2. 获取租户...") try: fetched = manager.get_tenant(tenant.id) assert fetched is not None assert fetched.name == tenant.name print(f" ✓ 通过 ID 获取租户成功") fetched_by_slug = manager.get_tenant_by_slug(tenant.slug) assert fetched_by_slug is not None assert fetched_by_slug.id == tenant.id print(f" ✓ 通过 Slug 获取租户成功") except Exception as e: print(f" ✗ 获取租户失败: {e}") import traceback traceback.print_exc() return False # 3. 更新租户 print("\n3. 更新租户...") try: updated = manager.update_tenant( tenant.id, name="Test Company Updated", tier="enterprise" ) assert updated is not None assert updated.name == "Test Company Updated" assert updated.tier == "enterprise" print(f" ✓ 租户更新成功") print(f" - 新名称: {updated.name}") print(f" - 新层级: {updated.tier}") except Exception as e: print(f" ✗ 租户更新失败: {e}") import traceback traceback.print_exc() return False # 4. 列出租户 print("\n4. 列出租户...") try: tenants = manager.list_tenants() assert len(tenants) >= 1 print(f" ✓ 列出租户成功,共 {len(tenants)} 个租户") except Exception as e: print(f" ✗ 列出租户失败: {e}") return False return tenant.id def test_domain_management(tenant_id: str): """测试域名管理功能""" print("\n" + "=" * 60) print("测试域名管理功能") print("=" * 60) manager = get_tenant_manager("test_tenant.db") # 1. 添加域名 print("\n1. 添加自定义域名...") try: domain = manager.add_domain(tenant_id, "app.example.com", is_primary=True) print(f" ✓ 域名添加成功: {domain.id}") print(f" - 域名: {domain.domain}") print(f" - 状态: {domain.status}") print(f" - 验证令牌: {domain.verification_token}") print(f" - 是否主域名: {domain.is_primary}") except Exception as e: print(f" ✗ 域名添加失败: {e}") import traceback traceback.print_exc() return False # 2. 获取域名验证指导 print("\n2. 获取域名验证指导...") try: instructions = manager.get_domain_verification_instructions(domain.id) assert instructions is not None print(f" ✓ 获取验证指导成功") print(f" - DNS 记录: {instructions['dns_record']}") except Exception as e: print(f" ✗ 获取验证指导失败: {e}") return False # 3. 验证域名 print("\n3. 验证域名...") try: success = manager.verify_domain(tenant_id, domain.id) if success: print(f" ✓ 域名验证成功") else: print(f" ! 域名验证返回 False(可能是模拟验证)") except Exception as e: print(f" ✗ 域名验证失败: {e}") return False # 4. 获取域名列表 print("\n4. 获取域名列表...") try: domains = manager.list_domains(tenant_id) assert len(domains) >= 1 print(f" ✓ 获取域名列表成功,共 {len(domains)} 个域名") for d in domains: print(f" - {d.domain} ({d.status})") except Exception as e: print(f" ✗ 获取域名列表失败: {e}") return False # 5. 通过域名获取租户 print("\n5. 通过域名解析租户...") try: resolved = manager.get_tenant_by_domain("app.example.com") if resolved: assert resolved.id == tenant_id print(f" ✓ 域名解析租户成功") else: print(f" ! 域名解析租户返回 None(可能域名未激活)") except Exception as e: print(f" ✗ 域名解析失败: {e}") return False return True def test_branding_management(tenant_id: str): """测试品牌配置管理功能""" print("\n" + "=" * 60) print("测试品牌配置管理功能") print("=" * 60) manager = get_tenant_manager("test_tenant.db") # 1. 更新品牌配置 print("\n1. 更新品牌配置...") try: branding = manager.update_branding( tenant_id, logo_url="https://example.com/logo.png", favicon_url="https://example.com/favicon.ico", primary_color="#FF5733", secondary_color="#33FF57", custom_css="body { font-size: 14px; }", custom_js="console.log('Custom JS loaded');" ) assert branding is not None print(f" ✓ 品牌配置更新成功") print(f" - Logo: {branding.logo_url}") print(f" - 主色调: {branding.primary_color}") print(f" - 次色调: {branding.secondary_color}") except Exception as e: print(f" ✗ 品牌配置更新失败: {e}") import traceback traceback.print_exc() return False # 2. 获取品牌配置 print("\n2. 获取品牌配置...") try: fetched = manager.get_branding(tenant_id) assert fetched is not None assert fetched.primary_color == "#FF5733" print(f" ✓ 获取品牌配置成功") except Exception as e: print(f" ✗ 获取品牌配置失败: {e}") return False # 3. 生成品牌 CSS print("\n3. 生成品牌 CSS...") try: css = manager.get_branding_css(tenant_id) assert "--tenant-primary" in css assert "#FF5733" in css print(f" ✓ 品牌 CSS 生成成功") print(f" - CSS 长度: {len(css)} 字符") except Exception as e: print(f" ✗ 品牌 CSS 生成失败: {e}") return False return True def test_member_management(tenant_id: str): """测试成员管理功能""" print("\n" + "=" * 60) print("测试成员管理功能") print("=" * 60) manager = get_tenant_manager("test_tenant.db") # 1. 邀请成员 print("\n1. 邀请成员...") try: member = manager.invite_member( tenant_id=tenant_id, email="user@example.com", role="admin", invited_by="user_001" ) print(f" ✓ 成员邀请成功: {member.id}") print(f" - 邮箱: {member.email}") print(f" - 角色: {member.role}") print(f" - 状态: {member.status}") print(f" - 权限: {member.permissions}") except Exception as e: print(f" ✗ 成员邀请失败: {e}") import traceback traceback.print_exc() return False # 2. 获取成员列表 print("\n2. 获取成员列表...") try: members = manager.list_members(tenant_id) assert len(members) >= 2 # owner + invited member print(f" ✓ 获取成员列表成功,共 {len(members)} 个成员") for m in members: print(f" - {m.email} ({m.role}, {m.status})") except Exception as e: print(f" ✗ 获取成员列表失败: {e}") return False # 3. 接受邀请 print("\n3. 接受邀请...") try: # 注意:accept_invitation 使用的是 member id 而不是 token # 修正:查看源码后发现它接受的是 invitation_id(即 member id) accepted = manager.accept_invitation(member.id, "user_002") if accepted: print(f" ✓ 邀请接受成功") else: print(f" ! 邀请接受返回 False(可能是状态不对)") except Exception as e: print(f" ✗ 邀请接受失败: {e}") import traceback traceback.print_exc() return False # 4. 更新成员角色 print("\n4. 更新成员角色...") try: success = manager.update_member_role( tenant_id=tenant_id, member_id=member.id, role="member" ) if success: print(f" ✓ 成员角色更新成功") else: print(f" ! 成员角色更新返回 False") except Exception as e: print(f" ✗ 成员角色更新失败: {e}") import traceback traceback.print_exc() return False # 5. 检查权限 print("\n5. 检查用户权限...") try: # 检查 owner 权限 has_permission = manager.check_permission( tenant_id=tenant_id, user_id="user_001", resource="project", action="create" ) print(f" ✓ 权限检查成功") print(f" - Owner 是否有 project:create 权限: {has_permission}") except Exception as e: print(f" ✗ 权限检查失败: {e}") return False # 6. 获取用户租户列表 print("\n6. 获取用户租户列表...") try: user_tenants = manager.get_user_tenants("user_001") assert len(user_tenants) >= 1 print(f" ✓ 获取用户租户列表成功,共 {len(user_tenants)} 个租户") except Exception as e: print(f" ✗ 获取用户租户列表失败: {e}") return False return True def test_usage_stats(tenant_id: str): """测试使用统计功能""" print("\n" + "=" * 60) print("测试使用统计功能") print("=" * 60) manager = get_tenant_manager("test_tenant.db") # 1. 记录使用 print("\n1. 记录资源使用...") try: manager.record_usage( tenant_id=tenant_id, storage_bytes=1024 * 1024 * 50, # 50MB transcription_seconds=600, # 10分钟 api_calls=100, projects_count=5, entities_count=50, members_count=3 ) print(f" ✓ 资源使用记录成功") except Exception as e: print(f" ✗ 资源使用记录失败: {e}") import traceback traceback.print_exc() return False # 2. 获取使用统计 print("\n2. 获取使用统计...") try: stats = manager.get_usage_stats(tenant_id) print(f" ✓ 使用统计获取成功") print(f" - 存储: {stats['storage_mb']:.2f} MB") print(f" - 转录: {stats['transcription_minutes']:.2f} 分钟") print(f" - API 调用: {stats['api_calls']}") print(f" - 项目数: {stats['projects_count']}") print(f" - 实体数: {stats['entities_count']}") print(f" - 成员数: {stats['members_count']}") print(f" - 配额: {stats['limits']}") except Exception as e: print(f" ✗ 使用统计获取失败: {e}") import traceback traceback.print_exc() return False # 3. 检查资源限制 print("\n3. 检查资源限制...") try: allowed, current, limit = manager.check_resource_limit(tenant_id, "storage") print(f" ✓ 资源限制检查成功") print(f" - 存储: {allowed}, 当前: {current}, 限制: {limit}") except Exception as e: print(f" ✗ 资源限制检查失败: {e}") import traceback traceback.print_exc() return False return True def test_tenant_context(): """测试租户上下文管理""" print("\n" + "=" * 60) print("测试租户上下文管理") print("=" * 60) # 1. 设置和获取租户上下文 print("\n1. 设置和获取租户上下文...") try: TenantContext.set_current_tenant("tenant_123") tenant_id = TenantContext.get_current_tenant() assert tenant_id == "tenant_123" print(f" ✓ 租户上下文设置成功: {tenant_id}") except Exception as e: print(f" ✗ 租户上下文设置失败: {e}") return False # 2. 设置和获取用户上下文 print("\n2. 设置和获取用户上下文...") try: TenantContext.set_current_user("user_456") user_id = TenantContext.get_current_user() assert user_id == "user_456" print(f" ✓ 用户上下文设置成功: {user_id}") except Exception as e: print(f" ✗ 用户上下文设置失败: {e}") return False # 3. 清除上下文 print("\n3. 清除上下文...") try: TenantContext.clear() assert TenantContext.get_current_tenant() is None assert TenantContext.get_current_user() is None print(f" ✓ 上下文清除成功") except Exception as e: print(f" ✗ 上下文清除失败: {e}") return False return True def cleanup(): """清理测试数据""" print("\n" + "=" * 60) print("清理测试数据") print("=" * 60) test_db = "test_tenant.db" if os.path.exists(test_db): os.remove(test_db) print(f"✓ 删除测试数据库: {test_db}") def main(): """主测试函数""" print("\n" + "=" * 60) print("InsightFlow Phase 8 - Multi-Tenant SaaS 测试") print("=" * 60) all_passed = True tenant_id = None try: # 测试租户上下文 if not test_tenant_context(): all_passed = False # 测试租户管理 tenant_id = test_tenant_management() if not tenant_id: all_passed = False # 测试域名管理 if not test_domain_management(tenant_id): all_passed = False # 测试品牌配置 if not test_branding_management(tenant_id): all_passed = False # 测试成员管理 if not test_member_management(tenant_id): all_passed = False # 测试使用统计 if not test_usage_stats(tenant_id): all_passed = False except Exception as e: print(f"\n测试过程中发生错误: {e}") import traceback traceback.print_exc() all_passed = False finally: cleanup() print("\n" + "=" * 60) if all_passed: print("✓ 所有测试通过!") else: print("✗ 部分测试失败") print("=" * 60) return 0 if all_passed else 1 if __name__ == "__main__": sys.exit(main())