1// Copyright 2015 Google LLC 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// http://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15package bigquery 16 17import ( 18 "encoding/json" 19 "math" 20 "reflect" 21 "testing" 22 23 "cloud.google.com/go/civil" 24 "cloud.google.com/go/internal/testutil" 25) 26 27var ( 28 nullsTestTime = civil.Time{Hour: 7, Minute: 50, Second: 22, Nanosecond: 1000} 29 nullsTestDateTime = civil.DateTime{Date: civil.Date{Year: 2016, Month: 11, Day: 5}, Time: nullsTestTime} 30) 31 32func TestNullsJSON(t *testing.T) { 33 for _, test := range []struct { 34 in interface{} 35 want string 36 }{ 37 {&NullInt64{Valid: true, Int64: 3}, `3`}, 38 {&NullFloat64{Valid: true, Float64: 3.14}, `3.14`}, 39 {&NullBool{Valid: true, Bool: true}, `true`}, 40 {&NullString{Valid: true, StringVal: "foo"}, `"foo"`}, 41 {&NullGeography{Valid: true, GeographyVal: "ST_GEOPOINT(47.649154, -122.350220)"}, `"ST_GEOPOINT(47.649154, -122.350220)"`}, 42 {&NullTimestamp{Valid: true, Timestamp: testTimestamp}, `"2016-11-05T07:50:22.000000008Z"`}, 43 {&NullDate{Valid: true, Date: testDate}, `"2016-11-05"`}, 44 {&NullTime{Valid: true, Time: nullsTestTime}, `"07:50:22.000001"`}, 45 {&NullDateTime{Valid: true, DateTime: nullsTestDateTime}, `"2016-11-05 07:50:22.000001"`}, 46 47 {&NullInt64{}, `null`}, 48 {&NullFloat64{}, `null`}, 49 {&NullBool{}, `null`}, 50 {&NullString{}, `null`}, 51 {&NullGeography{}, `null`}, 52 {&NullTimestamp{}, `null`}, 53 {&NullDate{}, `null`}, 54 {&NullTime{}, `null`}, 55 {&NullDateTime{}, `null`}, 56 57 {&NullFloat64{Valid: true, Float64: math.Inf(1)}, `"Infinity"`}, 58 {&NullFloat64{Valid: true, Float64: math.Inf(-1)}, `"-Infinity"`}, 59 {&NullFloat64{Valid: true, Float64: math.NaN()}, `"NaN"`}, 60 } { 61 bytes, err := json.Marshal(test.in) 62 if err != nil { 63 t.Fatal(err) 64 } 65 if got, want := string(bytes), test.want; got != want { 66 t.Errorf("%#v: got %s, want %s", test.in, got, want) 67 } 68 69 typ := reflect.Indirect(reflect.ValueOf(test.in)).Type() 70 value := reflect.New(typ).Interface() 71 err = json.Unmarshal(bytes, value) 72 if err != nil { 73 t.Fatal(err) 74 } 75 76 if !testutil.Equal(value, test.in) { 77 t.Errorf("%#v: got %#v, want %#v", test.in, value, test.in) 78 } 79 } 80} 81 82func TestNullFloat64JSON(t *testing.T) { 83 for _, tc := range []struct { 84 name string 85 in string 86 unmarshalled NullFloat64 87 marshalled string 88 }{ 89 { 90 name: "float value", 91 in: "3.14", 92 unmarshalled: NullFloat64{Valid: true, Float64: 3.14}, 93 marshalled: "3.14", 94 }, 95 { 96 name: "null", 97 in: "null", 98 unmarshalled: NullFloat64{}, 99 marshalled: "null", 100 }, 101 { 102 name: "long infinity", 103 in: `"Infinity"`, 104 unmarshalled: NullFloat64{Valid: true, Float64: math.Inf(1)}, 105 marshalled: `"Infinity"`, 106 }, 107 { 108 name: "short infinity", 109 in: `"Inf"`, 110 unmarshalled: NullFloat64{Valid: true, Float64: math.Inf(1)}, 111 marshalled: `"Infinity"`, 112 }, 113 { 114 name: "positive short infinity", 115 in: `"+Inf"`, 116 unmarshalled: NullFloat64{Valid: true, Float64: math.Inf(1)}, 117 marshalled: `"Infinity"`, 118 }, 119 { 120 name: "minus infinity", 121 in: `"-Infinity"`, 122 unmarshalled: NullFloat64{Valid: true, Float64: math.Inf(-1)}, 123 marshalled: `"-Infinity"`, 124 }, 125 { 126 name: "minus short infinity", 127 in: `"-Inf"`, 128 unmarshalled: NullFloat64{Valid: true, Float64: math.Inf(-1)}, 129 marshalled: `"-Infinity"`, 130 }, 131 { 132 name: "NaN", 133 in: `"NaN"`, 134 unmarshalled: NullFloat64{Valid: true, Float64: math.NaN()}, 135 marshalled: `"NaN"`, 136 }, 137 } { 138 tc := tc 139 t.Run(tc.name, func(t *testing.T) { 140 t.Parallel() 141 142 var f NullFloat64 143 err := json.Unmarshal([]byte(tc.in), &f) 144 if err != nil { 145 t.Fatal(err) 146 } 147 if got, want := f, tc.unmarshalled; !testutil.Equal(got, want) { 148 t.Errorf("%#v: got %#v, want %#v", tc.in, got, want) 149 } 150 151 b, err := json.Marshal(f) 152 if err != nil { 153 t.Fatal(err) 154 } 155 if got, want := string(b), tc.marshalled; got != want { 156 t.Errorf("%#v: got %s, want %s", tc.in, got, want) 157 } 158 }) 159 } 160} 161