github.com/authzed/spicedb@v1.32.1-0.20240520085336-ebda56537386/pkg/caveats/types/types_test.go (about) 1 package types 2 3 import ( 4 "testing" 5 "time" 6 7 "google.golang.org/protobuf/types/known/structpb" 8 9 "github.com/stretchr/testify/require" 10 ) 11 12 func TestConversion(t *testing.T) { 13 tcs := []struct { 14 name string 15 vtype VariableType 16 inputValue any 17 expectedValue any 18 expectedErr string 19 }{ 20 { 21 name: "bool to bool", 22 vtype: BooleanType, 23 inputValue: true, 24 expectedValue: true, 25 expectedErr: "", 26 }, 27 { 28 name: "string to bool", 29 vtype: BooleanType, 30 inputValue: "hiya", 31 expectedValue: nil, 32 expectedErr: "for bool: a bool value is required, but found string `hiya`", 33 }, 34 { 35 name: "number to string", 36 vtype: StringType, 37 inputValue: 42, 38 expectedValue: nil, 39 expectedErr: "for string: a string value is required, but found int `42`", 40 }, 41 { 42 name: "float to float", 43 vtype: DoubleType, 44 inputValue: 42.0, 45 expectedValue: 42.0, 46 expectedErr: "", 47 }, 48 { 49 name: "string to float", 50 vtype: DoubleType, 51 inputValue: "42.0", 52 expectedValue: 42.0, 53 expectedErr: "", 54 }, 55 { 56 name: "float to uint", 57 vtype: UIntType, 58 inputValue: 42.0, 59 expectedValue: uint64(42), 60 expectedErr: "", 61 }, 62 { 63 name: "invalid float to uint", 64 vtype: UIntType, 65 inputValue: 42.1, 66 expectedValue: uint64(42), 67 expectedErr: "for uint: a uint value is required, but found numeric value `42.1`", 68 }, 69 { 70 name: "negative float to uint", 71 vtype: UIntType, 72 inputValue: -42.0, 73 expectedValue: nil, 74 expectedErr: "for uint: a uint value is required, but found int64 value `-42`", 75 }, 76 { 77 name: "float to int", 78 vtype: IntType, 79 inputValue: 42.0, 80 expectedValue: int64(42), 81 expectedErr: "", 82 }, 83 { 84 name: "string to int", 85 vtype: IntType, 86 inputValue: "42", 87 expectedValue: int64(42), 88 expectedErr: "", 89 }, 90 { 91 name: "string to uint", 92 vtype: UIntType, 93 inputValue: "42", 94 expectedValue: uint64(42), 95 expectedErr: "", 96 }, 97 { 98 name: "invalid float to int", 99 vtype: IntType, 100 inputValue: 42.1, 101 expectedValue: int64(42), 102 expectedErr: "for int: a int value is required, but found numeric value `42.1`", 103 }, 104 { 105 name: "negative float to int", 106 vtype: IntType, 107 inputValue: -42.0, 108 expectedValue: int64(-42), 109 expectedErr: "", 110 }, 111 { 112 name: "valid list<uint>", 113 vtype: MustListType(UIntType), 114 inputValue: []any{uint64(1)}, 115 expectedValue: []any{uint64(1)}, 116 expectedErr: "", 117 }, 118 { 119 name: "valid list<uint> with conversion", 120 vtype: MustListType(UIntType), 121 inputValue: []any{42.0}, 122 expectedValue: []any{uint64(42)}, 123 expectedErr: "", 124 }, 125 { 126 name: "valid list<uint> with string conversion", 127 vtype: MustListType(UIntType), 128 inputValue: []any{"42"}, 129 expectedValue: []any{uint64(42)}, 130 expectedErr: "", 131 }, 132 { 133 name: "valid list<uint> with string conversion of max int", 134 vtype: MustListType(UIntType), 135 inputValue: []any{"9223372036854775807"}, 136 expectedValue: []any{uint64(9223372036854775807)}, 137 expectedErr: "", 138 }, 139 { 140 name: "map with invalid value type", 141 vtype: MustMapType(UIntType), 142 inputValue: map[string]any{"foo": "a1"}, 143 expectedValue: nil, 144 expectedErr: "for map<uint>: found an invalid value for key `foo`: for uint: a uint64 value is required, but found invalid string value `a1`", 145 }, 146 { 147 name: "map with invalid value type in different key", 148 vtype: MustMapType(UIntType), 149 inputValue: map[string]any{"foo": "1", "bar": "a1"}, 150 expectedValue: nil, 151 expectedErr: "for map<uint>: found an invalid value for key `bar`: for uint: a uint64 value is required, but found invalid string value `a1`", 152 }, 153 { 154 name: "map with valid converted value type", 155 vtype: MustMapType(UIntType), 156 inputValue: map[string]any{"foo": "1"}, 157 expectedValue: map[string]any{"foo": uint64(1)}, 158 expectedErr: "", 159 }, 160 { 161 name: "map with invalid nested value in list", 162 vtype: MustMapType(MustListType(UIntType)), 163 inputValue: map[string]any{"foo": []any{"-1"}}, 164 expectedValue: nil, 165 expectedErr: "for map<list<uint>>: found an invalid value for key `foo`: for list<uint>: found an invalid value for item at index 0: for uint: a uint value is required, but found int64 value `-1`", 166 }, 167 { 168 name: "map with valid nested value in list", 169 vtype: MustMapType(MustListType(UIntType)), 170 inputValue: map[string]any{"foo": []any{"1"}}, 171 expectedValue: map[string]any{"foo": []any{uint64(1)}}, 172 expectedErr: "", 173 }, 174 { 175 name: "map with invalid nested map value", 176 vtype: MustMapType(MustMapType(StringType)), 177 inputValue: map[string]any{"foo": map[string]any{"bar": 42.0}}, 178 expectedValue: nil, 179 expectedErr: "for map<map<string>>: found an invalid value for key `foo`: for map<string>: found an invalid value for key `bar`: for string: a string value is required, but found float64 `42`", 180 }, 181 { 182 name: "map with valid nested map value", 183 vtype: MustMapType(MustMapType(StringType)), 184 inputValue: map[string]any{"foo": map[string]any{"bar": "hiya"}}, 185 expectedValue: map[string]any{"foo": map[string]any{"bar": "hiya"}}, 186 expectedErr: "", 187 }, 188 { 189 name: "valid bytes", 190 vtype: BytesType, 191 inputValue: func() string { 192 v, _ := structpb.NewValue([]byte{1, 2, 3, 42}) 193 return v.GetStringValue() 194 }(), 195 expectedValue: []byte{1, 2, 3, 42}, 196 expectedErr: "", 197 }, 198 { 199 name: "invalid type for bytes", 200 vtype: BytesType, 201 inputValue: 42.0, 202 expectedValue: nil, 203 expectedErr: "for bytes: bytes requires a base64 unicode string, found: float64 `42`", 204 }, 205 { 206 name: "invalid bytes", 207 vtype: BytesType, 208 inputValue: "testing123", 209 expectedValue: nil, 210 expectedErr: "for bytes: bytes requires a base64 encoded string: illegal base64 data at input byte 8", 211 }, 212 { 213 name: "valid duration", 214 vtype: DurationType, 215 inputValue: "10h4m", 216 expectedValue: 604 * time.Minute, 217 expectedErr: "", 218 }, 219 { 220 name: "invalid duration", 221 vtype: DurationType, 222 inputValue: "10hm", 223 expectedValue: nil, 224 expectedErr: "for duration: could not parse duration string `10hm`: time: unknown unit \"hm\" in duration \"10hm\"", 225 }, 226 { 227 name: "valid timestamp", 228 vtype: TimestampType, 229 inputValue: "2023-01-22T01:13:00Z", 230 expectedValue: time.Date(2023, time.January, 22, 1, 13, 0, 0, time.UTC), 231 expectedErr: "", 232 }, 233 { 234 name: "invalid timestamp", 235 vtype: TimestampType, 236 inputValue: "2023-0122", 237 expectedValue: nil, 238 expectedErr: "for timestamp: could not parse RFC 3339 formatted timestamp string `2023-0122`: parsing time \"2023-0122\" as \"2006-01-02T15:04:05Z07:00\": cannot parse \"22\" as \"-\"", 239 }, 240 { 241 name: "invalid timestamp value", 242 vtype: TimestampType, 243 inputValue: 42.0, 244 expectedValue: nil, 245 expectedErr: "for timestamp: timestamps requires a RFC 3339 formatted timestamp string, found: float64 `42`", 246 }, 247 { 248 name: "valid ipaddress", 249 vtype: IPAddressType, 250 inputValue: "1.2.3.4", 251 expectedValue: MustParseIPAddress("1.2.3.4"), 252 expectedErr: "", 253 }, 254 { 255 name: "invalid ipaddress", 256 vtype: IPAddressType, 257 inputValue: "1...23.4", 258 expectedValue: nil, 259 expectedErr: "for ipaddress: could not parse ip address string `1...23.4`: ParseAddr(\"1...23.4\"): IPv4 field must have at least one digit (at \"..23.4\")", 260 }, 261 { 262 name: "invalid ipaddress type", 263 vtype: IPAddressType, 264 inputValue: 42.0, 265 expectedValue: nil, 266 expectedErr: "for ipaddress: ipaddress requires an ipaddress string, found: float64 `42`", 267 }, 268 { 269 name: "invalid ipaddress in list", 270 vtype: MustListType(IPAddressType), 271 inputValue: []any{"1...23.4"}, 272 expectedValue: nil, 273 expectedErr: "for list<ipaddress>: found an invalid value for item at index 0: for ipaddress: could not parse ip address string `1...23.4`: ParseAddr(\"1...23.4\"): IPv4 field must have at least one digit (at \"..23.4\")", 274 }, 275 { 276 name: "ipaddress in list", 277 vtype: MustListType(IPAddressType), 278 inputValue: []any{"1.2.3.4", "4.5.6.7"}, 279 expectedValue: []any{MustParseIPAddress("1.2.3.4"), MustParseIPAddress("4.5.6.7")}, 280 expectedErr: "", 281 }, 282 } 283 284 for _, tc := range tcs { 285 tc := tc 286 t.Run(tc.name, func(t *testing.T) { 287 result, err := tc.vtype.ConvertValue(tc.inputValue) 288 if err != nil { 289 require.Equal(t, tc.expectedErr, err.Error()) 290 } else { 291 require.NoError(t, err) 292 require.Equal(t, tc.expectedValue, result) 293 } 294 }) 295 } 296 }