


先写测试,再修改基于图的授权服务
修改基于图的授权服务时使用。它会追踪请求经过的路径,按仓库的构建目标和 lint 规则工作,先写一个失败的测试,并且不手动改动生成的文件。
要使用这个AI规则
## 把正确性放在第一位,并使用仓库的构建目标
按以下顺序应用项目优先级:
1. 正确性:每个授权判定都必须准确。
2. 可靠性:服务必须保持可用,并能以可预测的方式恢复。
3. 性能:只有在正确性和可靠性得到保护后,才可尽量降低延迟和内存分配。
将仓库维护的构建目标视为事实来源。优先使用它们,而不是临时命令,因为它们包含必需的生成处理、标志、竞争检测、覆盖率、lint和环境设置。
典型的目标组应包括:
- 生产构建;
- 完整测试套件;
- 针对指定用例的筛选测试;
- 包含内存指标的基准测试;
- 需要容器的集成测试;
- 使用仓库配置的工具版本执行lint;
- 生成的测试替身;
- 使用内存数据存储或容器数据存储进行本地开发。
运行目标前,先阅读其定义和必需的环境变量。如果规范目标不可用或含义不清,应解决该缺口,而不是默默替换为较弱的命令。
## 修改前,追踪请求经过解析器链的路径
修改行为前,先梳理请求经过的完整运行时路径。顺序如下:
1. 接收HTTP或RPC请求;
2. 应用中间件;
3. 进入传输处理器;
4. 调用命令或应用服务;
5. 解析授权图;
6. 访问存储;
7. 返回响应。
找出分别负责启动和关闭、处理器、业务命令、图计算、规划、存储接口及实现、模型解析、元组验证、身份认证和API授权的模块。
对于基于图的检查,应同时查看解析器构建器和解析器接口。只有配置启用时,解析器管线才可以添加缓存、分派节流或影子比较。
不要假定该链是线性的。某些解析器系统会形成循环委派链,最后一个解析器返回第一个解析器。修改分派行为前,要验证构建顺序、终止条件,以及可选包装器与核心本地检查器之间的区别。
将运行时连接视为比目录名称或设计假设更可靠的证据。
## 遵守 lint 的命名规则,让包装后的错误仍能被识别
编辑import或名称前,先阅读当前生效的lint配置。保留:
- 生成API和解析器所需的alias;
- 配置规定的import分组和顺序;
- 不带多余prefix的接口名称;
- 遵循`NewTypeName`模式的构造函数名称;
- 遵循`WithPropertyName`模式的函数式option名称;
- 形如`TypeNameOption func(*TypeName)`的option类型;
- 遵循项目导出命名风格的导出常量;
- 遵循项目本地命名风格的未导出常量和context key。
将稳定的领域错误定义为sentinel值。添加context时不得破坏错误标识:
- 声明类似`var ErrNotFound = errors.New("not found")`的sentinel;
- 使用类似`fmt.Errorf("load relationship: %w", ErrNotFound)`的方式添加context并包装。
使用`errors.Is`或`errors.As`进行匹配。不要直接比较已经包装的错误,也不要使用会丢失错误链的格式化动词。
在明确的边界执行领域到传输层的转换。命令逻辑应返回领域错误,由专用转换器将其映射为HTTP或RPC响应。
## 先写失败的测试,并维护授权的矩阵测试
对行为变更采用测试驱动开发:
1. 添加一个聚焦测试,表达缺失或错误的行为。
2. 运行该测试,确认它因预期原因失败。
3. 实现能使其通过的最小变更。
4. 先运行聚焦测试,再运行直接受影响的测试套件。
5. 只有测试全部通过后才进行重构。
对于授权算法,应维护matrix test,定义模型、关系元组,以及check API、对象列表API和用户列表API的预期结果。matrix的每个stage应让模型和assertion可以一起阅读。
最小matrix case应保留以下顺序和含义:
1. 为测试命名,例如`viewer_can_read_document`;
2. 在stage model中定义`user`和`document`类型;
3. 定义接受user的`viewer`关系;
4. 添加由`document:1`、`viewer`和`user:alice`组成的tuple;
5. assert使用相同object、relation和user进行check时返回`true`。
保持嵌入测试资源已在对应runner中注册。当API有多个列表或检查入口时,要验证每个相关runner,不要假定一个入口能覆盖其他入口。
修改高频图路径、解析器选择、存储访问或对内存分配敏感的代码时,应使用基准测试。对于数据存储特有的行为,应使用基于容器的测试。
## 不手动修改生成文件和其他仓库的 schema
不要直接编辑生成的mock、生成的protocol binding或其他生成产物。修改源文件或generator,然后通过规范目标重新生成。
不要在下游服务仓库中修改由外部管理的schema定义。应在拥有该定义的仓库中修改并评审schema,然后更新下游dependency。
要考虑以下运行陷阱:
- 完整测试目标可能自动重新生成mock,而直接运行语言级测试命令可能不会;
- SQL数据存储的集成测试可能需要容器运行时;
- 解析器递归可能是有意行为,因为该链是循环的;
- lint配置语法与版本有关;
- 修改存储接口时,所有backend及其共享contract test都必须一起调整。
如果所需的generator、容器运行时或上游schema不可用,应报告缺少的前提条件。不得手工修改派生产物来伪造完成状态。
## 新增 API 端点或修改存储时,走完全部步骤
新增API endpoint时:
1. 更新由本项目管理的API schema;
2. 添加传输处理器;
3. 添加命令或应用服务逻辑;
4. 在传输边界映射领域错误;
5. 添加聚焦测试和matrix test。
修改存储接口时:
1. 更新接口;
2. 更新共享存储contract test;
3. 为所有受支持的backend实现该变更;
4. 运行受影响的内存测试套件和基于容器的测试套件。
修改图解析器时:
1. 实现解析器接口;
2. 添加或更新builder option;
3. 验证条件组合和循环委派;
4. 测试正确性、节流或缓存交互以及终止条件;
5. 如果高频路径可能变化,则执行基准测试。
修改类型系统时,应将解析、验证、加权或优化的图逻辑及相关validation test作为一个完整变更一起更新。
宣称完成前,要检查diff中是否意外编辑了生成文件,确认聚焦测试已经从失败变为通过,运行直接受影响的规范目标,并准确记录任何未能运行的测试类别及其所缺少的前提条件。
