09_tipitaka_discussions.php 4.1 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889
  1. <?php
  2. use \WikipaliApi\Api\TipitakaDiscussionsApi;
  3. /*
  4. * 按经文范围列讨论:GET /v3/tipitaka-discussions/{channel}?book=&chapter=|para=&to=&type=…
  5. *
  6. * 只读端点。完整一轮借 discussions 的写接口在 fixtures 的 discussion_sentence 上建一条
  7. * issue(测试账号自己的句子,notify=false),按它所在的段落列出来核对,最后删掉。
  8. * 权限矩阵(匿名 / basic 可见性)、排序、墓碑由 api-v13 的 TipitakaDiscussionV3Test 覆盖。
  9. */
  10. /** fixture 句子的 book / paragraph(从 v2 单句接口取,各服务器的 fixture 不同) */
  11. function tipitaka_discussion_coord(): array
  12. {
  13. $res = raw('GET', '/v2/sentence/' . fixtures()['discussion_sentence']);
  14. assert_status(200, $res, '取 fixture 句子');
  15. $data = $res['body']['data'] ?? [];
  16. return [(int) ($data['book'] ?? 0), (int) ($data['paragraph'] ?? 0)];
  17. }
  18. test('GET /v3/tipitaka-discussions/{channel} → 200,列表 + 分页', function () {
  19. $res = request(fn () => api(TipitakaDiscussionsApi::class)
  20. ->getApiV3TipitakaDiscussionsChannelWithHttpInfo(fixtures()['discussion_channel']));
  21. assert_status(200, $res, 'tipitaka-discussions');
  22. assert_has_key('data', $res['body'], 'tipitaka-discussions');
  23. assert_has_key('meta', $res['body'], 'tipitaka-discussions');
  24. });
  25. test('GET /v3/tipitaka-discussions/{channel}(chapter 不带 book)→ 422', function () {
  26. $res = raw('GET', '/v3/tipitaka-discussions/' . fixtures()['discussion_channel'], ['chapter' => 1]);
  27. assert_status(422, $res, 'chapter 不带 book');
  28. assert_field_error('book', $res, 'chapter 不带 book');
  29. });
  30. test('GET /v3/tipitaka-discussions/{channel}(非法 type)→ 422', function () {
  31. $res = raw('GET', '/v3/tipitaka-discussions/' . fixtures()['discussion_channel'], ['type' => 'issue,bogus']);
  32. assert_status(422, $res, '非法 type');
  33. assert_field_error('type', $res, '非法 type');
  34. });
  35. test('GET /v3/tipitaka-discussions/{非 uuid} → 404', function () {
  36. assert_status(404, raw('GET', '/v3/tipitaka-discussions/not-a-uuid'), '非 uuid 的 channel');
  37. });
  38. test('tipitaka-discussions 一轮:在 fixture 句子上建 issue → 按段落列出、带句子坐标 → 按类型过滤 → 删', function () {
  39. $token = required_token();
  40. [$book, $para] = tipitaka_discussion_coord();
  41. $stamp = 'api-test-tipitaka-discussions-' . date('Ymd-His');
  42. $create = raw('POST', '/v3/discussions', null, [
  43. 'target_type' => 'sentence', 'target_id' => fixtures()['discussion_sentence'],
  44. 'type' => 'issue', 'severity' => 'suggestion', 'title' => $stamp,
  45. 'content' => '由 api-test 建立,测完即删', 'notify' => false,
  46. ], $token);
  47. assert_status(201, $create, '建 issue');
  48. $id = $create['body']['data']['id'] ?? null;
  49. if (! $id) {
  50. fail('建 issue:应返回 id');
  51. }
  52. try {
  53. $client = api(TipitakaDiscussionsApi::class, $token);
  54. $list = request(fn () => $client->getApiV3TipitakaDiscussionsChannelWithHttpInfo(
  55. fixtures()['discussion_channel'], 200, $book, null, $para, null, 'issue'));
  56. assert_status(200, $list, '按段落列 issue');
  57. $hit = null;
  58. foreach ($list['body']['data'] ?? [] as $row) {
  59. if (($row['id'] ?? null) === $id) {
  60. $hit = $row;
  61. }
  62. }
  63. if (! $hit) {
  64. fail('按段落列 issue:应包含刚建的那条');
  65. }
  66. assert_eq($book, $hit['sentence']['book'] ?? null, '句子坐标 book');
  67. assert_eq($para, $hit['sentence']['paragraph'] ?? null, '句子坐标 paragraph');
  68. // 按别的类型过滤就不该出现
  69. $glossary = request(fn () => $client->getApiV3TipitakaDiscussionsChannelWithHttpInfo(
  70. fixtures()['discussion_channel'], 200, $book, null, $para, null, 'glossary'));
  71. assert_status(200, $glossary, '按 glossary 过滤');
  72. assert_eq(false, in_array($id, array_column($glossary['body']['data'] ?? [], 'id'), true), '按 glossary 过滤不含 issue');
  73. } finally {
  74. $del = raw('DELETE', "/v3/discussions/{$id}", null, null, $token);
  75. }
  76. assert_status(200, $del, '删 issue');
  77. });