PHP 8's (string) cast of a float formats with the precision ini (14 significant digits, %.14G), not the shortest round-trip form: 1/3 is 0.33333333333333, 1e14 is 1.0E+14 and 5e-324 is 4.9406564584125E-324. phpFloatString, used for the string form of JSON floats in size, in, regex and integer checks, printed up to 17 digits and switched to the exponent form only from 1e15. TestPHPFloatStringMatchesPHPCast pins 27 values to php -r output.
255 lines
18 KiB
Go
255 lines
18 KiB
Go
package lagoon
|
|
|
|
import (
|
|
"encoding/json"
|
|
"math"
|
|
"reflect"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// laravelRuleCases is a truth table for ValidateRequest. Each want is the
|
|
// English errors object that WinterCMS's validator (Laravel 9, the
|
|
// vendored winter/storm Factory) returned for the same JSON input and rule
|
|
// strings, so a case that fails here is a divergence from PHP. Inputs are
|
|
// decoded with UseNumber, as the application decodes request bodies.
|
|
var laravelRuleCases = []struct {
|
|
name string
|
|
data string
|
|
rules [][2]string
|
|
want string
|
|
}{
|
|
{"accepted-yes", `{"t":"yes"}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-on", `{"t":"on"}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-one-string", `{"t":"1"}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-true-string", `{"t":"true"}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-true", `{"t":true}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-one", `{"t":1}`, [][2]string{{"t", `accepted`}}, `{}`},
|
|
{"accepted-no", `{"t":"no"}`, [][2]string{{"t", `accepted`}}, `{"t":["The t must be accepted."]}`},
|
|
{"accepted-zero", `{"t":0}`, [][2]string{{"t", `accepted`}}, `{"t":["The t must be accepted."]}`},
|
|
{"accepted-false-string", `{"t":"false"}`, [][2]string{{"t", `accepted`}}, `{"t":["The t must be accepted."]}`},
|
|
{"accepted-two", `{"t":2}`, [][2]string{{"t", `accepted`}}, `{"t":["The t must be accepted."]}`},
|
|
{"accepted-missing", `{}`, [][2]string{{"t", `accepted`}}, `{"t":["The t must be accepted."]}`},
|
|
{"array-keys-ok", `{"a":{"x":1,"y":2}}`, [][2]string{{"a", `array:x,y`}}, `{}`},
|
|
{"array-keys-extra", `{"a":{"x":1,"z":2}}`, [][2]string{{"a", `array:x,y`}}, `{"a":["The a must be an array."]}`},
|
|
{"array-scalar", `{"a":"str"}`, [][2]string{{"a", `array`}}, `{"a":["The a must be an array."]}`},
|
|
{"array-list-keys", `{"a":[1,2]}`, [][2]string{{"a", `array:0,1`}}, `{}`},
|
|
{"array-list-keys-short", `{"a":[1,2,3]}`, [][2]string{{"a", `array:0,1`}}, `{"a":["The a must be an array."]}`},
|
|
{"boolean-true", `{"b":true}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-false", `{"b":false}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-zero", `{"b":0}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-one", `{"b":1}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-zero-string", `{"b":"0"}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-one-string", `{"b":"1"}`, [][2]string{{"b", `boolean`}}, `{}`},
|
|
{"boolean-true-string", `{"b":"true"}`, [][2]string{{"b", `boolean`}}, `{"b":["The b field must be true or false."]}`},
|
|
{"boolean-yes", `{"b":"yes"}`, [][2]string{{"b", `boolean`}}, `{"b":["The b field must be true or false."]}`},
|
|
{"boolean-two", `{"b":2}`, [][2]string{{"b", `boolean`}}, `{"b":["The b field must be true or false."]}`},
|
|
{"boolean-two-string", `{"b":"2"}`, [][2]string{{"b", `boolean`}}, `{"b":["The b field must be true or false."]}`},
|
|
{"boolean-float-one", `{"b":1.0}`, [][2]string{{"b", `boolean`}}, `{"b":["The b field must be true or false."]}`},
|
|
{"numeric-exp", `{"n":"1e3"}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-spaces", `{"n":" 12 "}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-trailing-letters", `{"n":"12abc"}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"numeric-neg-frac", `{"n":"-.5"}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-frac-exp", `{"n":".5e1"}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-plus", `{"n":"+1"}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-bare-e", `{"n":"1e"}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"numeric-hex", `{"n":"0x1A"}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"numeric-float", `{"n":12.5}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-true", `{"n":true}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"numeric-dot", `{"n":"."}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"numeric-leading-space", `{"n":" 12"}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-trailing-space", `{"n":"12 "}`, [][2]string{{"n", `numeric`}}, `{}`},
|
|
{"numeric-array", `{"n":[1]}`, [][2]string{{"n", `numeric`}}, `{"n":["The n must be a number."]}`},
|
|
{"integer-leading-zero", `{"i":"007"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-neg-zero", `{"i":"-0"}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-plus", `{"i":"+5"}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-spaces", `{"i":" 5 "}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-decimal-string", `{"i":"5.0"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-float-whole", `{"i":5.0}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-float-exp", `{"i":1000.0}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-overflow", `{"i":"9223372036854775808"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-max", `{"i":"9223372036854775807"}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-true", `{"i":true}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"integer-false", `{"i":false}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-array", `{"i":[]}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-exp-string", `{"i":"1e3"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-minus", `{"i":"-"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-plus-only", `{"i":"+"}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-float-frac", `{"i":5.5}`, [][2]string{{"i", `integer`}}, `{"i":["The i must be an integer."]}`},
|
|
{"integer-zero", `{"i":"0"}`, [][2]string{{"i", `integer`}}, `{}`},
|
|
{"in-ok", `{"f":"LP"}`, [][2]string{{"f", `in:LP,CD`}}, `{}`},
|
|
{"in-case", `{"f":"lp"}`, [][2]string{{"f", `in:LP,CD`}}, `{"f":["The selected f is invalid."]}`},
|
|
{"in-loose-numeric", `{"f":"1.0"}`, [][2]string{{"f", `in:1,2`}}, `{}`},
|
|
{"in-int-value", `{"f":1}`, [][2]string{{"f", `in:1,2`}}, `{}`},
|
|
{"in-bool-value", `{"f":true}`, [][2]string{{"f", `in:1,2`}}, `{}`},
|
|
{"in-array-ok", `{"f":["LP","CD"]}`, [][2]string{{"f", `array|in:LP,CD`}}, `{}`},
|
|
{"in-array-bad", `{"f":["LP","X"]}`, [][2]string{{"f", `array|in:LP,CD`}}, `{"f":["The selected f is invalid."]}`},
|
|
{"in-array-strict-string", `{"f":["1.0"]}`, [][2]string{{"f", `array|in:1,2`}}, `{"f":["The selected f is invalid."]}`},
|
|
{"in-array-ints", `{"f":[1,2]}`, [][2]string{{"f", `array|in:1,2`}}, `{}`},
|
|
{"in-array-nested", `{"f":[["LP"]]}`, [][2]string{{"f", `array|in:LP`}}, `{"f":["The selected f is invalid."]}`},
|
|
{"in-array-without-array-rule", `{"f":["LP"]}`, [][2]string{{"f", `in:LP`}}, `{"f":["The selected f is invalid."]}`},
|
|
{"not-in-ok", `{"x":"a"}`, [][2]string{{"x", `not_in:b,c`}}, `{}`},
|
|
{"not-in-fail", `{"x":"b"}`, [][2]string{{"x", `not_in:b,c`}}, `{"x":["The selected x is invalid."]}`},
|
|
{"not-in-loose", `{"x":"2.0"}`, [][2]string{{"x", `not_in:2`}}, `{"x":["The selected x is invalid."]}`},
|
|
{"not-in-array-some", `{"x":["a","b"]}`, [][2]string{{"x", `array|not_in:b`}}, `{}`},
|
|
{"not-in-array-all", `{"x":["b"]}`, [][2]string{{"x", `array|not_in:b`}}, `{"x":["The selected x is invalid."]}`},
|
|
{"not-in-array-none", `{"x":["a"]}`, [][2]string{{"x", `array|not_in:b`}}, `{}`},
|
|
{"not-in-array-without-array-rule", `{"x":["b"]}`, [][2]string{{"x", `not_in:b`}}, `{}`},
|
|
{"string-int", `{"s":123}`, [][2]string{{"s", `string`}}, `{"s":["The s must be a string."]}`},
|
|
{"string-null", `{"s":null}`, [][2]string{{"s", `string`}}, `{"s":["The s must be a string."]}`},
|
|
{"string-null-nullable", `{"s":null}`, [][2]string{{"s", `nullable|string`}}, `{}`},
|
|
{"size-string", `{"s":"abc"}`, [][2]string{{"s", `size:4`}}, `{"s":["The s must be 4 characters."]}`},
|
|
{"size-string-ok", `{"s":"abc"}`, [][2]string{{"s", `size:3`}}, `{}`},
|
|
{"max-string-chars", `{"s":"ąęś"}`, [][2]string{{"s", `max:2`}}, `{"s":["The s may not be greater than 2 characters."]}`},
|
|
{"min-array", `{"a":[1]}`, [][2]string{{"a", `array|min:2`}}, `{"a":["The a must have at least 2 items."]}`},
|
|
{"max-array", `{"a":[1,2,3]}`, [][2]string{{"a", `array|max:2`}}, `{"a":["The a may not have more than 2 items."]}`},
|
|
{"size-array", `{"a":[1,2]}`, [][2]string{{"a", `array|size:3`}}, `{"a":["The a must contain 3 items."]}`},
|
|
{"between-array", `{"a":[]}`, [][2]string{{"a", `array|between:1,2`}}, `{"a":["The a must have between 1 and 2 items."]}`},
|
|
{"between-numeric", `{"n":"5"}`, [][2]string{{"n", `numeric|between:1,4`}}, `{"n":["The n must be between 1 and 4."]}`},
|
|
{"max-integer", `{"n":5}`, [][2]string{{"n", `integer|max:4`}}, `{"n":["The n may not be greater than 4."]}`},
|
|
{"min-numeric-decimal", `{"n":"0.5"}`, [][2]string{{"n", `numeric|min:1`}}, `{"n":["The n must be at least 1."]}`},
|
|
{"size-numeric", `{"n":"3"}`, [][2]string{{"n", `numeric|size:4`}}, `{"n":["The n must be 4."]}`},
|
|
{"size-numeric-string-without-numeric-rule", `{"n":"12345"}`, [][2]string{{"n", `size:5`}}, `{}`},
|
|
{"min-null", `{"n":null}`, [][2]string{{"n", `min:1`}}, `{"n":["The n must be at least 1 characters."]}`},
|
|
{"between-string", `{"s":"a"}`, [][2]string{{"s", `string|between:2,4`}}, `{"s":["The s must be between 2 and 4 characters."]}`},
|
|
{"max-bool", `{"s":true}`, [][2]string{{"s", `max:0`}}, `{"s":["The s may not be greater than 0 characters."]}`},
|
|
{"regex-ok", `{"d":"3:45"}`, [][2]string{{"d", `regex:/^\d{1,3}:[0-5]\d$/`}}, `{}`},
|
|
{"regex-bad", `{"d":"3:75"}`, [][2]string{{"d", `regex:/^\d{1,3}:[0-5]\d$/`}}, `{"d":["The d format is invalid."]}`},
|
|
{"regex-number", `{"d":345}`, [][2]string{{"d", `regex:/^\d+$/`}}, `{}`},
|
|
{"regex-array", `{"d":["x"]}`, [][2]string{{"d", `regex:/x/`}}, `{"d":["The d format is invalid."]}`},
|
|
{"regex-bool", `{"d":true}`, [][2]string{{"d", `regex:/1/`}}, `{"d":["The d format is invalid."]}`},
|
|
{"not-regex", `{"d":"abc"}`, [][2]string{{"d", `not_regex:/a/`}}, `{"d":["The d format is invalid."]}`},
|
|
{"not-regex-ok", `{"d":"xyz"}`, [][2]string{{"d", `not_regex:/a/`}}, `{}`},
|
|
{"regex-flags", `{"d":"ABC"}`, [][2]string{{"d", `regex:/^abc$/i`}}, `{}`},
|
|
{"regex-hash-delimiter", `{"d":"a/b"}`, [][2]string{{"d", `regex:#^a/b$#`}}, `{}`},
|
|
{"date-feb30", `{"d":"2020-02-30"}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"date-leap", `{"d":"2020-02-29"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-garbage", `{"d":"garbage"}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"date-int", `{"d":12345}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"date-slash", `{"d":"2020/02/29"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-us", `{"d":"02/29/2020"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-dot", `{"d":"29.02.2020"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-dash-dmy", `{"d":"29-02-2020"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-iso-z", `{"d":"2020-02-29T10:00:00Z"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-iso-offset", `{"d":"2020-02-29T10:00:00+02:00"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-bad-hour", `{"d":"2020-02-29 25:00"}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"date-with-time", `{"d":"2020-02-29 10:30"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-with-seconds", `{"d":"2020-02-29 10:30:15"}`, [][2]string{{"d", `date`}}, `{}`},
|
|
{"date-month13", `{"d":"2020-13-01"}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"date-array", `{"d":["2020-01-01"]}`, [][2]string{{"d", `date`}}, `{"d":["The d is not a valid date."]}`},
|
|
{"after-same", `{"d":"2020-01-01"}`, [][2]string{{"d", `after:2020-01-01`}}, `{"d":["The d must be a date after 2020-01-01."]}`},
|
|
{"after-or-equal-same", `{"d":"2020-01-01"}`, [][2]string{{"d", `after_or_equal:2020-01-01`}}, `{}`},
|
|
{"before-ok", `{"d":"2019-12-31"}`, [][2]string{{"d", `before:2020-01-01`}}, `{}`},
|
|
{"before-or-equal-future", `{"d":"2099-01-01"}`, [][2]string{{"d", `before_or_equal:tomorrow`}}, `{"d":["The d must be a date before or equal to tomorrow."]}`},
|
|
{"before-or-equal-past", `{"d":"2001-01-01"}`, [][2]string{{"d", `before_or_equal:tomorrow`}}, `{}`},
|
|
{"after-or-equal-1900", `{"d":"1899-12-31"}`, [][2]string{{"d", `after_or_equal:1900-01-01`}}, `{"d":["The d must be a date after or equal to 1900-01-01."]}`},
|
|
{"after-field-bad", `{"start":"2020-01-01","end":"2019-01-01"}`, [][2]string{{"end", `after:start`}}, `{"end":["The end must be a date after start."]}`},
|
|
{"after-field-ok", `{"start":"2020-01-01","end":"2021-01-01"}`, [][2]string{{"end", `after:start`}}, `{}`},
|
|
{"after-unparsable", `{"d":"garbage"}`, [][2]string{{"d", `after:2020-01-01`}}, `{"d":["The d must be a date after 2020-01-01."]}`},
|
|
{"after-missing-field", `{"d":"2020-01-01"}`, [][2]string{{"d", `after:nofield`}}, `{}`},
|
|
{"before-int", `{"d":5}`, [][2]string{{"d", `before:2020-01-01`}}, `{}`},
|
|
{"after-yesterday", `{"d":"2001-01-01"}`, [][2]string{{"d", `after:yesterday`}}, `{"d":["The d must be a date after yesterday."]}`},
|
|
{"before-today", `{"d":"2001-01-01"}`, [][2]string{{"d", `before:today`}}, `{}`},
|
|
{"after-now", `{"d":"2099-01-01 10:00"}`, [][2]string{{"d", `after:now`}}, `{}`},
|
|
{"url-https", `{"u":"https://example.com/x?y=1"}`, [][2]string{{"u", `url`}}, `{}`},
|
|
{"url-bare", `{"u":"notaurl"}`, [][2]string{{"u", `url`}}, `{"u":["The u format is invalid."]}`},
|
|
{"url-localhost-port", `{"u":"http://localhost:8000"}`, [][2]string{{"u", `url`}}, `{}`},
|
|
{"url-ftp", `{"u":"ftp://a.b"}`, [][2]string{{"u", `url`}}, `{}`},
|
|
{"url-space", `{"u":"https://exa mple.com"}`, [][2]string{{"u", `url`}}, `{"u":["The u format is invalid."]}`},
|
|
{"url-int", `{"u":5}`, [][2]string{{"u", `url`}}, `{"u":["The u format is invalid."]}`},
|
|
{"filled-null", `{"x":null}`, [][2]string{{"x", `filled`}}, `{"x":["The x field must have a value."]}`},
|
|
{"filled-missing", `{}`, [][2]string{{"x", `filled`}}, `{}`},
|
|
{"filled-blank", `{"x":""}`, [][2]string{{"x", `filled`}}, `{"x":["The x field must have a value."]}`},
|
|
{"nullable-blank", `{"x":""}`, [][2]string{{"x", `nullable|string|max:2`}}, `{}`},
|
|
{"sometimes-missing", `{}`, [][2]string{{"x", `sometimes|required`}}, `{}`},
|
|
{"sometimes-present-null", `{"x":null}`, [][2]string{{"x", `sometimes|required`}}, `{"x":["The x field is required."]}`},
|
|
{"integer-blank", `{"x":""}`, [][2]string{{"x", `integer`}}, `{}`},
|
|
{"present-missing", `{}`, [][2]string{{"x", `present`}}, `{"x":["The x field must be present."]}`},
|
|
{"present-null", `{"x":null}`, [][2]string{{"x", `present`}}, `{}`},
|
|
{"required-whitespace", `{"x":" "}`, [][2]string{{"x", `required`}}, `{"x":["The x field is required."]}`},
|
|
{"required-empty-array", `{"x":[]}`, [][2]string{{"x", `required`}}, `{"x":["The x field is required."]}`},
|
|
{"required-zero", `{"x":0}`, [][2]string{{"x", `required`}}, `{}`},
|
|
{"required-false", `{"x":false}`, [][2]string{{"x", `required`}}, `{}`},
|
|
{"nested-wildcard", `{"a":[{"b":[{"c":""}]},{"b":[{"c":"x"},{"c":null}]}]}`, [][2]string{{"a.*.b.*.c", `required`}}, `{"a.0.b.0.c":["The a.0.b.0.c field is required."],"a.1.b.1.c":["The a.1.b.1.c field is required."]}`},
|
|
{"map-wildcard", `{"a":{"k2":{"c":"v"},"k1":{"c":""}}}`, [][2]string{{"a.*.c", `required`}}, `{"a.k1.c":["The a.k1.c field is required."]}`},
|
|
{"wildcard-missing-mid", `{"a":[{"x":1}]}`, [][2]string{{"a.*.b.*.c", `required`}}, `{}`},
|
|
{"wildcard-scalar-parent", `{"a":"str"}`, [][2]string{{"a.*", `required`}}, `{}`},
|
|
{"wildcard-in", `{"f":["LP","X","CD","Y"]}`, [][2]string{{"f", `array`}, {"f.*", `in:LP,CD`}}, `{"f.1":["The selected f.1 is invalid."],"f.3":["The selected f.3 is invalid."]}`},
|
|
{"wildcard-numeric-keys", `{"t":[{"title":"a","duration":"3:75"},{"title":"","duration":"1:00"}]}`, [][2]string{{"t.*.title", `required|string|max:255`}, {"t.*.duration", `nullable|string|regex:/^\d{1,3}:[0-5]\d$/`}}, `{"t.1.title":["The t.1.title field is required."],"t.0.duration":["The t.0.duration format is invalid."]}`},
|
|
{"wildcard-trailing", `{"a":["x",""]}`, [][2]string{{"a.*", `required`}}, `{"a.1":["The a.1 field is required."]}`},
|
|
{"bail", `{"x":"ab"}`, [][2]string{{"x", `bail|integer|min:5`}}, `{"x":["The x must be an integer."]}`},
|
|
{"no-bail", `{"x":"ab"}`, [][2]string{{"x", `integer|min:5`}}, `{"x":["The x must be an integer.","The x must be at least 5."]}`},
|
|
{"implicit-stop", `{}`, [][2]string{{"x", `required|integer|min:5`}}, `{"x":["The x field is required."]}`},
|
|
{"order", `{"b":"","a":""}`, [][2]string{{"b", `required`}, {"a", `required`}}, `{"b":["The b field is required."],"a":["The a field is required."]}`},
|
|
}
|
|
|
|
func TestValidateRulesMatchLaravel(t *testing.T) {
|
|
for _, tc := range laravelRuleCases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
dec := json.NewDecoder(strings.NewReader(tc.data))
|
|
dec.UseNumber()
|
|
var input map[string]any
|
|
if err := dec.Decode(&input); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var rules []RequestRule
|
|
for _, r := range tc.rules {
|
|
rules = append(rules, RequestRule{Field: r[0], Rules: ParseRules(r[1])})
|
|
}
|
|
got := mustValidate(t, inLocale("en"), input, rules)
|
|
var want map[string][]string
|
|
if err := json.Unmarshal([]byte(tc.want), &want); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(want) == 0 && len(got) == 0 {
|
|
return
|
|
}
|
|
if !reflect.DeepEqual(got, want) {
|
|
t.Fatalf("got %v\nwant %v (PHP)", got, want)
|
|
}
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestPHPFloatStringMatchesPHPCast pins phpFloatString to PHP 8's (string)
|
|
// cast of a float, which formats with the precision ini (14 significant
|
|
// digits, %.14G): the wants are php -r '(string)$x' output.
|
|
func TestPHPFloatStringMatchesPHPCast(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
in float64
|
|
want string
|
|
}{
|
|
{0.1 + 0.2, "0.3"},
|
|
{1e14, "1.0E+14"},
|
|
{1e15, "1.0E+15"},
|
|
{1e20, "1.0E+20"},
|
|
{123456789012345.0, "1.2345678901234E+14"},
|
|
{12345678901234.0, "12345678901234"},
|
|
{1.5e-5, "1.5E-5"},
|
|
{0.0001, "0.0001"},
|
|
{0.00012345678901234567, "0.00012345678901235"},
|
|
{-2.5, "-2.5"},
|
|
{3.0, "3"},
|
|
{1.0 / 3, "0.33333333333333"},
|
|
{2.0 / 3, "0.66666666666667"},
|
|
{-1e-10, "-1.0E-10"},
|
|
{99999999999999.9, "1.0E+14"},
|
|
{0.1, "0.1"},
|
|
{999999999999999.0, "1.0E+15"},
|
|
{5e-324, "4.9406564584125E-324"},
|
|
{1.7976931348623157e308, "1.7976931348623E+308"},
|
|
{100.0, "100"},
|
|
{1234.5678, "1234.5678"},
|
|
{-0.000099999, "-9.9999E-5"},
|
|
{0, "0"},
|
|
{math.Copysign(0, -1), "-0"},
|
|
{math.Inf(1), "INF"},
|
|
{math.Inf(-1), "-INF"},
|
|
{math.NaN(), "NAN"},
|
|
} {
|
|
if got := phpFloatString(tc.in); got != tc.want {
|
|
t.Errorf("phpFloatString(%v) = %q, want %q", tc.in, got, tc.want)
|
|
}
|
|
}
|
|
}
|