@atproto/syntax 0.4.3 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  import * as fs from 'node:fs'
2
- import * as readline from 'node:readline'
3
- import { describe, expect, it } from 'vitest'
2
+ import { describe, expect, it, test } from 'vitest'
4
3
  import {
5
4
  InvalidDatetimeError,
6
5
  ensureValidDatetime,
@@ -9,67 +8,111 @@ import {
9
8
  normalizeDatetimeAlways,
10
9
  } from '../src'
11
10
 
12
- describe('datetime validation', () => {
13
- const expectValid = (h: string) => {
14
- ensureValidDatetime(h)
15
- normalizeDatetime(h)
16
- normalizeDatetimeAlways(h)
17
- }
18
- const expectInvalid = (h: string) => {
19
- expect(() => ensureValidDatetime(h)).toThrow(InvalidDatetimeError)
20
- }
21
-
22
- it('conforms to interop valid datetimes', () => {
23
- const lineReader = readline.createInterface({
24
- input: fs.createReadStream(
25
- `${__dirname}/interop-files/datetime_syntax_valid.txt`,
26
- ),
27
- terminal: false,
28
- })
29
- lineReader.on('line', (line) => {
30
- if (line.startsWith('#') || line.length === 0) {
31
- return
32
- }
33
- if (!isValidDatetime(line)) {
34
- console.log(line)
35
- }
36
- expectValid(line)
37
- })
38
- })
39
-
40
- it('conforms to interop invalid datetimes', () => {
41
- const lineReader = readline.createInterface({
42
- input: fs.createReadStream(
43
- `${__dirname}/interop-files/datetime_syntax_invalid.txt`,
44
- ),
45
- terminal: false,
46
- })
47
- lineReader.on('line', (line) => {
48
- if (line.startsWith('#') || line.length === 0) {
49
- return
50
- }
51
- expectInvalid(line)
52
- })
53
- })
54
-
55
- it('conforms to interop invalid parse (semantics) datetimes', () => {
56
- const lineReader = readline.createInterface({
57
- input: fs.createReadStream(
58
- `${__dirname}/interop-files/datetime_parse_invalid.txt`,
59
- ),
60
- terminal: false,
61
- })
62
- lineReader.on('line', (line) => {
63
- if (line.startsWith('#') || line.length === 0) {
64
- return
65
- }
66
- expectInvalid(line)
67
- })
11
+ const interopValid = readLines(
12
+ `${__dirname}/interop-files/datetime_syntax_valid.txt`,
13
+ )
14
+ const interopInvalidSyntax = readLines(
15
+ `${__dirname}/interop-files/datetime_syntax_invalid.txt`,
16
+ )
17
+ const interopInvalidParse = readLines(
18
+ `${__dirname}/interop-files/datetime_parse_invalid.txt`,
19
+ )
20
+
21
+ describe(ensureValidDatetime, () => {
22
+ describe('valid interop', () => {
23
+ for (const dt of interopValid) {
24
+ test(dt, () => {
25
+ expect(() => ensureValidDatetime(dt)).not.toThrow()
26
+ })
27
+ }
28
+ })
29
+
30
+ describe('fails on interop (invalid syntax)', () => {
31
+ for (const dt of interopInvalidSyntax) {
32
+ test(dt, () => {
33
+ expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
34
+ })
35
+ }
36
+ })
37
+
38
+ describe('fails on interop (invalid parse)', () => {
39
+ for (const dt of interopInvalidParse) {
40
+ test(dt, () => {
41
+ expect(() => ensureValidDatetime(dt)).toThrow(InvalidDatetimeError)
42
+ })
43
+ }
44
+ })
45
+
46
+ it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
47
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
48
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
49
+ expect(() => ensureValidDatetime('9999-12-31T23:59:00-00:01')).toThrow(
50
+ InvalidDatetimeError,
51
+ )
52
+ })
53
+ })
54
+
55
+ describe(isValidDatetime, () => {
56
+ describe('valid interop', () => {
57
+ for (const dt of interopValid) {
58
+ test(dt, () => {
59
+ expect(isValidDatetime(dt)).toBe(true)
60
+ })
61
+ }
62
+ })
63
+
64
+ describe('fails on interop (invalid syntax)', () => {
65
+ for (const dt of interopInvalidSyntax) {
66
+ test(dt, () => {
67
+ expect(isValidDatetime(dt)).toBe(false)
68
+ })
69
+ }
70
+ })
71
+
72
+ describe('fails on interop (invalid parse)', () => {
73
+ for (const dt of interopInvalidParse) {
74
+ test(dt, () => {
75
+ expect(isValidDatetime(dt)).toBe(false)
76
+ })
77
+ }
78
+ })
79
+
80
+ it('rejects datetime that normalizes past year 9999 due to negative offset', () => {
81
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
82
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
83
+ expect(isValidDatetime('9999-12-31T23:59:00-00:01')).toBe(false)
68
84
  })
69
85
  })
70
86
 
71
- describe('normalization', () => {
72
- it('normalizes datetimes', () => {
87
+ describe(normalizeDatetime, () => {
88
+ describe('valid interop', () => {
89
+ for (const dt of interopValid) {
90
+ test(dt, () => {
91
+ expect(() => normalizeDatetime(dt)).not.toThrow()
92
+ })
93
+ }
94
+ })
95
+
96
+ // @NOTE Normalize will actually succeed on some of the invalid syntax cases,
97
+ // because it is more lenient than the regex validation.
98
+
99
+ // describe('fails on interop (invalid syntax)', () => {
100
+ // for (const dt of interopInvalidSyntax) {
101
+ // test(dt, () => {
102
+ // expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
103
+ // })
104
+ // }
105
+ // })
106
+
107
+ describe('fails on interop (invalid parse)', () => {
108
+ for (const dt of interopInvalidParse) {
109
+ test(dt, () => {
110
+ expect(() => normalizeDatetime(dt)).toThrow(InvalidDatetimeError)
111
+ })
112
+ }
113
+ })
114
+
115
+ it('normalizes valid input', () => {
73
116
  expect(normalizeDatetime('1234-04-12T23:20:50Z')).toEqual(
74
117
  '1234-04-12T23:20:50.000Z',
75
118
  )
@@ -90,7 +133,7 @@ describe('normalization', () => {
90
133
  )
91
134
  })
92
135
 
93
- it('throws on invalid normalized datetimes', () => {
136
+ it('throws on invalid input', () => {
94
137
  expect(() => normalizeDatetime('')).toThrow(InvalidDatetimeError)
95
138
  expect(() => normalizeDatetime('blah')).toThrow(InvalidDatetimeError)
96
139
  expect(() => normalizeDatetime('1999-19-39T23:20:50.123Z')).toThrow(
@@ -105,15 +148,58 @@ describe('normalization', () => {
105
148
  expect(() => normalizeDatetime('0001-01-01T00:00:00+01:00')).toThrow(
106
149
  InvalidDatetimeError,
107
150
  )
151
+ // 9999-12-31T23:59:00-00:01 is syntactically valid, but normalizing to
152
+ // UTC advances it to 10000-01-01T00:00:00Z, which is out of range
153
+ expect(() => normalizeDatetime('9999-12-31T23:59:00-00:01')).toThrow(
154
+ InvalidDatetimeError,
155
+ )
108
156
  })
157
+ })
109
158
 
110
- it('normalizes datetimes always', () => {
159
+ describe(normalizeDatetimeAlways, () => {
160
+ it('normalizes valid input', () => {
111
161
  expect(normalizeDatetimeAlways('1985-04-12T23:20:50Z')).toEqual(
112
162
  '1985-04-12T23:20:50.000Z',
113
163
  )
164
+ })
165
+
166
+ it('normalizes invalid input', () => {
114
167
  expect(normalizeDatetimeAlways('blah')).toEqual('1970-01-01T00:00:00.000Z')
115
168
  expect(normalizeDatetimeAlways('0000-01-01T00:00:00+01:00')).toEqual(
116
169
  '1970-01-01T00:00:00.000Z',
117
170
  )
118
171
  })
172
+
173
+ describe('valid interop', () => {
174
+ for (const dt of interopValid) {
175
+ test(dt, () => {
176
+ // @NOTE we can't test the returned value as some will normalize while others won't.
177
+ expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
178
+ })
179
+ }
180
+ })
181
+
182
+ describe('succeeds on interop (invalid syntax)', () => {
183
+ for (const dt of interopInvalidSyntax) {
184
+ test(dt, () => {
185
+ // @NOTE we can't test the returned value as some will normalize while others won't.
186
+ expect(() => normalizeDatetimeAlways(dt)).not.toThrow()
187
+ })
188
+ }
189
+ })
190
+
191
+ describe('succeeds on interop invalid parse', () => {
192
+ for (const dt of interopInvalidParse) {
193
+ test(dt, () => {
194
+ expect(normalizeDatetimeAlways(dt)).toEqual('1970-01-01T00:00:00.000Z')
195
+ })
196
+ }
197
+ })
119
198
  })
199
+
200
+ function readLines(filePath: string): string[] {
201
+ return fs
202
+ .readFileSync(filePath, 'utf-8')
203
+ .split(/\r?\n/)
204
+ .filter((line) => !line.startsWith('#') && line.length > 0)
205
+ }