zodHelpers.test.ts 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337
  1. import { zz } from '../../../zodHelpers'
  2. import { describe, expect, it } from 'vitest'
  3. import mongodb from 'mongodb'
  4. const { ObjectId } = mongodb
  5. describe('zodHelpers', () => {
  6. describe('objectId', () => {
  7. it('fails to parse when provided with an invalid ObjectId', () => {
  8. const parsed = zz.objectId().safeParse('aa')
  9. expect(parsed.success).toBe(false)
  10. expect(parsed.error?.issues).toHaveLength(1)
  11. expect(parsed.error?.issues).toMatchObject([
  12. expect.objectContaining({
  13. message: 'invalid Mongo ObjectId',
  14. }),
  15. ])
  16. })
  17. it('parses successfully when provided with a valid ObjectId', () => {
  18. const parsed = zz.objectId().safeParse('507f1f77bcf86cd799439011')
  19. expect(parsed.success).toBe(true)
  20. expect(parsed.data).toBe('507f1f77bcf86cd799439011')
  21. })
  22. })
  23. describe('coercedObjectId', () => {
  24. it('fails to parse when provided with an invalid ObjectId', () => {
  25. const parsed = zz.coercedObjectId().safeParse('aa')
  26. expect(parsed.success).toBe(false)
  27. expect(parsed.error?.issues).toHaveLength(1)
  28. expect(parsed.error?.issues).toMatchObject([
  29. expect.objectContaining({
  30. message: 'invalid Mongo ObjectId',
  31. }),
  32. ])
  33. })
  34. it('parses to an ObjectId when provided with a valid ObjectId string', () => {
  35. const parsed = zz.coercedObjectId().safeParse('507f1f77bcf86cd799439011')
  36. expect(parsed.success).toBe(true)
  37. expect(parsed.data).toBeInstanceOf(ObjectId)
  38. expect(parsed.data?.toString()).toBe('507f1f77bcf86cd799439011')
  39. })
  40. })
  41. describe('datetime', () => {
  42. it('parses valid ISO 8601 datetime strings', () => {
  43. const parsed = zz.datetime().safeParse('2024-01-01T12:00:00Z')
  44. expect(parsed.success).toBe(true)
  45. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00Z'))
  46. })
  47. it('parses a valid ISO 8601 datetime with offset', () => {
  48. const parsed = zz
  49. .datetime({ offset: true })
  50. .safeParse('2024-01-01T12:00:00+00:00')
  51. expect(parsed.success).toBe(true)
  52. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00+00:00'))
  53. })
  54. it('parses a valid Date object', () => {
  55. const date = new Date('2024-01-01T12:00:00Z')
  56. const parsed = zz.datetime().safeParse(date)
  57. expect(parsed.success).toBe(true)
  58. expect(parsed.data).toEqual(date)
  59. })
  60. it('fails to parse datetime with offset when offset option is false', () => {
  61. const parsed = zz
  62. .datetime({ offset: false })
  63. .safeParse('2024-01-01T12:00:00+00:00')
  64. expect(parsed.success).toBe(false)
  65. expect(parsed.error?.issues).toHaveLength(1)
  66. expect(parsed.error?.issues).toMatchObject([
  67. expect.objectContaining({
  68. code: 'invalid_format',
  69. format: 'datetime',
  70. message: 'Invalid ISO datetime',
  71. }),
  72. ])
  73. })
  74. it('fails to parse null when schema is not nullable', () => {
  75. const parsed = zz.datetime().safeParse(null)
  76. expect(parsed.success).toBe(false)
  77. expect(parsed.error?.message).toContain(
  78. 'Invalid input: expected date, received null'
  79. )
  80. })
  81. it('fails to parse invalid datetime strings', () => {
  82. const parsed = zz.datetime().safeParse('invalid-datetime')
  83. expect(parsed.success).toBe(false)
  84. expect(parsed.error?.issues).toHaveLength(1)
  85. expect(parsed.error?.issues).toMatchObject([
  86. expect.objectContaining({
  87. code: 'invalid_format',
  88. format: 'datetime',
  89. message: 'Invalid ISO datetime',
  90. }),
  91. ])
  92. })
  93. })
  94. describe('datetimeNullable', () => {
  95. it('parses valid ISO 8601 datetime strings', () => {
  96. const parsed = zz.datetimeNullable().safeParse('2024-01-01T12:00:00Z')
  97. expect(parsed.success).toBe(true)
  98. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00Z'))
  99. })
  100. it('parses a valid ISO 8601 datetime with offset', () => {
  101. const parsed = zz
  102. .datetimeNullable({ offset: true })
  103. .safeParse('2024-01-01T12:00:00+00:00')
  104. expect(parsed.success).toBe(true)
  105. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00+00:00'))
  106. })
  107. it('parses a valid Date object', () => {
  108. const date = new Date('2024-01-01T12:00:00Z')
  109. const parsed = zz.datetimeNullable().safeParse(date)
  110. expect(parsed.success).toBe(true)
  111. expect(parsed.data).toEqual(date)
  112. })
  113. it('fails to parse datetime with offset when offset option is false', () => {
  114. const parsed = zz
  115. .datetimeNullable({ offset: false })
  116. .safeParse('2024-01-01T12:00:00+00:00')
  117. expect(parsed.success).toBe(false)
  118. expect(parsed.error?.issues).toHaveLength(1)
  119. expect(parsed.error?.issues).toMatchObject([
  120. expect.objectContaining({
  121. code: 'invalid_format',
  122. format: 'datetime',
  123. message: 'Invalid ISO datetime',
  124. }),
  125. ])
  126. })
  127. it('parses null when schema is nullable and input is null', () => {
  128. const parsed = zz.datetimeNullable().safeParse(null)
  129. expect(parsed.success).toBe(true)
  130. expect(parsed.data).toBeNull()
  131. })
  132. it('fails to parse invalid datetime strings', () => {
  133. const parsed = zz.datetimeNullable().safeParse('invalid-datetime')
  134. expect(parsed.success).toBe(false)
  135. expect(parsed.error?.issues).toHaveLength(1)
  136. expect(parsed.error?.issues).toMatchObject([
  137. expect.objectContaining({
  138. code: 'invalid_format',
  139. format: 'datetime',
  140. message: 'Invalid ISO datetime',
  141. }),
  142. ])
  143. })
  144. })
  145. describe('datetimeNullish', () => {
  146. it('parses valid ISO 8601 datetime strings', () => {
  147. const parsed = zz.datetimeNullish().safeParse('2024-01-01T12:00:00Z')
  148. expect(parsed.success).toBe(true)
  149. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00Z'))
  150. })
  151. it('parses a valid ISO 8601 datetime with offset', () => {
  152. const parsed = zz
  153. .datetimeNullish({ offset: true })
  154. .safeParse('2024-01-01T12:00:00+00:00')
  155. expect(parsed.success).toBe(true)
  156. expect(parsed.data).toEqual(new Date('2024-01-01T12:00:00+00:00'))
  157. })
  158. it('parses a valid Date object', () => {
  159. const date = new Date('2024-01-01T12:00:00Z')
  160. const parsed = zz.datetimeNullish().safeParse(date)
  161. expect(parsed.success).toBe(true)
  162. expect(parsed.data).toEqual(date)
  163. })
  164. it('parses null when schema is nullable and input is null', () => {
  165. const parsed = zz.datetimeNullish().safeParse(null)
  166. expect(parsed.success).toBe(true)
  167. expect(parsed.data).toBeNull()
  168. })
  169. it('parses undefined when schema is nullish and input is undefined', () => {
  170. const parsed = zz.datetimeNullish().safeParse(undefined)
  171. expect(parsed.success).toBe(true)
  172. expect(parsed.data).toBeUndefined()
  173. })
  174. it('fails to parse datetime with offset when offset option is false', () => {
  175. const parsed = zz
  176. .datetimeNullish({ offset: false })
  177. .safeParse('2024-01-01T12:00:00+00:00')
  178. expect(parsed.success).toBe(false)
  179. expect(parsed.error?.issues).toHaveLength(1)
  180. expect(parsed.error?.issues).toMatchObject([
  181. expect.objectContaining({
  182. code: 'invalid_format',
  183. format: 'datetime',
  184. message: 'Invalid ISO datetime',
  185. }),
  186. ])
  187. })
  188. it('fails to parse invalid datetime strings', () => {
  189. const parsed = zz.datetimeNullish().safeParse('invalid-datetime')
  190. expect(parsed.success).toBe(false)
  191. expect(parsed.error?.issues).toHaveLength(1)
  192. expect(parsed.error?.issues).toMatchObject([
  193. expect.objectContaining({
  194. code: 'invalid_format',
  195. format: 'datetime',
  196. message: 'Invalid ISO datetime',
  197. }),
  198. ])
  199. })
  200. })
  201. describe('buildId', () => {
  202. it('fails to parse when provided with an invalid buildId', () => {
  203. const parsed = zz.buildId().safeParse('aa')
  204. expect(parsed.success).toBe(false)
  205. expect(parsed.error?.issues).toHaveLength(1)
  206. expect(parsed.error?.issues).toMatchObject([
  207. expect.objectContaining({
  208. message: 'invalid buildId',
  209. }),
  210. ])
  211. })
  212. it('parses successfully when provided with a valid buildId', () => {
  213. const parsed = zz.buildId().safeParse('19d6c341530-878fff6cdab7fb0c')
  214. expect(parsed.success).toBe(true)
  215. expect(parsed.data).toBe('19d6c341530-878fff6cdab7fb0c')
  216. })
  217. it('fails to parse when provided with an editorBuildId', () => {
  218. const parsed = zz
  219. .buildId()
  220. .safeParse(
  221. '03b1d773-6203-4669-b365-6a0aa5625878-19d6c341530-878fff6cdab7fb0c'
  222. )
  223. expect(parsed.success).toBe(false)
  224. expect(parsed.error?.issues).toHaveLength(1)
  225. expect(parsed.error?.issues).toMatchObject([
  226. expect.objectContaining({
  227. message: 'invalid buildId',
  228. }),
  229. ])
  230. })
  231. })
  232. describe('editorBuildId', () => {
  233. it('fails to parse when provided with an invalid buildId', () => {
  234. const parsed = zz.editorBuildId().safeParse('aa')
  235. expect(parsed.success).toBe(false)
  236. expect(parsed.error?.issues).toHaveLength(1)
  237. expect(parsed.error?.issues).toMatchObject([
  238. expect.objectContaining({
  239. message: 'invalid editorId-buildId',
  240. }),
  241. ])
  242. })
  243. it('fails to parse when provided with a buildId', () => {
  244. const parsed = zz
  245. .editorBuildId()
  246. .safeParse('19d6c341530-878fff6cdab7fb0c')
  247. expect(parsed.success).toBe(false)
  248. expect(parsed.error?.issues).toHaveLength(1)
  249. expect(parsed.error?.issues).toMatchObject([
  250. expect.objectContaining({
  251. message: 'invalid editorId-buildId',
  252. }),
  253. ])
  254. })
  255. it('parses successfully when provided with a valid editorId-buildId', () => {
  256. const parsed = zz
  257. .editorBuildId()
  258. .safeParse(
  259. '03b1d773-6203-4669-b365-6a0aa5625878-19d6c341530-878fff6cdab7fb0c'
  260. )
  261. expect(parsed.success).toBe(true)
  262. expect(parsed.data).toBe(
  263. '03b1d773-6203-4669-b365-6a0aa5625878-19d6c341530-878fff6cdab7fb0c'
  264. )
  265. })
  266. })
  267. describe('filepath', () => {
  268. it('fails to parse with empty input', () => {
  269. const parsed = zz.filepath().safeParse('')
  270. expect(parsed.success).toBe(false)
  271. expect(parsed.error?.issues).toHaveLength(1)
  272. expect(parsed.error?.issues).toMatchObject([
  273. expect.objectContaining({
  274. message: 'path is empty',
  275. }),
  276. ])
  277. })
  278. it('fails to parse with absolute path', () => {
  279. const parsed = zz.filepath().safeParse('/output.pdf')
  280. expect(parsed.success).toBe(false)
  281. expect(parsed.error?.issues).toHaveLength(1)
  282. expect(parsed.error?.issues).toMatchObject([
  283. expect.objectContaining({
  284. message: 'path is absolute',
  285. }),
  286. ])
  287. })
  288. it('fails to parse when provided with path traversal', () => {
  289. const parsed = zz.filepath().safeParse('../output.pdf')
  290. expect(parsed.success).toBe(false)
  291. expect(parsed.error?.issues).toHaveLength(1)
  292. expect(parsed.error?.issues).toMatchObject([
  293. expect.objectContaining({
  294. message: 'path traversal detected',
  295. }),
  296. ])
  297. })
  298. it('parses successfully when provided a valid path', () => {
  299. const parsed = zz.filepath().safeParse('output.pdf')
  300. expect(parsed.success).toBe(true)
  301. expect(parsed.data).toBe('output.pdf')
  302. })
  303. it('parses successfully when provided a valid nested path', () => {
  304. const parsed = zz.filepath().safeParse('foo/output.pdf')
  305. expect(parsed.success).toBe(true)
  306. expect(parsed.data).toBe('foo/output.pdf')
  307. })
  308. })
  309. })