Skip to content

Commit 86d71eb

Browse files
committed
pass all JSON Schema Test Suite tests
1 parent fdbd714 commit 86d71eb

File tree

3 files changed

+44
-26
lines changed

3 files changed

+44
-26
lines changed

src/formats.ts

+11-10
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export const fullFormats: DefinedFormats = {
6262
hostname:
6363
/^(?=.{1,253}\.?$)[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?(?:\.[a-z0-9](?:[-0-9a-z]{0,61}[0-9a-z])?)*\.?$/i,
6464
// optimized https://www.safaribooksonline.com/library/view/regular-expressions-cookbook/9780596802837/ch07s16.html
65-
ipv4: /^(?:(?:25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(?:25[0-5]|2[0-4]\d|[01]?\d\d?)$/,
65+
ipv4: /^(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)$/,
6666
ipv6: /^((([0-9a-f]{1,4}:){7}([0-9a-f]{1,4}|:))|(([0-9a-f]{1,4}:){6}(:[0-9a-f]{1,4}|((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){5}(((:[0-9a-f]{1,4}){1,2})|:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3})|:))|(([0-9a-f]{1,4}:){4}(((:[0-9a-f]{1,4}){1,3})|((:[0-9a-f]{1,4})?:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){3}(((:[0-9a-f]{1,4}){1,4})|((:[0-9a-f]{1,4}){0,2}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){2}(((:[0-9a-f]{1,4}){1,5})|((:[0-9a-f]{1,4}){0,3}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(([0-9a-f]{1,4}:){1}(((:[0-9a-f]{1,4}){1,6})|((:[0-9a-f]{1,4}){0,4}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:))|(:(((:[0-9a-f]{1,4}){1,7})|((:[0-9a-f]{1,4}){0,5}:((25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)(\.(25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)){3}))|:)))$/i,
6767
regex,
6868
// uuid: http://tools.ietf.org/html/rfc4122
@@ -149,7 +149,7 @@ function compareDate(d1: string, d2: string): number | undefined {
149149
return 0
150150
}
151151

152-
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-]\d\d)(?::?(\d\d))?)?$/i
152+
const TIME = /^(\d\d):(\d\d):(\d\d(?:\.\d+)?)(z|([+-])(\d\d)(?::?(\d\d))?)?$/i
153153

154154
function time(str: string, withTimeZone?: boolean, strictTime?: boolean): boolean {
155155
const matches: string[] | null = TIME.exec(str)
@@ -158,14 +158,15 @@ function time(str: string, withTimeZone?: boolean, strictTime?: boolean): boolea
158158
const min: number = +matches[2]
159159
const sec: number = +matches[3]
160160
const tz: string | undefined = matches[4]
161-
const tzH: number = +(matches[5] || 0)
162-
const tzM: number = +(matches[6] || 0)
163-
return (
164-
((hr <= 23 && min <= 59 && sec < 60 && tzH <= 24 && tzM < 60) ||
165-
// leap second
166-
(hr - tzH === 23 && min - tzM === 59 && sec < 61 && tzH <= 24 && tzM < 60)) &&
167-
(!withTimeZone || (tz !== "" && (!strictTime || !!tz)))
168-
)
161+
const tzSign: number = matches[5] === "-" ? -1 : 1
162+
const tzH: number = +(matches[6] || 0)
163+
const tzM: number = +(matches[7] || 0)
164+
if (tzH > 23 || tzM > 59 || (withTimeZone && (tz === "" || (strictTime && !tz)))) return false
165+
if (hr <= 23 && min <= 59 && sec < 60) return true
166+
// leap second
167+
const utcMin = min - tzM * tzSign
168+
const utcHr = hr - tzH * tzSign - (utcMin < 0 ? 1 : 0)
169+
return (utcHr === 23 || utcHr === -1) && (utcMin === 59 || utcMin === -1) && sec < 61
169170
}
170171

171172
function strict_time(str: string): boolean {

tests/json-schema.spec.js

+32-15
Original file line numberDiff line numberDiff line change
@@ -2,30 +2,47 @@ const jsonSchemaTest = require("json-schema-test")
22
const Ajv = require("ajv").default
33
const addFormats = require("../dist")
44

5-
const ajv = new Ajv({$data: true, strictTypes: false, formats: {allowedUnknown: true}})
6-
addFormats(ajv)
7-
8-
jsonSchemaTest(ajv, {
9-
description: `JSON-Schema Test Suite draft-07 formats + extras`,
5+
jsonSchemaTest(getAjv(true), {
6+
description: `JSON-Schema Test Suite formats`,
107
suites: {
118
"draft-07 formats": "./JSON-Schema-Test-Suite/tests/draft7/optional/format/*.json",
129
"draft-07 regex": "./JSON-Schema-Test-Suite/tests/draft7/optional/ecmascript-regex.json",
1310
"draft-2019-09 formats": "./JSON-Schema-Test-Suite/tests/draft2019-09/optional/format/*.json",
1411
"draft-2019-09 regex":
1512
"./JSON-Schema-Test-Suite/tests/draft2019-09/optional/ecmascript-regex.json",
16-
extras: "./extras/{**/,}*.json",
13+
"draft-2020-12 formats": "./JSON-Schema-Test-Suite/tests/draft2020-12/optional/format/*.json",
14+
"draft-2020-12 regex":
15+
"./JSON-Schema-Test-Suite/tests/draft2020-12/optional/ecmascript-regex.json",
1716
},
1817
only: [],
1918
skip: ["format/idn-email", "format/idn-hostname", "format/iri", "format/iri-reference"],
20-
afterEach({valid, errors}) {
21-
expect(typeof valid).toBe("boolean")
22-
if (valid === true) {
23-
expect(errors).toBe(null)
24-
} else {
25-
expect(Array.isArray(errors)).toBe(true)
26-
errors.every((err) => expect(typeof err).toBe("object"))
27-
}
28-
},
19+
afterEach,
2920
cwd: __dirname,
3021
hideFolder: "draft7/",
3122
})
23+
24+
jsonSchemaTest(getAjv(), {
25+
description: `Extra tests`,
26+
suites: {
27+
extras: "./extras/{**/,}*.json",
28+
},
29+
only: [],
30+
afterEach,
31+
cwd: __dirname,
32+
})
33+
34+
function getAjv(strictTime) {
35+
const ajv = new Ajv({$data: true, strictTypes: false, formats: {allowedUnknown: true}})
36+
addFormats(ajv, {mode: "full", keywords: true, strictTime})
37+
return ajv
38+
}
39+
40+
function afterEach({valid, errors}) {
41+
expect(typeof valid).toBe("boolean")
42+
if (valid === true) {
43+
expect(errors).toBe(null)
44+
} else {
45+
expect(Array.isArray(errors)).toBe(true)
46+
errors.every((err) => expect(typeof err).toBe("object"))
47+
}
48+
}

0 commit comments

Comments
 (0)