单元测试全绿,不代表你就能上线
我们卖的是身份认证,这让我们最害怕的那个问题变得格外简单:当客户把他们的应用接到我们这里、把所有功能都打开时,它在真实系统上、从头到尾,真的跑得通吗?不是"单元测试过没过",而是:一个全新的租户能不能注册、配置好 SSO 和 SCIM 和 MFA 和自定义 claim 以及品牌外观,把一个真实的应用指向它,再让一个真实用户带着 token 里正确的 claim、通过一个真实的浏览器登录进来。我们用唯一让自己信得过的方式来回答这个问题:用一个会变成客户的端到端测试。
这是一次单独的 Playwright 运行,刻意做成串行且有状态,它走完一个租户从诞生到删除的整个生命周期。一个租户注册、被配置到牙齿、为一个真实的消费端应用提供真实登录、被备份并恢复,然后把自己删除。这条链上只要断了任何一环,这次运行就会变红,我们就不发布。
它把一切都配置了,而不是只走通幸福路径的一小片
测试的中段是刻意做得彻底的。它不是登录一下就算完事;它走遍真实实施者会碰到的每一个界面,并且真刀真枪地用上:一个带重定向 URI 和 token 设置的自定义 OIDC 客户端,一个会发出用户 claim 的自定义 scope,若干角色以及一次角色分配,一个带自定义属性的终端用户,一个有真实成员关系并带组到角色映射的组,SAML 和 OIDC 企业连接,一个 SCIM 预配 token,一个自定义域名,一个出站预配应用,一个被邀请并改派了角色的队友,通过真实结账完成的计费,带筛选器和 CSV 导出的审计日志,一个沙箱环境,以及安全、webhook 和邮件设置(每一项都被切换过,然后再恢复到安全状态)。这其中的每一项,都在同一次运行里被创建、被验证,并在合理之处被删除。
重点不是打钩。客户从不会孤立地用一个功能;他们用的是一个组合,而组合恰恰是东西悄无声息出问题的地方。一个自定义 claim 只有出现在签发出的 token 里才算数。一个组到角色的映射只有在 token 被铸造的那一刻角色恰好落位才算数。要知道答案,唯一的办法就是把整套东西搭起来,然后真的去用它。
凭据之舞
下面这部分,正是让一个身份认证产品的端到端测试变得真正棘手的地方:测试开始时,凭据根本还不存在。你没法把客户端密钥或 SCIM token 写死,因为整件事的关键就在于:租户要在运行过程中现场铸造出它们。
所以测试做的,正是真实实施者会做的事,只是更快。它在门户里创建 OIDC 客户端,并把客户端 id 和密钥读回来。它一键铸造出一个 Portal API 凭据并抓取下来。它生成一个 SCIM token。然后它把这些刚铸出来的密钥逐个喂给一旁待命的示例消费端应用,用新值重写那个应用的配置,将其发布出去,到这时才驱动真正的登录。在某一步诞生的凭据,会在下一步被注入到受测应用里,浏览器再据此完成一次真实的 OIDC 重定向。这套集成是一个移动的靶子,随着它逐渐成形,测试不断地把自己重新瞄准上去。
那个消费端应用是一次真实的部署,不是 mock。当测试验证一次登录时,是一个真实的应用真的重定向到了租户的颁发者,真的拿回了一个 code,真的把它换了,测试再把得到的 token 解码,确认自定义 claim 和映射出的角色货真价实地在里头。然后它驱动那些更难啃的边角:一个藏在启用开关后面的自定义品牌登录页,以及一个必须拦下被策略拒绝之登录的强制 webhook。最后这一项跑出绿色,意味着拒绝路径是有效的,而这正是你最想确信无疑的结果。
多因素,动真格的
光靠一次密码登录证明不了多少。这次运行会注册并随后使用一个 TOTP 认证器,以及一个通过虚拟认证器接入的 WebAuthn 凭据,让第二因素像真实用户那样被真正用上,而不是被打桩糊弄过去。
人人都跳过的那一半:把它找回来
大多数端到端测试停在"它能用"。我们的会继续往下,走进那些你只有在凌晨两点才会想到的部分。它做一次备份,把租户的数据从它自己脚下删掉,再从那次备份恢复,并验证配置和用户都完好无损地回来了。然后它对整个租户跑一次干净的自助删除。一次运行不留下任何残余,而这也正是我们如何知道:解除预配是真的解除了预配。
为什么这是我们信得过的那个测试
一墙绿色的单元测试告诉你的是:你的函数是正确的。这个测试告诉你的是:一个客户可以走进来,搭出他们真正想要的集成,配上他们真正需要的安全功能,而且我们能把他们的数据弄丢、再原样交还回去。这就是"代码是对的"和"我们可以上线了"之间的区别。
这次运行所演练的每一项功能(SSO 和 SAML、SCIM、MFA、自定义 claim 和 scope、品牌外观、强制 webhook、审计导出),在产品的每一个套餐层级里都有,并没有被锁在企业版加购的后面。看看都包含了什么。