TipitakaContentParaTest.php 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. <?php
  2. use App\Models\PaliText;
  3. use App\Models\Sentence;
  4. use App\Services\PaliContentService;
  5. use Illuminate\Foundation\Testing\RefreshDatabase;
  6. use Illuminate\Support\Facades\Cache;
  7. use Illuminate\Support\Str;
  8. uses(RefreshDatabase::class);
  9. /**
  10. * 建一个句子,返回模型
  11. */
  12. function makeSentence(string $channelUid, int $book, int $para, int $wordStart, string $content): Sentence
  13. {
  14. $sentence = new Sentence;
  15. $sentence->forceFill([
  16. // sentences.id 不是自增列,必须显式给值
  17. 'id' => random_int(1, PHP_INT_MAX),
  18. 'uid' => (string) Str::uuid(),
  19. 'book_id' => $book,
  20. 'paragraph' => $para,
  21. 'word_start' => $wordStart,
  22. 'word_end' => $wordStart,
  23. 'channel_uid' => $channelUid,
  24. 'editor_uid' => (string) Str::uuid(),
  25. 'content' => $content,
  26. 'content_type' => 'markdown',
  27. 'strlen' => mb_strlen($content),
  28. 'status' => 30,
  29. 'create_time' => time() * 1000,
  30. 'modify_time' => time() * 1000,
  31. 'language' => 'zh-Hans',
  32. ])->save();
  33. return $sentence;
  34. }
  35. /**
  36. * 建测试用的 channel 和句子:book 9001 的第 1 段两句,第 2 段一句。返回 channel uid
  37. */
  38. function makeParagraphFixture(): string
  39. {
  40. $channel = makeChannel(makeStudio('para-owner'), 'para channel');
  41. makeSentence($channel, 9001, 1, 1, 'first sentence');
  42. makeSentence($channel, 9001, 1, 2, 'second sentence');
  43. makeSentence($channel, 9001, 2, 1, 'other paragraph');
  44. return $channel;
  45. }
  46. it('renders every sentence of a paragraph wrapped in divs', function () {
  47. $channel = makeParagraphFixture();
  48. $data = $this->getJson("/api/v2/tipitaka-content-para/9001-1?channel={$channel}")
  49. ->assertOk()
  50. ->json('data');
  51. expect($data['para'])->toBe(1);
  52. // 默认只输出 display
  53. expect($data)->not->toHaveKey('sentences');
  54. expect($data['display'])
  55. ->toContain("<div class='translation' data-para='1'>")
  56. ->toContain("<div class='sentence' data-sid='9001-1-1-1'>")
  57. ->toContain("<div class='sentence' data-sid='9001-1-2-2'>")
  58. ->toContain("<div class='para-block'>");
  59. });
  60. it('wraps a chapter title paragraph in a heading', function () {
  61. $channel = makeParagraphFixture();
  62. (new PaliText)->forceFill([
  63. 'book' => 9001,
  64. 'paragraph' => 1,
  65. 'level' => 2,
  66. 'class' => '',
  67. 'toc' => '',
  68. 'text' => '',
  69. 'html' => '',
  70. 'pcd_book_id' => 0,
  71. 'uid' => (string) Str::uuid(),
  72. ])->save();
  73. $display = $this->getJson("/api/v2/tipitaka-content-para/9001-1?channel={$channel}")
  74. ->assertOk()
  75. ->json('data.display');
  76. expect($display)->toContain('<h2>')->not->toContain('para-block');
  77. });
  78. it('can output only the sentences or both', function () {
  79. $channel = makeParagraphFixture();
  80. $sentences = $this->getJson("/api/v2/tipitaka-content-para/9001-1?channel={$channel}&view=sentences")
  81. ->assertOk()
  82. ->json('data');
  83. expect($sentences)->not->toHaveKey('display');
  84. expect($sentences['sentences'])->toHaveCount(2);
  85. expect($sentences['sentences'][0])->toHaveKeys(['sid', 'html']);
  86. $all = $this->getJson("/api/v2/tipitaka-content-para/9001-1?channel={$channel}&view=all")
  87. ->assertOk()
  88. ->json('data');
  89. expect($all)->toHaveKeys(['para', 'display', 'sentences']);
  90. $items = $this->getJson("/api/v2/tipitaka-content-para?book=9001&para=1&channel={$channel}&view=sentences")
  91. ->assertOk()
  92. ->json('data.items');
  93. expect($items[0])->not->toHaveKey('display');
  94. expect($items[0]['sentences'])->toHaveCount(2);
  95. });
  96. it('lists the paragraphs of a range and skips empty ones', function () {
  97. $channel = makeParagraphFixture();
  98. $items = $this->getJson("/api/v2/tipitaka-content-para?book=9001&para=1&to=3&channel={$channel}")
  99. ->assertOk()
  100. ->json('data.items');
  101. expect(array_column($items, 'para'))->toBe([1, 2]);
  102. });
  103. it('outputs one line per sentence for non html formats', function () {
  104. $channel = makeParagraphFixture();
  105. $display = $this->getJson("/api/v2/tipitaka-content-para/9001-1?channel={$channel}&format=text")
  106. ->assertOk()
  107. ->json('data.display');
  108. expect($display)->toBe("first sentence\nsecond sentence");
  109. });
  110. it('rejects an invalid id or channel', function () {
  111. $channel = makeParagraphFixture();
  112. $this->getJson("/api/v2/tipitaka-content-para/bad-id?channel={$channel}")
  113. ->assertJsonPath('ok', false);
  114. $this->getJson('/api/v2/tipitaka-content-para/9001-1?channel=not-a-uuid')
  115. ->assertJsonPath('ok', false);
  116. $this->getJson("/api/v2/tipitaka-content-para/9001-9?channel={$channel}")
  117. ->assertJsonPath('ok', false);
  118. });
  119. it('caches the paragraph and drops the cache when a sentence changes', function () {
  120. $channel = makeParagraphFixture();
  121. $url = "/api/v2/tipitaka-content-para/9001-1?channel={$channel}";
  122. $this->getJson($url)->assertOk();
  123. $tag = PaliContentService::paragraphCacheTag(9001, 1, $channel);
  124. $key = PaliContentService::paragraphCacheKey(9001, 1, $channel, 'html');
  125. expect(Cache::tags([$tag])->has($key))->toBeTrue();
  126. $sentence = Sentence::where('book_id', 9001)->where('paragraph', 1)->orderBy('word_start')->first();
  127. $sentence->content = 'changed sentence';
  128. $sentence->save();
  129. expect(Cache::tags([$tag])->has($key))->toBeFalse();
  130. expect($this->getJson($url)->json('data.display'))->toContain('changed sentence');
  131. });
  132. it('drops the cache when a sentence is added or deleted', function () {
  133. $channel = makeParagraphFixture();
  134. $url = "/api/v2/tipitaka-content-para/9001-1?channel={$channel}";
  135. $this->getJson($url)->assertOk();
  136. makeSentence($channel, 9001, 1, 3, 'third sentence');
  137. expect($this->getJson($url.'&view=sentences')->json('data.sentences'))->toHaveCount(3);
  138. Sentence::where('book_id', 9001)->where('paragraph', 1)->where('word_start', 3)->first()->delete();
  139. expect($this->getJson($url.'&view=sentences')->json('data.sentences'))->toHaveCount(2);
  140. });