diff --git a/src/lib/markdown/excerpt.test.ts b/src/lib/markdown/excerpt.test.ts
new file mode 100644
index 0000000..3edfedf
--- /dev/null
+++ b/src/lib/markdown/excerpt.test.ts
@@ -0,0 +1,63 @@
+import { describe, expect, it } from 'vitest';
+import { createExcerptHtml } from './excerpt';
+
+describe('createExcerptHtml', () => {
+ it('renders inline styles instead of stripping them', () => {
+ expect(createExcerptHtml('这是**粗体**与*斜体*,还有~~删除线~~。')).toBe(
+ '这是粗体与斜体,还有删除线。...'
+ );
+ });
+
+ it('keeps inline code and drops the backticks', () => {
+ expect(createExcerptHtml('先执行 `pnpm run build` 再发布')).toBe(
+ '先执行 pnpm run build 再发布...'
+ );
+ });
+
+ it('reduces links and images to readable text', () => {
+ expect(createExcerptHtml('参考[官方文档](https://example.com)与说明')).toBe(
+ '参考官方文档与配图说明...'
+ );
+ });
+
+ it('removes block level markers so the excerpt stays a single line', () => {
+ expect(createExcerptHtml('# 标题\n\n- 第一项\n- 第二项\n\n> 引用内容')).toBe(
+ '标题 第一项 第二项 引用内容...'
+ );
+ });
+
+ it('drops Hugo shortcodes and raw HTML', () => {
+ expect(
+ createExcerptHtml('{{< admonition note "提示" >}}正文{{< /admonition >}}片段')
+ ).toBe('正文片段...');
+ });
+
+ it('closes inline tags that are cut off by the length limit', () => {
+ const html = createExcerptHtml(`**${'a'.repeat(200)}**`, 10);
+
+ expect(html).toBe(`${'a'.repeat(10)}...`);
+ });
+
+ it('counts HTML entities as a single visible character', () => {
+ expect(createExcerptHtml('&'.repeat(30), 5)).toBe(`${'&'.repeat(5)}...`);
+ });
+
+ it('never keeps a dangling inline tag when the cut lands on it', () => {
+ const html = createExcerptHtml(`${'a'.repeat(9)}**bc**`, 10);
+
+ expect(html).toBe(`aaaaaaaaab...`);
+ });
+
+ it('escapes characters that would otherwise break the markup', () => {
+ const html = createExcerptHtml('对比 1 < 2 的结果 ');
+
+ expect(html).toContain('1 < 2');
+ expect(html).not.toContain('