")
->toContain("
");
});
it('wraps a chapter title paragraph in a heading', function () {
$channel = makeParagraphFixture();
(new PaliText)->forceFill([
'book' => 9001,
'paragraph' => 1,
'level' => 2,
'class' => '',
'toc' => '',
'text' => '',
'html' => '',
'pcd_book_id' => 0,
'uid' => (string) Str::uuid(),
])->save();
$display = $this->getJson("/api/v3/tipitaka-read-para/9001-1?channel={$channel}")
->assertOk()
->json('data.display');
expect($display)->toContain('
')->not->toContain('para-block');
});
it('can output only the sentences or both', function () {
$channel = makeParagraphFixture();
$sentences = $this->getJson("/api/v3/tipitaka-read-para/9001-1?channel={$channel}&view=sentences")
->assertOk()
->json('data');
expect($sentences)->not->toHaveKey('display');
expect($sentences['sentences'])->toHaveCount(2);
expect($sentences['sentences'][0])->toHaveKeys(['sid', 'html']);
$all = $this->getJson("/api/v3/tipitaka-read-para/9001-1?channel={$channel}&view=all")
->assertOk()
->json('data');
expect($all)->toHaveKeys(['para', 'display', 'sentences']);
$items = $this->getJson("/api/v3/tipitaka-read-para?book=9001¶=1&channel={$channel}&view=sentences")
->assertOk()
->json('data.items');
expect($items[0])->not->toHaveKey('display');
expect($items[0]['sentences'])->toHaveCount(2);
});
it('lists the paragraphs of a range and skips empty ones', function () {
$channel = makeParagraphFixture();
$items = $this->getJson("/api/v3/tipitaka-read-para?book=9001¶=1&to=3&channel={$channel}")
->assertOk()
->json('data.items');
expect(array_column($items, 'para'))->toBe([1, 2]);
});
it('outputs one line per sentence for non html formats', function () {
$channel = makeParagraphFixture();
$display = $this->getJson("/api/v3/tipitaka-read-para/9001-1?channel={$channel}&format=text")
->assertOk()
->json('data.display');
expect($display)->toBe("first sentence\nsecond sentence");
});
it('rejects an invalid id or channel', function () {
$channel = makeParagraphFixture();
$this->getJson("/api/v3/tipitaka-read-para/bad-id?channel={$channel}")
->assertJsonPath('ok', false);
$this->getJson('/api/v3/tipitaka-read-para/9001-1?channel=not-a-uuid')
->assertJsonPath('ok', false);
$this->getJson("/api/v3/tipitaka-read-para/9001-9?channel={$channel}")
->assertJsonPath('ok', false);
});
it('caches the paragraph and drops the cache when a sentence changes', function () {
$channel = makeParagraphFixture();
$url = "/api/v3/tipitaka-read-para/9001-1?channel={$channel}";
$this->getJson($url)->assertOk();
$tag = PaliContentService::paragraphCacheTag(9001, 1, $channel);
$key = PaliContentService::paragraphCacheKey(9001, 1, $channel, 'html');
expect(Cache::tags([$tag])->has($key))->toBeTrue();
$sentence = Sentence::where('book_id', 9001)->where('paragraph', 1)->orderBy('word_start')->first();
$sentence->content = 'changed sentence';
$sentence->save();
expect(Cache::tags([$tag])->has($key))->toBeFalse();
expect($this->getJson($url)->json('data.display'))->toContain('changed sentence');
});
it('drops the cache when a sentence is added or deleted', function () {
$channel = makeParagraphFixture();
$url = "/api/v3/tipitaka-read-para/9001-1?channel={$channel}";
$this->getJson($url)->assertOk();
makeSentence($channel, 9001, 1, 3, 'third sentence');
expect($this->getJson($url.'&view=sentences')->json('data.sentences'))->toHaveCount(3);
Sentence::where('book_id', 9001)->where('paragraph', 1)->where('word_start', 3)->first()->delete();
expect($this->getJson($url.'&view=sentences')->json('data.sentences'))->toHaveCount(2);
});