translation-pairing.spec.ts 3.6 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. /** Regression tests for the bilingual cutoff and structural signature. */
  2. import { describe, expect, it } from 'vitest'
  3. import {
  4. datedDocumentDate,
  5. isIsoDate,
  6. parseTranslationMarkdown,
  7. parseTranslationPairingManifest,
  8. requiresPairByDate,
  9. translationStructureDiff,
  10. translationStructureSignature,
  11. } from './translation-pairing.ts'
  12. function signature(markdown: string) {
  13. return translationStructureSignature(parseTranslationMarkdown(markdown), 'counterpart.zh.md')
  14. }
  15. describe('translation pairing manifest', () => {
  16. it('accepts a real ISO cutoff and string-array fields', () => {
  17. expect(parseTranslationPairingManifest(JSON.stringify({
  18. requiredSince: '2026-07-14',
  19. required: ['README.md'],
  20. excluded: ['docs/generated/'],
  21. }))).toEqual({
  22. requiredSince: '2026-07-14',
  23. required: ['README.md'],
  24. excluded: ['docs/generated/'],
  25. })
  26. })
  27. it.each(['2026-7-14', '2026-02-29', '2026-13-01', 'not-a-date'])('rejects invalid cutoff %s', (cutoff) => {
  28. expect(isIsoDate(cutoff)).toBe(false)
  29. expect(() => parseTranslationPairingManifest(JSON.stringify({
  30. requiredSince: cutoff,
  31. required: [],
  32. excluded: [],
  33. }))).toThrow('requiredSince must be a valid YYYY-MM-DD date')
  34. })
  35. it('rejects non-string manifest arrays', () => {
  36. expect(() => parseTranslationPairingManifest(JSON.stringify({
  37. requiredSince: '2026-07-14',
  38. required: [42],
  39. excluded: [],
  40. }))).toThrow('required must be an array of strings')
  41. })
  42. })
  43. describe('date-based pairing frontier', () => {
  44. const cutoff = '2026-07-14'
  45. it('enforces the cutoff day and every later day, but not the preceding day', () => {
  46. expect(requiresPairByDate('.agents/notes/2026-07-13-before.md', cutoff)).toBe(false)
  47. expect(requiresPairByDate('.agents/notes/2026-07-14-at-cutoff.md', cutoff)).toBe(true)
  48. expect(requiresPairByDate('.agents/notes/2026-07-15-after.md', cutoff)).toBe(true)
  49. })
  50. it('matches only a date at the start of the basename', () => {
  51. expect(datedDocumentDate('.agents/notes/2026-07-14-proposal.md')).toBe('2026-07-14')
  52. expect(datedDocumentDate('docs/release-notes-2026-07-14-alpha.md')).toBeUndefined()
  53. expect(requiresPairByDate('docs/release-notes-2026-07-14-alpha.md', cutoff)).toBe(false)
  54. })
  55. })
  56. describe('translation structural signature', () => {
  57. it('accepts matching list kinds, starts, and item counts', () => {
  58. const source = signature('3. One\n4. Two\n\n- A\n- B\n')
  59. const counterpart = signature('3. 一\n4. 二\n\n- 甲\n- 乙\n')
  60. expect(translationStructureDiff(source, counterpart)).toEqual([])
  61. })
  62. it('rejects an altered ordered-list start', () => {
  63. const source = signature('3. One\n4. Two\n\n- A\n- B\n')
  64. const counterpart = signature('1. 一\n2. 二\n\n- 甲\n- 乙\n')
  65. expect(translationStructureDiff(source, counterpart)).toEqual([
  66. 'list (kind, start, item count) #1 diverges between the pair: "ordered:start=3:items=2" vs "ordered:start=1:items=2"',
  67. ])
  68. })
  69. it('rejects a missing list item', () => {
  70. const source = signature('- A\n- B\n')
  71. const counterpart = signature('- 甲\n')
  72. expect(translationStructureDiff(source, counterpart)).toEqual([
  73. 'list (kind, start, item count) #1 diverges between the pair: "bullet:items=2" vs "bullet:items=1"',
  74. ])
  75. })
  76. it('rejects altered table row or column counts', () => {
  77. const source = signature('| A | B |\n|---|---|\n| 1 | 2 |\n| 3 | 4 |\n')
  78. const counterpart = signature('| 甲 | 乙 |\n|---|---|\n| 一 | 二 |\n')
  79. expect(translationStructureDiff(source, counterpart)).toEqual([
  80. 'table (row x column count) #1 diverges between the pair: "3x2" vs "2x2"',
  81. ])
  82. })
  83. })