group-mentions.spec.ts 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import {
  2. MentionMember,
  3. extractMentionNames,
  4. findUnknownMentions,
  5. mentionedIds,
  6. planDispatch,
  7. stripMentions,
  8. } from './group-mentions';
  9. const A: MentionMember = { id: 'a', name: 'app-lj-test-v1' };
  10. const B: MentionMember = { id: 'b', name: 'lj' };
  11. const C: MentionMember = { id: 'c', name: 'lj-test' };
  12. const MEMBERS = [A, B, C];
  13. describe('group-mentions', () => {
  14. describe('extractMentionNames', () => {
  15. it('抽出多个 @ 名字', () => {
  16. expect(extractMentionNames('@lj 你好 @app-lj-test-v1 干活')).toEqual([
  17. 'lj 你好',
  18. 'app-lj-test-v1 干活',
  19. ]);
  20. });
  21. it('邮箱里的 @ 不算(前有字母数字)', () => {
  22. expect(extractMentionNames('发到 a@Nova.com')).toEqual([]);
  23. expect(findUnknownMentions('发到 a@Nova.com', MEMBERS)).toEqual([]);
  24. });
  25. });
  26. describe('findUnknownMentions', () => {
  27. it('全部是成员 → 无无效名字', () => {
  28. expect(findUnknownMentions('@lj @lj-test 干活', MEMBERS)).toEqual([]);
  29. });
  30. it('不存在的名字被识别', () => {
  31. expect(findUnknownMentions('@zhangsan 你好', MEMBERS)).toEqual(['zhangsan']);
  32. });
  33. it('🔴「@lj-test」不会因为存在成员「lj」而被误判为无效', () => {
  34. expect(findUnknownMentions('@lj-test 干活', MEMBERS)).toEqual([]);
  35. });
  36. it('成员名带空格也能识别', () => {
  37. const m = [{ id: 'x', name: 'Zhang San' }];
  38. expect(findUnknownMentions('@Zhang San 干活', m)).toEqual([]);
  39. });
  40. });
  41. describe('mentionedIds', () => {
  42. it('按正文出现顺序返回', () => {
  43. expect(mentionedIds('@lj 和 @app-lj-test-v1 都做', MEMBERS)).toEqual(['b', 'a']);
  44. });
  45. it('前缀陷阱:@lj-test 不命中 lj', () => {
  46. expect(mentionedIds('@lj-test 干活', MEMBERS)).toEqual(['c']);
  47. });
  48. it('重复 @ 同一人只返回一次', () => {
  49. expect(mentionedIds('@lj 做 A,@lj 再做 B', MEMBERS)).toEqual(['b']);
  50. });
  51. });
  52. describe('stripMentions', () => {
  53. it('剥掉 @ 后保留任务正文', () => {
  54. expect(stripMentions('@lj @app-lj-test-v1 各自汇报', MEMBERS)).toBe('各自汇报');
  55. });
  56. it('长名优先,@lj-test 不被剥成「-test」', () => {
  57. expect(stripMentions('@lj-test 干活', MEMBERS)).toBe('干活');
  58. });
  59. it('邮箱不受影响', () => {
  60. expect(stripMentions('发给 a@Nova.com 看下', MEMBERS)).toBe('发给 a@Nova.com 看下');
  61. });
  62. });
  63. describe('planDispatch', () => {
  64. it('✅ 正常:@ 两个成员,返回目标与剥好的正文', () => {
  65. const r = planDispatch('@lj @app-lj-test-v1 各自汇报', MEMBERS);
  66. expect(r.ok).toBe(true);
  67. if (r.ok) {
  68. expect(r.targets).toEqual(['b', 'a']);
  69. expect(r.body).toBe('各自汇报');
  70. }
  71. });
  72. it('🔴 不 @ 任何人 → no-mention(不再兜底群发)', () => {
  73. const r = planDispatch('大家看下这个', MEMBERS);
  74. expect(r.ok).toBe(false);
  75. if (!r.ok) expect(r.reason).toBe('no-mention');
  76. });
  77. it('🔴 @ 不存在的名字 → invalid-mention + invalidNames', () => {
  78. const r = planDispatch('@zhangsan 你好', MEMBERS);
  79. expect(r.ok).toBe(false);
  80. if (!r.ok) {
  81. expect(r.reason).toBe('invalid-mention');
  82. expect(r.invalidNames).toEqual(['zhangsan']);
  83. }
  84. });
  85. it('🔴 有效 @ 与无效 @ 混用 → 整体拦截', () => {
  86. const r = planDispatch('@lj @zhangsan 你俩都做', MEMBERS);
  87. expect(r.ok).toBe(false);
  88. if (!r.ok) {
  89. expect(r.reason).toBe('invalid-mention');
  90. expect(r.invalidNames).toEqual(['zhangsan']);
  91. }
  92. });
  93. it('只剩 @ 没有正文 → empty-body(不给容器发空指令)', () => {
  94. const r = planDispatch('@lj', MEMBERS);
  95. expect(r.ok).toBe(false);
  96. if (!r.ok) expect(r.reason).toBe('empty-body');
  97. });
  98. it('空输入 → empty-body', () => {
  99. const r = planDispatch(' ', MEMBERS);
  100. expect(r.ok).toBe(false);
  101. if (!r.ok) expect(r.reason).toBe('empty-body');
  102. });
  103. it('单个成员 @ 也能分发', () => {
  104. const r = planDispatch('@lj 只回复 333', MEMBERS);
  105. expect(r.ok).toBe(true);
  106. if (r.ok) {
  107. expect(r.targets).toEqual(['b']);
  108. expect(r.body).toBe('只回复 333');
  109. }
  110. });
  111. });
  112. });
  113. describe('stripMentions 标点边界(评审修复)', () => {
  114. it('@ 后面的中文句号一并剥掉', () => {
  115. expect(stripMentions('@lj。然后回答', MEMBERS)).toBe('然后回答');
  116. });
  117. it('@ 后面的逗号一并剥掉', () => {
  118. expect(stripMentions('@lj,再做B', MEMBERS)).toBe('再做B');
  119. });
  120. it('只吃一个标点,保留正文自身的标点', () => {
  121. expect(stripMentions('@lj,做完后提交。', MEMBERS)).toBe('做完后提交。');
  122. });
  123. it('多个 @ 混合标点', () => {
  124. expect(stripMentions('@lj、@lj-test 各自汇报', MEMBERS)).toBe('各自汇报');
  125. });
  126. });