Files
summercms/modules/lagoon/validate_rules_test.go
Jakub Zych 3ac1d64ab4 fix(12-05): cast floats to strings with PHP's 14-digit precision in request validation
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.
2026-10-02 15:46:24 +02:00

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)
}
}
}