/
Nspection
/
rules-manager-mcp
Обзор
Документация
Войти
/
Nspection
/
rules-manager-mcp
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
dev
node_modules/zod/src/v3/tests/string.test.ts
916 строк
64 KB
Buglov Evgeny
Первый коммит
27 фев 2026, 21:12
27 фев 2026, 21:12
e51dee3
Код
Авторство
О чём код?
import { Buffer } from "node:buffer"; // @ts-ignore TS6133 import { expect, test } from "vitest"; import * as z from "zod/v3"; const minFive = z.string().min(5, "min5"); const maxFive = z.string().max(5, "max5"); const justFive = z.string().length(5); const nonempty = z.string().nonempty("nonempty"); const includes = z.string().includes("includes"); const includesFromIndex2 = z.string().includes("includes", { position: 2 }); const startsWith = z.string().startsWith("startsWith"); const endsWith = z.string().endsWith("endsWith"); test("passing validations", () => { minFive.parse("12345"); minFive.parse("123456"); maxFive.parse("12345"); maxFive.parse("1234"); nonempty.parse("1"); justFive.parse("12345"); includes.parse("XincludesXX"); includesFromIndex2.parse("XXXincludesXX"); startsWith.parse("startsWithX"); endsWith.parse("XendsWith"); }); test("failing validations", () => { expect(() => minFive.parse("1234")).toThrow(); expect(() => maxFive.parse("123456")).toThrow(); expect(() => nonempty.parse("")).toThrow(); expect(() => justFive.parse("1234")).toThrow(); expect(() => justFive.parse("123456")).toThrow(); expect(() => includes.parse("XincludeXX")).toThrow(); expect(() => includesFromIndex2.parse("XincludesXX")).toThrow(); expect(() => startsWith.parse("x")).toThrow(); expect(() => endsWith.parse("x")).toThrow(); }); test("email validations", () => { const validEmails = [ `email@domain.com`, `firstname.lastname@domain.com`, `email@subdomain.domain.com`, `firstname+lastname@domain.com`, `1234567890@domain.com`, `email@domain-one.com`, `_______@domain.com`, `email@domain.name`, `email@domain.co.jp`, `firstname-lastname@domain.com`, `very.common@example.com`, `disposable.style.email.with+symbol@example.com`, `other.email-with-hyphen@example.com`, `fully-qualified-domain@example.com`, `user.name+tag+sorting@example.com`, `x@example.com`, `mojojojo@asdf.example.com`, `example-indeed@strange-example.com`, `example@s.example`, `user-@example.org`, `user@my-example.com`, `a@b.cd`, `work+user@mail.com`, `tom@test.te-st.com`, `something@subdomain.domain-with-hyphens.tld`, `common'name@domain.com`, `francois@etu.inp-n7.fr`, ]; const invalidEmails = [ // no "printable characters" // `user%example.com@example.org`, // `mailhost!username@example.org`, // `test/test@test.com`, // double @ `francois@@etu.inp-n7.fr`, // do not support quotes `"email"@domain.com`, `"e asdf sadf ?<>ail"@domain.com`, `" "@example.org`, `"john..doe"@example.org`, `"very.(),:;<>[]\".VERY.\"very@\\ \"very\".unusual"@strange.example.com`, // do not support comma `a,b@domain.com`, // do not support IPv4 `email@123.123.123.123`, `email@[123.123.123.123]`, `postmaster@123.123.123.123`, `user@[68.185.127.196]`, `ipv4@[85.129.96.247]`, `valid@[79.208.229.53]`, `valid@[255.255.255.255]`, `valid@[255.0.55.2]`, `valid@[255.0.55.2]`, // do not support ipv6 `hgrebert0@[IPv6:4dc8:ac7:ce79:8878:1290:6098:5c50:1f25]`, `bshapiro4@[IPv6:3669:c709:e981:4884:59a3:75d1:166b:9ae]`, `jsmith@[IPv6:2001:db8::1]`, `postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:7334]`, `postmaster@[IPv6:2001:0db8:85a3:0000:0000:8a2e:0370:192.168.1.1]`, // microsoft test cases `plainaddress`, `#@%^%#$@#$@#.com`, `@domain.com`, `Joe Smith <email@domain.com>`, `email.domain.com`, `email@domain@domain.com`, `.email@domain.com`, `email.@domain.com`, `email..email@domain.com`, `あいうえお@domain.com`, `email@domain.com (Joe Smith)`, `email@domain`, `email@-domain.com`, `email@111.222.333.44444`, `email@domain..com`, `Abc.example.com`, `A@b@c@example.com`, `colin..hacks@domain.com`, `a"b(c)d,e:f;g<h>i[j\k]l@example.com`, `just"not"right@example.com`, `this is"not\allowed@example.com`, `this\ still\"not\\allowed@example.com`, // random `i_like_underscore@but_its_not_allowed_in_this_part.example.com`, `QA[icon]CHOCOLATE[icon]@test.com`, `invalid@-start.com`, `invalid@end.com-`, `a.b@c.d`, `invalid@[1.1.1.-1]`, `invalid@[68.185.127.196.55]`, `temp@[192.168.1]`, `temp@[9.18.122.]`, `double..point@test.com`, `asdad@test..com`, `asdad@hghg...sd...au`, `asdad@hghg........au`, `invalid@[256.2.2.48]`, `invalid@[256.2.2.48]`, `invalid@[999.465.265.1]`, `jkibbey4@[IPv6:82c4:19a8::70a9:2aac:557::ea69:d985:28d]`, `mlivesay3@[9952:143f:b4df:2179:49a1:5e82:b92e:6b6]`, `gbacher0@[IPv6:bc37:4d3f:5048:2e26:37cc:248e:df8e:2f7f:af]`, `invalid@[IPv6:5348:4ed3:5d38:67fb:e9b:acd2:c13:192.168.256.1]`, `test@.com`, `aaaaaaaaaaaaaaalongemailthatcausesregexDoSvulnerability@test.c`, ]; const emailSchema = z.string().email(); expect( validEmails.every((email) => { return emailSchema.safeParse(email).success; }) ).toBe(true); expect( invalidEmails.every((email) => { return emailSchema.safeParse(email).success === false; }) ).toBe(true); }); const validBase64Strings = [ "SGVsbG8gV29ybGQ=", // "Hello World" "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==", // "This is an encoded string" "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms=", // "Many hands make light work" "UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // "Patience is the key to success" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // "Base64 encoding is fun" "MTIzNDU2Nzg5MA==", // "1234567890" "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", // "abcdefghijklmnopqrstuvwxyz" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "ISIkJSMmJyonKCk=", // "!\"#$%&'()*" "", // Empty string is technically valid base64 "w7/Dv8O+w74K", // ÿÿþþ ]; for (const str of validBase64Strings) { test(`base64 should accept ${str}`, () => { expect(z.string().base64().safeParse(str).success).toBe(true); }); } const invalidBase64Strings = [ "12345", // Not padded correctly, not a multiple of 4 characters "12345===", // Not padded correctly "SGVsbG8gV29ybGQ", // Missing padding "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw", // Missing padding "!UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // Invalid character '!' "?QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // Invalid character '?' ".MTIzND2Nzg5MC4=", // Invalid character '.' "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", // Missing padding "w7_Dv8O-w74K", // Has - and _ characters (is base64url) ]; for (const str of invalidBase64Strings) { test(`base64 should reject ${str}`, () => { expect(z.string().base64().safeParse(str).success).toBe(false); }); } const validBase64URLStrings = [ "SGVsbG8gV29ybGQ", // "Hello World" "SGVsbG8gV29ybGQ=", // "Hello World" with padding "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw", // "This is an encoded string" "VGhpcyBpcyBhbiBlbmNvZGVkIHN0cmluZw==", // "This is an encoded string" with padding "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms", // "Many hands make light work" "TWFueSBoYW5kcyBtYWtlIGxpZ2h0IHdvcms=", // "Many hands make light work" with padding "UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // "Patience is the key to success" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg", // "Base64 encoding is fun" "QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // "Base64 encoding is fun" with padding "MTIzNDU2Nzg5MA", // "1234567890" "MTIzNDU2Nzg5MA==", // "1234567890" with padding "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo", // "abcdefghijklmnopqrstuvwxyz" "YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=", // "abcdefghijklmnopqrstuvwxyz with padding" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" "QUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVo=", // "ABCDEFGHIJKLMNOPQRSTUVWXYZ" with padding "ISIkJSMmJyonKCk", // "!\"#$%&'()*" "ISIkJSMmJyonKCk=", // "!\"#$%&'()*" with padding "", // Empty string is technically valid base64url "w7_Dv8O-w74K", // ÿÿþþ "123456", ]; for (const str of validBase64URLStrings) { test(`base64url should accept ${str}`, () => { expect(z.string().base64url().safeParse(str).success).toBe(true); }); } const invalidBase64URLStrings = [ "w7/Dv8O+w74K", // Has + and / characters (is base64) "12345", // Invalid length (not a multiple of 4 characters when adding allowed number of padding characters) "12345===", // Not padded correctly "!UGF0aWVuY2UgaXMgdGhlIGtleSB0byBzdWNjZXNz", // Invalid character '!' "?QmFzZTY0IGVuY29kaW5nIGlzIGZ1bg==", // Invalid character '?' ".MTIzND2Nzg5MC4=", // Invalid character '.' ]; for (const str of invalidBase64URLStrings) { test(`base64url should reject ${str}`, () => { expect(z.string().base64url().safeParse(str).success).toBe(false); }); } function makeJwt(header: object, payload: object) { const headerBase64 = Buffer.from(JSON.stringify(header)).toString("base64url"); const payloadBase64 = Buffer.from(JSON.stringify(payload)).toString("base64url"); const signature = "signature"; // Placeholder for the signature return `${headerBase64}.${payloadBase64}.${signature}`; } test("jwt validations", () => { const jwt = z.string().jwt(); const jwtWithAlg = z.string().jwt({ alg: "HS256" }); expect(() => jwt.parse("invalid")).toThrow(); expect(() => jwt.parse("invalid.invalid")).toThrow(); expect(() => jwt.parse("invalid.invalid.invalid")).toThrow(); // Valid JWTs const d1 = makeJwt({ typ: "JWT", alg: "HS256" }, {}); expect(() => jwt.parse(d1)).not.toThrow(); expect(() => jwtWithAlg.parse(d1)).not.toThrow(); // Invalid header const d2 = makeJwt({}, {}); expect(() => jwt.parse(d2)).toThrow(); // Wrong algorithm const d3 = makeJwt({ typ: "JWT", alg: "RS256" }, {}); expect(() => jwtWithAlg.parse(d3)).toThrow(); // missing typ is fine const d4 = makeJwt({ alg: "HS256" }, {}); jwt.parse(d4); // type isn't JWT const d5 = makeJwt({ typ: "SUP", alg: "HS256" }, { foo: "bar" }); expect(() => jwt.parse(d5)).toThrow(); // Custom error message const customMsg = "Invalid JWT token"; const jwtWithMsg = z.string().jwt({ message: customMsg }); try { jwtWithMsg.parse("invalid"); } catch (error) { expect((error as z.ZodError).issues[0].message).toBe(customMsg); } }); test("url validations", () => { const url = z.string().url(); url.parse("http://google.com"); url.parse("https://google.com/asdf?asdf=ljk3lk4&asdf=234#asdf"); expect(() => url.parse("asdf")).toThrow(); expect(() => url.parse("https:/")).toThrow(); expect(() => url.parse("asdfj@lkjsdf.com")).toThrow(); }); test("url error overrides", () => { try { z.string().url().parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("Invalid url"); } try { z.string().url("badurl").parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("badurl"); } try { z.string().url({ message: "badurl" }).parse("https"); } catch (err) { expect((err as z.ZodError).issues[0].message).toEqual("badurl"); } }); test("emoji validations", () => { const emoji = z.string().emoji(); emoji.parse("👋👋👋👋"); emoji.parse("🍺👩🚀🫡"); emoji.parse("💚💙💜💛❤️"); emoji.parse("🐛🗝🐏🍡🎦🚢🏨💫🎌☘🗡😹🔒🎬➡️🍹🗂🚨⚜🕑〽️🚦🌊🍴💍🍌💰😳🌺🍃"); emoji.parse("🇹🇷🤽🏿♂️"); emoji.parse( "😀😁😂🤣😃😄😅😆😉😊😋😎😍😘🥰😗😙😚☺️☺🙂🤗🤩🤔🤨😐😑😶🙄😏😣😥😮🤐😯😪😫😴😌😛😜😝🤤😒😓😔😕🙃🤑😲☹️☹🙁😖😞😟😤😢😭😦😧😨😩🤯😬😰😱🥵🥶😳🤪😵😡😠🤬😷🤒🤕🤢🤮🤧😇🤠🥳🥴🥺🤥🤫🤭🧐🤓😈👿🤡👹👺💀☠️☠ … [Строка слишком длинная. Вы можете скачать файл] ); expect(() => emoji.parse(":-)")).toThrow(); expect(() => emoji.parse("😀 is an emoji")).toThrow(); expect(() => emoji.parse("😀stuff")).toThrow(); expect(() => emoji.parse("stuff😀")).toThrow(); }); test("uuid", () => { const uuid = z.string().uuid("custom error"); uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0"); uuid.parse("d89e7b01-7598-ed11-9d7a-0022489382fd"); // new sequential id uuid.parse("00000000-0000-0000-0000-000000000000"); uuid.parse("b3ce60f8-e8b9-40f5-1150-172ede56ff74"); // Variant 0 - RFC 4122: Reserved, NCS backward compatibility uuid.parse("92e76bf9-28b3-4730-cd7f-cb6bc51f8c09"); // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility const result = uuid.safeParse("9491d710-3185-4e06-bea0-6a2f275345e0X"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("bad uuid", () => { const uuid = z.string().uuid("custom error"); uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0"); const result = uuid.safeParse("invalid uuid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("nanoid", () => { const nanoid = z.string().nanoid("custom error"); nanoid.parse("lfNZluvAxMkf7Q8C5H-QS"); nanoid.parse("mIU_4PJWikaU8fMbmkouz"); nanoid.parse("Hb9ZUtUa2JDm_dD-47EGv"); nanoid.parse("5Noocgv_8vQ9oPijj4ioQ"); const result = nanoid.safeParse("Xq90uDyhddC53KsoASYJGX"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("bad nanoid", () => { const nanoid = z.string().nanoid("custom error"); nanoid.parse("ySh_984wpDUu7IQRrLXAp"); const result = nanoid.safeParse("invalid nanoid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("custom error"); } }); test("cuid", () => { const cuid = z.string().cuid(); cuid.parse("ckopqwooh000001la8mbi2im9"); const result = cuid.safeParse("cifjhdsfhsd-invalid-cuid"); expect(result.success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid cuid"); } }); test("cuid2", () => { const cuid2 = z.string().cuid2(); const validStrings = [ "a", // short string "tz4a98xxat96iws9zmbrgj3a", // normal string "kf5vz6ssxe4zjcb409rjgo747tc5qjazgptvotk6", // longer than require("@paralleldrive/cuid2").bigLength ]; for (const s of validStrings) { cuid2.parse(s); } const invalidStrings = [ "", // empty string "tz4a98xxat96iws9zMbrgj3a", // include uppercase "tz4a98xxat96iws-zmbrgj3a", // involve symbols ]; const results = invalidStrings.map((s) => cuid2.safeParse(s)); expect(results.every((r) => !r.success)).toEqual(true); if (!results[0].success) { expect(results[0].error.issues[0].message).toEqual("Invalid cuid2"); } }); test("ulid", () => { const ulid = z.string().ulid(); ulid.parse("01ARZ3NDEKTSV4RRFFQ69G5FAV"); const result = ulid.safeParse("invalidulid"); expect(result.success).toEqual(false); const tooLong = "01ARZ3NDEKTSV4RRFFQ69G5FAVA"; expect(ulid.safeParse(tooLong).success).toEqual(false); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid ulid"); } const caseInsensitive = ulid.safeParse("01arZ3nDeKTsV4RRffQ69G5FAV"); expect(caseInsensitive.success).toEqual(true); }); test("regex", () => { z.string() .regex(/^moo+$/) .parse("mooooo"); expect(() => z.string().uuid().parse("purr")).toThrow(); }); test("regexp error message", () => { const result = z .string() .regex(/^moo+$/) .safeParse("boooo"); if (!result.success) { expect(result.error.issues[0].message).toEqual("Invalid"); } else { throw new Error("validation should have failed"); } expect(() => z.string().uuid().parse("purr")).toThrow(); }); test("regex lastIndex reset", () => { const schema = z.string().regex(/^\d+$/g); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); expect(schema.safeParse("123").success).toEqual(true); }); test("checks getters", () => { expect(z.string().email().isEmail).toEqual(true); expect(z.string().email().isURL).toEqual(false); expect(z.string().email().isCUID).toEqual(false); expect(z.string().email().isCUID2).toEqual(false); expect(z.string().email().isUUID).toEqual(false); expect(z.string().email().isNANOID).toEqual(false); expect(z.string().email().isIP).toEqual(false); expect(z.string().email().isCIDR).toEqual(false); expect(z.string().email().isULID).toEqual(false); expect(z.string().url().isEmail).toEqual(false); expect(z.string().url().isURL).toEqual(true); expect(z.string().url().isCUID).toEqual(false); expect(z.string().url().isCUID2).toEqual(false); expect(z.string().url().isUUID).toEqual(false); expect(z.string().url().isNANOID).toEqual(false); expect(z.string().url().isIP).toEqual(false); expect(z.string().url().isCIDR).toEqual(false); expect(z.string().url().isULID).toEqual(false); expect(z.string().cuid().isEmail).toEqual(false); expect(z.string().cuid().isURL).toEqual(false); expect(z.string().cuid().isCUID).toEqual(true); expect(z.string().cuid().isCUID2).toEqual(false); expect(z.string().cuid().isUUID).toEqual(false); expect(z.string().cuid().isNANOID).toEqual(false); expect(z.string().cuid().isIP).toEqual(false); expect(z.string().cuid().isCIDR).toEqual(false); expect(z.string().cuid().isULID).toEqual(false); expect(z.string().cuid2().isEmail).toEqual(false); expect(z.string().cuid2().isURL).toEqual(false); expect(z.string().cuid2().isCUID).toEqual(false); expect(z.string().cuid2().isCUID2).toEqual(true); expect(z.string().cuid2().isUUID).toEqual(false); expect(z.string().cuid2().isNANOID).toEqual(false); expect(z.string().cuid2().isIP).toEqual(false); expect(z.string().cuid2().isCIDR).toEqual(false); expect(z.string().cuid2().isULID).toEqual(false); expect(z.string().uuid().isEmail).toEqual(false); expect(z.string().uuid().isURL).toEqual(false); expect(z.string().uuid().isCUID).toEqual(false); expect(z.string().uuid().isCUID2).toEqual(false); expect(z.string().uuid().isUUID).toEqual(true); expect(z.string().uuid().isNANOID).toEqual(false); expect(z.string().uuid().isIP).toEqual(false); expect(z.string().uuid().isCIDR).toEqual(false); expect(z.string().uuid().isULID).toEqual(false); expect(z.string().nanoid().isEmail).toEqual(false); expect(z.string().nanoid().isURL).toEqual(false); expect(z.string().nanoid().isCUID).toEqual(false); expect(z.string().nanoid().isCUID2).toEqual(false); expect(z.string().nanoid().isUUID).toEqual(false); expect(z.string().nanoid().isNANOID).toEqual(true); expect(z.string().nanoid().isIP).toEqual(false); expect(z.string().nanoid().isCIDR).toEqual(false); expect(z.string().nanoid().isULID).toEqual(false); expect(z.string().ip().isEmail).toEqual(false); expect(z.string().ip().isURL).toEqual(false); expect(z.string().ip().isCUID).toEqual(false); expect(z.string().ip().isCUID2).toEqual(false); expect(z.string().ip().isUUID).toEqual(false); expect(z.string().ip().isNANOID).toEqual(false); expect(z.string().ip().isIP).toEqual(true); expect(z.string().ip().isCIDR).toEqual(false); expect(z.string().ip().isULID).toEqual(false); expect(z.string().cidr().isEmail).toEqual(false); expect(z.string().cidr().isURL).toEqual(false); expect(z.string().cidr().isCUID).toEqual(false); expect(z.string().cidr().isCUID2).toEqual(false); expect(z.string().cidr().isUUID).toEqual(false); expect(z.string().cidr().isNANOID).toEqual(false); expect(z.string().cidr().isIP).toEqual(false); expect(z.string().cidr().isCIDR).toEqual(true); expect(z.string().cidr().isULID).toEqual(false); expect(z.string().ulid().isEmail).toEqual(false); expect(z.string().ulid().isURL).toEqual(false); expect(z.string().ulid().isCUID).toEqual(false); expect(z.string().ulid().isCUID2).toEqual(false); expect(z.string().ulid().isUUID).toEqual(false); expect(z.string().ulid().isNANOID).toEqual(false); expect(z.string().ulid().isIP).toEqual(false); expect(z.string().ulid().isCIDR).toEqual(false); expect(z.string().ulid().isULID).toEqual(true); }); test("min max getters", () => { expect(z.string().min(5).minLength).toEqual(5); expect(z.string().min(5).min(10).minLength).toEqual(10); expect(z.string().minLength).toEqual(null); expect(z.string().max(5).maxLength).toEqual(5); expect(z.string().max(5).max(1).maxLength).toEqual(1); expect(z.string().maxLength).toEqual(null); }); test("trim", () => { expect(z.string().trim().min(2).parse(" 12 ")).toEqual("12"); // ordering of methods is respected expect(z.string().min(2).trim().parse(" 1 ")).toEqual("1"); expect(() => z.string().trim().min(2).parse(" 1 ")).toThrow(); }); test("lowerCase", () => { expect(z.string().toLowerCase().parse("ASDF")).toEqual("asdf"); expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF"); }); test("datetime", () => { const a = z.string().datetime({}); expect(a.isDatetime).toEqual(true); const b = z.string().datetime({ offset: true }); expect(b.isDatetime).toEqual(true); const c = z.string().datetime({ precision: 3 }); expect(c.isDatetime).toEqual(true); const d = z.string().datetime({ offset: true, precision: 0 }); expect(d.isDatetime).toEqual(true); const { isDatetime } = z.string().datetime(); expect(isDatetime).toEqual(true); }); test("datetime parsing", () => { const datetime = z.string().datetime(); datetime.parse("1970-01-01T00:00:00.000Z"); datetime.parse("2022-10-13T09:52:31.816Z"); datetime.parse("2022-10-13T09:52:31.8162314Z"); datetime.parse("1970-01-01T00:00:00Z"); datetime.parse("2022-10-13T09:52:31Z"); datetime.parse("2022-10-13T09:52Z"); expect(() => datetime.parse("")).toThrow(); expect(() => datetime.parse("foo")).toThrow(); expect(() => datetime.parse("2020-10-14")).toThrow(); expect(() => datetime.parse("T18:45:12.123")).toThrow(); expect(() => datetime.parse("2020-10-14T17:42:29+00:00")).toThrow(); expect(() => datetime.parse("2020-10-14T17:42.123+00:00")).toThrow(); const datetimeNoMs = z.string().datetime({ precision: 0 }); datetimeNoMs.parse("1970-01-01T00:00:00Z"); datetimeNoMs.parse("2022-10-13T09:52:31Z"); datetimeNoMs.parse("2022-10-13T09:52Z"); expect(() => datetimeNoMs.parse("tuna")).toThrow(); expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow(); expect(() => datetimeNoMs.parse("1970-01-01T00:00:00.Z")).toThrow(); expect(() => datetimeNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow(); const datetime3Ms = z.string().datetime({ precision: 3 }); datetime3Ms.parse("1970-01-01T00:00:00.000Z"); datetime3Ms.parse("2022-10-13T09:52:31.123Z"); expect(() => datetime3Ms.parse("tuna")).toThrow(); expect(() => datetime3Ms.parse("1970-01-01T00:00:00.1Z")).toThrow(); expect(() => datetime3Ms.parse("1970-01-01T00:00:00.12Z")).toThrow(); expect(() => datetime3Ms.parse("2022-10-13T09:52:31Z")).toThrow(); expect(() => datetime3Ms.parse("2022-10-13T09:52Z")).toThrow(); const datetimeOffset = z.string().datetime({ offset: true }); datetimeOffset.parse("1970-01-01T00:00:00.000Z"); datetimeOffset.parse("2022-10-13T09:52:31.816234134Z"); datetimeOffset.parse("1970-01-01T00:00:00Z"); datetimeOffset.parse("2022-10-13T09:52:31.4Z"); datetimeOffset.parse("2020-10-14T17:42:29+00:00"); datetimeOffset.parse("2020-10-14T17:42:29+03:15"); datetimeOffset.parse("2020-10-14T17:42:29+0315"); datetimeOffset.parse("2020-10-14T17:42+0315"); expect(() => datetimeOffset.parse("2020-10-14T17:42:29+03")); expect(() => datetimeOffset.parse("tuna")).toThrow(); expect(() => datetimeOffset.parse("2022-10-13T09:52:31.Z")).toThrow(); const datetimeOffsetNoMs = z.string().datetime({ offset: true, precision: 0 }); datetimeOffsetNoMs.parse("1970-01-01T00:00:00Z"); datetimeOffsetNoMs.parse("2022-10-13T09:52:31Z"); datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00:00"); datetimeOffsetNoMs.parse("2020-10-14T17:42:29+0000"); datetimeOffsetNoMs.parse("2020-10-14T17:42+0000"); expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29+00")).toThrow(); expect(() => datetimeOffsetNoMs.parse("tuna")).toThrow(); expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.000Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("1970-01-01T00:00:00.Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("2022-10-13T09:52:31.816Z")).toThrow(); expect(() => datetimeOffsetNoMs.parse("2020-10-14T17:42:29.124+00:00")).toThrow(); const datetimeOffset4Ms = z.string().datetime({ offset: true, precision: 4 }); datetimeOffset4Ms.parse("1970-01-01T00:00:00.1234Z"); datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00:00"); datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+0000"); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42:29.1234+00")).toThrow(); expect(() => datetimeOffset4Ms.parse("tuna")).toThrow(); expect(() => datetimeOffset4Ms.parse("1970-01-01T00:00:00.123Z")).toThrow(); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42:29.124+00:00")).toThrow(); expect(() => datetimeOffset4Ms.parse("2020-10-14T17:42+00:00")).toThrow(); }); test("date", () => { const a = z.string().date(); expect(a.isDate).toEqual(true); }); test("date parsing", () => { const date = z.string().date(); date.parse("1970-01-01"); date.parse("2022-01-31"); date.parse("2022-03-31"); date.parse("2022-04-30"); date.parse("2022-05-31"); date.parse("2022-06-30"); date.parse("2022-07-31"); date.parse("2022-08-31"); date.parse("2022-09-30"); date.parse("2022-10-31"); date.parse("2022-11-30"); date.parse("2022-12-31"); date.parse("2000-02-29"); date.parse("2400-02-29"); expect(() => date.parse("2022-02-29")).toThrow(); expect(() => date.parse("2100-02-29")).toThrow(); expect(() => date.parse("2200-02-29")).toThrow(); expect(() => date.parse("2300-02-29")).toThrow(); expect(() => date.parse("2500-02-29")).toThrow(); expect(() => date.parse("")).toThrow(); expect(() => date.parse("foo")).toThrow(); expect(() => date.parse("200-01-01")).toThrow(); expect(() => date.parse("20000-01-01")).toThrow(); expect(() => date.parse("2000-0-01")).toThrow(); expect(() => date.parse("2000-011-01")).toThrow(); expect(() => date.parse("2000-01-0")).toThrow(); expect(() => date.parse("2000-01-011")).toThrow(); expect(() => date.parse("2000/01/01")).toThrow(); expect(() => date.parse("01-01-2022")).toThrow(); expect(() => date.parse("01/01/2022")).toThrow(); expect(() => date.parse("2000-01-01 00:00:00Z")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29+00:00")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29Z")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29")).toThrow(); expect(() => date.parse("2020-10-14T17:42:29.123Z")).toThrow(); expect(() => date.parse("2000-00-12")).toThrow(); expect(() => date.parse("2000-12-00")).toThrow(); expect(() => date.parse("2000-01-32")).toThrow(); expect(() => date.parse("2000-13-01")).toThrow(); expect(() => date.parse("2000-21-01")).toThrow(); expect(() => date.parse("2000-02-30")).toThrow(); expect(() => date.parse("2000-02-31")).toThrow(); expect(() => date.parse("2000-04-31")).toThrow(); expect(() => date.parse("2000-06-31")).toThrow(); expect(() => date.parse("2000-09-31")).toThrow(); expect(() => date.parse("2000-11-31")).toThrow(); }); test("time", () => { const a = z.string().time(); expect(a.isTime).toEqual(true); }); test("time parsing", () => { const time = z.string().time(); time.parse("00:00:00"); time.parse("23:00:00"); time.parse("00:59:00"); time.parse("00:00:59"); time.parse("23:59:59"); time.parse("09:52:31"); time.parse("23:59:59.9999999"); time.parse("23:59"); expect(() => time.parse("")).toThrow(); expect(() => time.parse("foo")).toThrow(); expect(() => time.parse("00:00:00Z")).toThrow(); expect(() => time.parse("0:00:00")).toThrow(); expect(() => time.parse("00:0:00")).toThrow(); expect(() => time.parse("00:00:0")).toThrow(); expect(() => time.parse("00:00:00.000+00:00")).toThrow(); expect(() => time.parse("24:00:00")).toThrow(); expect(() => time.parse("00:60:00")).toThrow(); expect(() => time.parse("00:00:60")).toThrow(); expect(() => time.parse("24:60:60")).toThrow(); expect(() => time.parse("24:60")).toThrow(); const time2 = z.string().time({ precision: 2 }); time2.parse("00:00:00.00"); time2.parse("09:52:31.12"); time2.parse("23:59:59.99"); expect(() => time2.parse("")).toThrow(); expect(() => time2.parse("foo")).toThrow(); expect(() => time2.parse("00:00:00")).toThrow(); expect(() => time2.parse("00:00:00.00Z")).toThrow(); expect(() => time2.parse("00:00:00.0")).toThrow(); expect(() => time2.parse("00:00:00.000")).toThrow(); expect(() => time2.parse("00:00:00.00+00:00")).toThrow(); expect(() => time2.parse("23:59")).toThrow(); // const time3 = z.string().time({ offset: true }); // time3.parse("00:00:00Z"); // time3.parse("09:52:31Z"); // time3.parse("00:00:00+00:00"); // time3.parse("00:00:00+0000"); // time3.parse("00:00:00.000Z"); // time3.parse("00:00:00.000+00:00"); // time3.parse("00:00:00.000+0000"); // expect(() => time3.parse("")).toThrow(); // expect(() => time3.parse("foo")).toThrow(); // expect(() => time3.parse("00:00:00")).toThrow(); // expect(() => time3.parse("00:00:00.000")).toThrow(); // const time4 = z.string().time({ offset: true, precision: 0 }); // time4.parse("00:00:00Z"); // time4.parse("09:52:31Z"); // time4.parse("00:00:00+00:00"); // time4.parse("00:00:00+0000"); // expect(() => time4.parse("")).toThrow(); // expect(() => time4.parse("foo")).toThrow(); // expect(() => time4.parse("00:00:00.0")).toThrow(); // expect(() => time4.parse("00:00:00.000")).toThrow(); // expect(() => time4.parse("00:00:00.000+00:00")).toThrow(); }); test("duration", () => { const duration = z.string().duration(); expect(duration.isDuration).toEqual(true); const validDurations = [ "P3Y6M4DT12H30M5S", "P2Y9M3DT12H31M8.001S", "+P3Y6M4DT12H30M5S", "-PT0.001S", "+PT0.001S", "PT0,001S", "PT12H30M5S", "-P2M1D", "P-2M-1D", "-P5DT10H", "P-5DT-10H", "P1Y", "P2MT30M", "PT6H", "P5W", "P0.5Y", "P0,5Y", "P42YT7.004M", ]; const invalidDurations = ["foo bar", "", " ", "P", "T1H", "P0.5Y1D", "P0,5Y6M", "P1YT"]; for (const val of validDurations) { const result = duration.safeParse(val); if (!result.success) { throw Error(`Valid duration could not be parsed: ${val}`); } } for (const val of invalidDurations) { const result = duration.safeParse(val); if (result.success) { throw Error(`Invalid duration was successful parsed: ${val}`); } expect(result.error.issues[0].message).toEqual("Invalid duration"); } }); test("IP validation", () => { const ip = z.string().ip(); expect(ip.safeParse("122.122.122.122").success).toBe(true); const ipv4 = z.string().ip({ version: "v4" }); expect(() => ipv4.parse("6097:adfa:6f0b:220d:db08:5021:6191:7990")).toThrow(); const ipv6 = z.string().ip({ version: "v6" }); expect(() => ipv6.parse("254.164.77.1")).toThrow(); const validIPs = [ "1e5e:e6c8:daac:514b:114b:e360:d8c0:682c", "9d4:c956:420f:5788:4339:9b3b:2418:75c3", "474f:4c83::4e40:a47:ff95:0cda", "d329:0:25b4:db47:a9d1:0:4926:0000", "e48:10fb:1499:3e28:e4b6:dea5:4692:912c", "114.71.82.94", "0.0.0.0", "37.85.236.115", "2001:4888:50:ff00:500:d::", "2001:4888:50:ff00:0500:000d:000:0000", "2001:4888:50:ff00:0500:000d:0000:0000", ]; const invalidIPs = [ "d329:1be4:25b4:db47:a9d1:dc71:4926:992c:14af", "d5e7:7214:2b78::3906:85e6:53cc:709:32ba", "8f69::c757:395e:976e::3441", "54cb::473f:d516:0.255.256.22", "54cb::473f:d516:192.168.1", "256.0.4.4", "-1.0.555.4", "0.0.0.0.0", "1.1.1", ]; // no parameters check IPv4 or IPv6 const ipSchema = z.string().ip(); expect(validIPs.every((ip) => ipSchema.safeParse(ip).success)).toBe(true); expect(invalidIPs.every((ip) => ipSchema.safeParse(ip).success === false)).toBe(true); }); test("CIDR validation", () => { const ipv4Cidr = z.string().cidr({ version: "v4" }); expect(() => ipv4Cidr.parse("2001:0db8:85a3::8a2e:0370:7334/64")).toThrow(); const ipv6Cidr = z.string().cidr({ version: "v6" }); expect(() => ipv6Cidr.parse("192.168.0.1/24")).toThrow(); const validCidrs = [ "192.168.0.0/24", "10.0.0.0/8", "203.0.113.0/24", "192.0.2.0/24", "127.0.0.0/8", "172.16.0.0/12", "192.168.1.0/24", "fc00::/7", "fd00::/8", "2001:db8::/32", "2607:f0d0:1002:51::4/64", "2001:0db8:85a3:0000:0000:8a2e:0370:7334/128", "2001:0db8:1234:0000::/64", ]; const invalidCidrs = [ "192.168.1.1/33", "10.0.0.1/-1", "192.168.1.1/24/24", "192.168.1.0/abc", "2001:db8::1/129", "2001:db8::1/-1", "2001:db8::1/64/64", "2001:db8::1/abc", ]; // no parameters check IPv4 or IPv6 const cidrSchema = z.string().cidr(); expect(validCidrs.every((ip) => cidrSchema.safeParse(ip).success)).toBe(true); expect(invalidCidrs.every((ip) => cidrSchema.safeParse(ip).success === false)).toBe(true); });