2
0

08_agent_grants.php 3.8 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. <?php
  2. use \WikipaliApi\Api\AgentGrantsApi;
  3. use \WikipaliApi\Model\PostApiV3AgentGrantsRequest;
  4. /*
  5. * agent 协作:人类签发写入授权(POST /v3/agent-grants),模型凭模型 token + X-Agent-Grant 代写。
  6. *
  7. * 完整代写链路需要一个归测试账号所有的 AI 模型:在 config.local.php 的
  8. * fixtures.agent_model 填它的 uid,没填则 SKIP。模型 token 目前只有 v2 端点能取
  9. * (GET /v2/ai-model-token/{uid}),这里只把它当准备步骤用。
  10. * 权限矩阵(别的 channel / book、撤销、伪造、v2 token 冒充)由 api-v13 的 AgentGrantV3Test 覆盖。
  11. */
  12. function agent_grant_body(string $model, string $channel): PostApiV3AgentGrantsRequest
  13. {
  14. $body = new PostApiV3AgentGrantsRequest();
  15. $body->setAiModel($model);
  16. $body->setTargetType('channel');
  17. $body->setTargetId($channel);
  18. return $body;
  19. }
  20. test('POST /v3/agent-grants(无 token)→ 401', function () {
  21. $res = request(fn () => api(AgentGrantsApi::class)->postApiV3AgentGrantsWithHttpInfo(
  22. agent_grant_body('00000000-0000-4000-8000-000000000000', fixtures()['channel'])
  23. ));
  24. assert_status(401, $res, 'agent-grants 无 token');
  25. assert_problem($res, 'agent-grants 无 token');
  26. });
  27. test('POST /v3/agent-grants(模型不存在)→ 422 errors.ai_model', function () {
  28. $res = request(fn () => api(AgentGrantsApi::class, required_token())->postApiV3AgentGrantsWithHttpInfo(
  29. agent_grant_body('00000000-0000-4000-8000-000000000000', fixtures()['channel'])
  30. ));
  31. assert_field_error('ai_model', $res, 'agent-grants 模型不存在');
  32. });
  33. test('POST /v3/agent-grants(target_type 不是 channel)→ 422', function () {
  34. $res = raw('POST', '/v3/agent-grants', null, [
  35. 'ai_model' => '00000000-0000-4000-8000-000000000000', 'target_type' => 'article', 'target_id' => fixtures()['channel'],
  36. ], required_token());
  37. assert_field_error('target_type', $res, 'agent-grants target_type');
  38. });
  39. test('agent 代写一轮:签授权 → 模型带授权建批注(editor=人、co_editor=模型)→ 不带授权 403 → 人删掉', function () {
  40. $model = fixtures()['agent_model'] ?? null;
  41. if (! $model) {
  42. skip('config.local.php 未配置 fixtures.agent_model(归测试账号所有的 AI 模型 uid)');
  43. }
  44. $token = required_token();
  45. $sentence = fixtures()['discussion_sentence'];
  46. $channel = fixtures()['discussion_channel'];
  47. $grant = request(fn () => api(AgentGrantsApi::class, $token)->postApiV3AgentGrantsWithHttpInfo(agent_grant_body($model, $channel)));
  48. assert_status(201, $grant, '签授权');
  49. $grantToken = $grant['body']['data']['token'] ?? null;
  50. // 准备:取模型 token(只有 v2 端点)
  51. $modelToken = raw('GET', "/v2/ai-model-token/{$model}", null, null, $token)['body']['data']['token'] ?? null;
  52. if (! $modelToken) {
  53. fail('取不到模型 token:fixtures.agent_model 是否归测试账号所有?');
  54. }
  55. $body = ['target_type' => 'sentence', 'target_id' => $sentence, 'type' => 'note', 'content' => 'api-test 代写的批注'];
  56. $denied = raw('POST', '/v3/discussions', null, $body, $modelToken);
  57. assert_status(403, $denied, '模型不带授权');
  58. $created = raw('POST', '/v3/discussions', null, $body, $modelToken, ['X-Agent-Grant' => $grantToken]);
  59. assert_status(201, $created, '模型带授权');
  60. $id = $created['body']['data']['id'] ?? null;
  61. try {
  62. assert_eq($model, $created['body']['data']['co_editor']['id'] ?? null, 'co_editor 是模型');
  63. if (($created['body']['data']['editor']['id'] ?? null) === $model) {
  64. fail('editor 应是授权人,不是模型');
  65. }
  66. } finally {
  67. $del = raw('DELETE', "/v3/discussions/{$id}", null, null, $token);
  68. }
  69. assert_status(200, $del, '人删掉模型写的');
  70. });