use rstest::*;
use serde_json::json;
use llg_test_utils::json_schema_check;
#[rstest]
#[case("1963-06-19T08:30:06.283185Z")] #[case("1963-06-19T08:30:06Z")] #[case("1985-04-12T23:20:50.52Z")] #[case("1996-12-19T16:39:57-08:00")] #[case("1990-12-31T23:59:60Z")] #[case("1990-12-31T15:59:60-08:00")] #[case("1937-01-01T12:00:27.87+00:20")] #[case("1963-06-19t08:30:06.283185z")] #[case("1990-12-31T15:59:50.123-08:00")] #[case("2020-01-31T23:59:59Z")] #[case("2020-04-30T12:00:00Z")] #[case("2020-02-29T00:00:00Z")] #[case("2020-01-01T00:00:00Z")] #[case("2020-06-15T10:00:00+23:59")] pub fn valid_date_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date-time"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("1963-06-38T08:30:06.283185Z")] #[case("1998-12-31T23:59:61Z")] #[case("1990-02-31T15:59:59.123-08:00")] #[case("1990-12-31T15:59:59-24:00")] #[case("1963-06-19T08:30:06.28123+01:00Z")] #[case("1990-12-31T24:00:00Z")] #[case("1990-12-31T15:60:00Z")] #[case("1990-12-31T10:00:00+10:60")] #[case("06/19/1963 08:30:06 PST")] #[case("2013-350T01:01:01")] #[case("1963-6-19T08:30:06.283185Z")] #[case("1963-06-1T08:30:06.283185Z")] #[case("2020-04-31T12:00:00Z")] #[case("2020-13-01T00:00:00Z")] #[case("1963-06-1\u{09ea}T00:00:00Z")] #[case("1963-06-11T0\u{09ea}:00:00Z")] pub fn bad_date_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date-time"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("08:30:06.283185Z")] #[case("08:30:06Z")] #[case("23:59:60Z")] #[case("23:20:50.52Z")] #[case("08:30:06+00:20")] #[case("08:30:06-08:00")] #[case("08:30:06z")] #[case("23:59:60+00:00")] #[case("01:29:60+01:30")] #[case("23:29:60+23:30")] #[case("15:59:60-08:00")] #[case("00:29:60-23:30")] #[case("00:00:00Z")] #[case("23:59:59Z")] pub fn valid_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"time"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("28:30:06.283185Z")] #[case("008:030:006Z")] #[case("8:3:6Z")] #[case("8:0030:6Z")] #[case("24:00:00Z")] #[case("00:60:00Z")] #[case("00:00:61Z")] #[case("01:02:03+24:00")] #[case("01:02:03+00:60")] #[case("01:02:03Z+00:30")] #[case("08:30:06 PST")] #[case("01:01:01,1111")] #[case("12:00:00")] #[case("12:00:00.52")] #[case("08:30:06#00:20")] #[case("ab:cd:ef")] #[case("1\u{09e8}:00:00Z")] #[case("08:30:06-8:000")] pub fn bad_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"time"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("1963-06-19")] #[case("2020-01-31")] #[case("2020-02-29")] #[case("2020-03-31")] #[case("2020-04-30")] #[case("2020-05-31")] #[case("2020-06-30")] #[case("2020-07-31")] #[case("2020-08-31")] #[case("2020-09-30")] #[case("2020-10-31")] #[case("2020-11-30")] #[case("2020-12-31")] pub fn valid_date(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("1963-13-19")] #[case("2020-01-32")] #[case("2020-02-30")] #[case("2020-04-31")] #[case("2020-06-31")] #[case("2020-09-31")] #[case("2020-11-31")] #[case("2020-13-01")] #[case("06/19/1963")] #[case("2013-350")] #[case("1998-1-20")] #[case("1998-01-1")] #[case("2020-00-01")] #[case("1963-06-1\u{09ea}")] #[case("20230328")] #[case("2023-W01")] #[case("2023-W13-2")] #[case("2022W527")] pub fn bad_date(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("P1M")] #[case("P4DT12H30M5S")] #[case("P4Y")] #[case("PT0S")] #[case("P0D")] #[case("PT1M")] #[case("PT36H")] #[case("P1DT12H")] #[case("P2W")] #[case("P1Y2M3DT4H5M6S")] #[case("PT1H")] #[case("PT1S")] pub fn valid_duration(#[case] s: &str) {
let schema = json!({"type":"string", "format":"duration"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("P2D1Y")] #[case("PT1D")] #[case("4DT12H30M5S")] #[case("P")] #[case("P1YT")] #[case("PT")] #[case("P1D2H")] #[case("P2S")] #[case("P1Y2W")] #[case("P1")] #[case("P\u{09e8}Y")] pub fn bad_duration(#[case] s: &str) {
let schema = json!({"type":"string", "format":"duration"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("joe.bloggs@example.com")] #[case("te~st@example.com")] #[case("~test@example.com")] #[case("test~@example.com")] #[case("te.s.t@example.com")] #[case("joe.bloggs@[127.0.0.1]")] #[case("user+tag@example.com")] #[case("a@b.com")] pub fn valid_email(#[case] s: &str) {
let schema = json!({"type":"string", "format":"email"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("joe.bloggs@@example.com")] #[case("2962")] #[case(".test@example.com")] #[case("test.@example.com")] #[case("te..st@example.com")] #[case("joe.bloggs@invalid=domain.com")] #[case("joe.bloggs@[127.0.0.300]")] pub fn bad_email(#[case] s: &str) {
let schema = json!({"type":"string", "format":"email"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("hostnam3")] #[case("www.example.com")] #[case("hostname")] #[case("h0stn4me")] #[case("1host")] #[case("host-name")] #[case("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijk.com")] #[case("a")] pub fn valid_hostname(#[case] s: &str) {
let schema = json!({"type":"string", "format":"hostname"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("hostnam3-")] #[case("")] #[case(".")] #[case(".example")] #[case("example.")] #[case("-hostname")] #[case("host_name")] #[case("abcdefghijklmnopqrstuvwxyzabcdefghijklmnopqrstuvwxyzabcdefghijkl.com")] pub fn bad_hostname(#[case] s: &str) {
let schema = json!({"type":"string", "format":"hostname"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("192.168.0.1")] #[case("0.0.0.0")] #[case("255.255.255.255")] #[case("127.0.0.1")] #[case("1.2.3.4")] #[case("87.10.0.1")] pub fn valid_ipv4(#[case] s: &str) {
let schema = json!({"type":"string", "format":"ipv4"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("192.168.0.0.1")] #[case("256.256.256.256")] #[case("127.0")] #[case("0x7f000001")] #[case("2130706433")] #[case("087.10.0.1")] #[case("192.168.1.0/24")] #[case("1\u{09e8}7.0.0.1")] pub fn bad_ipv4(#[case] s: &str) {
let schema = json!({"type":"string", "format":"ipv4"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("::42:ff:1")] #[case("::1")] #[case("::abef")] #[case("::")] #[case("d6::")] #[case("1:d6::42")] #[case("1:2:3:4:5:6:7:8")] #[case("2001:0db8:85a3:0000:0000:8a2e:0370:7334")] #[case("fe80::1")] #[case("2001:db8::1")] pub fn valid_ipv6(#[case] s: &str) {
let schema = json!({"type":"string", "format":"ipv6"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("1:1:1:1:1:1:1:1:1:1:1:1:1:1:1:1")] #[case("12345::")] #[case("::abcef")] #[case("::laptop")] #[case(":2:3:4:5:6:7:8")] #[case("1:2:3:4:5:6:7:")] #[case("1::d6::42")] #[case("1:2:3:4:5:::8")] #[case("1:2:3:4:5:6:7")] #[case("1")] #[case("1:2:3:4:5:6:7:\u{09ea}")] pub fn bad_ipv6(#[case] s: &str) {
let schema = json!({"type":"string", "format":"ipv6"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("2eb8aa08-AA98-11ea-B4Aa-73B441D16380")] #[case("2EB8AA08-AA98-11EA-B4AA-73B441D16380")] #[case("2eb8aa08-aa98-11ea-b4aa-73b441d16380")] #[case("00000000-0000-0000-0000-000000000000")] #[case("98d80576-482e-427f-8434-7f86890ab222")] #[case("99c17cbb-656f-564a-940f-1a4568f03487")] #[case("99c17cbb-656f-f64a-940f-1a4568f03487")] pub fn valid_uuid(#[case] s: &str) {
let schema = json!({"type":"string", "format":"uuid"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("2eb8-aa08-aa98-11ea-b4aa73b44-1d16380")] #[case("2eb8aa08-aa98-11ea-b4aa-73b441d1638")] #[case("2eb8aa08-aa98-11ea-73b441d16380")] #[case("2eb8aa08-aa98-11ea-b4ga-73b441d16380")] #[case("2eb8aa08aa9811eab4aa73b441d16380")] #[case("2eb8aa08aa98-11ea-b4aa73b441d16380")] #[case("2eb8aa08aa9811eab4aa73b441d16380----")] #[case("2eb8aa0-8aa98-11e-ab4aa7-3b441d16380")] pub fn bad_uuid(#[case] s: &str) {
let schema = json!({"type":"string", "format":"uuid"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("ftp://ftp.is.co.za/rfc/rfc1808.txt")]
#[case("http://www.ietf.org/rfc/rfc2396.txt")]
#[case("ldap://[2001:db8::7]/c=GB?objectClass?one")]
#[case("mailto:John.Doe@example.com")]
#[case("news:comp.infosystems.www.servers.unix")]
#[case("tel:+1-816-555-1212")]
#[case("telnet://192.0.2.16:80/")]
#[case("urn:oasis:names:specification:docbook:dtd:xml:4.1.2")]
#[case("http://example.com")]
#[case("https://example.com/path/to/resource")]
#[case("https://example.com?query=value")]
#[case("https://example.com#section")]
#[case("http://example.com:8080/")]
#[case("ftp://user:pass@ftp.example.com/")]
#[case("http://192.168.1.1/")]
#[case("http://[::1]:8080/path")]
#[case("https://example.com/path%20with%20spaces")]
#[case("file:///path/to/file")]
#[case("tel:+1-201-555-0123;ext=456")]
#[case("http://[2001:0db8:85a3:0000:0000:8a2e:0370:7334]/")] #[case("http://[2001:db8:85a3::8a2e:370:7334]/")]
#[case("http://[::1]/")] #[case("http://[::]/")]
#[case("http://[2001:db8::1]:8080/path")] #[case("http://user:pass@[2001:db8::1]:8080/path/to/resource")] #[case("http://[v1.test]/")] #[case("ssh://git@github.com:22/user/repo.git")] #[case("git://github.com/user/repo.git")] #[case("svn://svn.example.com/repo/trunk")] #[case("sftp://user@host.example.com/path/to/file")] #[case("s3://bucket-name/key/path")] #[case("data:text/plain;base64,SGVsbG8=")] #[case("javascript:void(0)")] #[case("magnet:?xt=urn:btih:abc123")] #[case("redis://localhost:6379/0")] #[case("postgres://user:pass@localhost:5432/db")] #[case("mysql://user:pass@localhost:3306/db")] #[case("mongodb://localhost:27017/mydb")] #[case("amqp://user:pass@host:5672/vhost")] #[case("ws://example.com/socket")] #[case("wss://example.com/socket")] #[case("irc://irc.example.com:6667/channel")] #[case("xmpp:user@example.com")] #[case("sip:user@example.com")] #[case("sips:user@example.com:5061")] #[case("rtsp://media.example.com:554/stream")] #[case("spotify:track:4uLU6hMCjMI75M1A2tKUQC")] #[case("slack://channel?team=T123&id=C456")] #[case("vscode://file/path/to/file.txt")] #[case("x-custom-scheme://anything/goes/here")] #[case("git+ssh://git@github.com/user/repo.git")] #[case("coap+tcp://example.com/sensor")] #[case("ms-windows-store://pdp?productid=abc123")] #[case("https://user:pass@api.example.com:8443/v2/users/123/profile?name=John%20Doe&active=true&sort=desc#contact-info")]
#[case("http://foo.bar/?baz=qux#quux")] #[case("http://foo.com/blah_(wikipedia)_blah#cite-1")] #[case("http://foo.bar/?q=Test%20URL-encoded%20stuff")] #[case("http://xn--nw2a.xn--j6w193g/")] #[case("http://-.~_!$&'()*+,;=:%40:80%2f::::::@example.com")] #[case("http://223.255.255.254")] pub fn valid_uri(#[case] s: &str) {
let schema = json!({"type":"string", "format":"uri"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("//example.com/path")] #[case("123://example.com")] #[case("http://example.com/%GG")] #[case("/path/to/resource")] #[case("http://example.com/path with spaces")] #[case("http://[:::]/")] #[case("http://2001:db8::1/")] #[case("http://[2001:db8:85a3:0000:0000:8a2e:0370:7334:extra]/")] #[case("\\\\WINDOWS\\fileshare")] #[case("abc")] #[case("http:// shouldfail.com")] #[case(":// should fail")] #[case("bar,baz:foo")]
#[case("https://[@example.org/test.txt")] #[case("https://example.org/foobar\\.txt")] #[case("https://example.org/foobar<>.txt")] #[case("https://example.org/foobar{}.txt")] #[case("https://example.org/foobar^.txt")] #[case("https://example.org/foobar`.txt")] #[case("https://example.org/foo bar.txt")] #[case("https://example.org/foobar|.txt")] #[case("a@b://example.com")] #[case("://example.com")] #[case("http://example.com:abc/")] #[case("http://user@@example.com/")] #[case("http://[::1/path")] #[case("http://exa mple.com/")] #[case("http://example.com/%")] #[case("http://example.com/%a")] #[case("http://example.com/path\x00")] #[case("http://example.com/\t")] pub fn bad_uri(#[case] s: &str) {
let schema = json!({"type":"string", "format":"uri"});
json_schema_check(&schema, &json!(s), false);
}
#[rstest]
#[case("1998-12-31T23:58:60Z")] #[case("1998-12-31T22:59:60Z")] #[case("2021-02-29T12:00:00Z")] pub fn regex_accepts_but_invalid_date_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date-time"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("22:59:60Z")] #[case("23:58:60Z")] #[case("12:30:60Z")] pub fn regex_accepts_but_invalid_time(#[case] s: &str) {
let schema = json!({"type":"string", "format":"time"});
json_schema_check(&schema, &json!(s), true);
}
#[rstest]
#[case("2021-02-29")] #[case("1900-02-29")] #[case("2100-02-29")] pub fn regex_accepts_but_invalid_date(#[case] s: &str) {
let schema = json!({"type":"string", "format":"date"});
json_schema_check(&schema, &json!(s), true);
}
#[test]
pub fn regex_accepts_but_invalid_hostname() {
let schema = json!({"type":"string", "format":"hostname"});
let long_hostname = format!("{0}.{0}.{0}.{0}.{0}.com", "a".repeat(50));
assert!(
long_hostname.len() > 253,
"Test hostname must exceed 253 chars"
);
json_schema_check(&schema, &json!(long_hostname), true);
}
#[rstest]
#[case("Some string")]
pub fn valid_unknown(#[case] s: &str) {
let schema = json!({"type":"string", "format":"unknown"});
json_schema_check(&schema, &json!(s), true);
}