github.com/nspcc-dev/neo-go@v0.105.2-0.20240517133400-6be757af3eba/pkg/smartcontract/manifest/manifest_test.go (about) 1 package manifest 2 3 import ( 4 "encoding/json" 5 "fmt" 6 "math/big" 7 "testing" 8 9 "github.com/nspcc-dev/neo-go/internal/random" 10 "github.com/nspcc-dev/neo-go/pkg/crypto/keys" 11 "github.com/nspcc-dev/neo-go/pkg/smartcontract" 12 "github.com/nspcc-dev/neo-go/pkg/util" 13 "github.com/nspcc-dev/neo-go/pkg/vm/stackitem" 14 "github.com/stretchr/testify/require" 15 ) 16 17 // Test vectors are taken from the main NEO repo 18 // https://github.com/neo-project/neo/blob/master/tests/neo.UnitTests/SmartContract/Manifest/UT_ContractManifest.cs#L10 19 func TestManifest_MarshalJSON(t *testing.T) { 20 t.Run("default", func(t *testing.T) { 21 s := `{"groups":[],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` 22 m := testUnmarshalMarshalManifest(t, s) 23 require.Equal(t, DefaultManifest("Test"), m) 24 }) 25 26 t.Run("permissions", func(t *testing.T) { 27 s := `{"groups":[],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"0x0000000000000000000000000000000000000000","methods":["method1","method2"]}],"trusts":[],"extra":null}` 28 testUnmarshalMarshalManifest(t, s) 29 }) 30 31 t.Run("safe methods", func(t *testing.T) { 32 s := `{"groups":[],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[{"name":"safeMet","offset":123,"parameters":[],"returntype":"Integer","safe":true}],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` 33 testUnmarshalMarshalManifest(t, s) 34 }) 35 36 t.Run("trust", func(t *testing.T) { 37 s := `{"groups":[],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":["0x0000000000000000000000000000000000000001"],"extra":null}` 38 testUnmarshalMarshalManifest(t, s) 39 }) 40 41 t.Run("groups", func(t *testing.T) { 42 s := `{"groups":[{"pubkey":"03b209fd4f53a7170ea4444e0cb0a6bb6a53c2bd016926989cf85f9b0fba17a70c","signature":"QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQQ=="}],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":null}` 43 testUnmarshalMarshalManifest(t, s) 44 }) 45 46 t.Run("extra", func(t *testing.T) { 47 s := `{"groups":[],"features":{},"supportedstandards":[],"name":"Test","abi":{"methods":[],"events":[]},"permissions":[{"contract":"*","methods":"*"}],"trusts":[],"extra":{"key":"value"}}` 48 testUnmarshalMarshalManifest(t, s) 49 }) 50 } 51 52 func testUnmarshalMarshalManifest(t *testing.T, s string) *Manifest { 53 js := []byte(s) 54 c := NewManifest("Test") 55 require.NoError(t, json.Unmarshal(js, c)) 56 57 data, err := json.Marshal(c) 58 require.NoError(t, err) 59 require.JSONEq(t, s, string(data)) 60 61 return c 62 } 63 64 func TestManifest_CanCall(t *testing.T) { 65 man1 := DefaultManifest("Test1") 66 man2 := DefaultManifest("Test2") 67 require.True(t, man1.CanCall(util.Uint160{}, man2, "method1")) 68 } 69 70 func TestPermission_IsAllowed(t *testing.T) { 71 manifest := DefaultManifest("Test") 72 73 t.Run("wildcard", func(t *testing.T) { 74 h := random.Uint160() 75 76 perm := NewPermission(PermissionWildcard) 77 require.True(t, perm.IsAllowed(h, manifest, "AAA")) 78 79 perm.Methods.Restrict() 80 require.False(t, perm.IsAllowed(h, manifest, "AAA")) 81 82 perm.Methods.Add("AAA") 83 require.True(t, perm.IsAllowed(h, manifest, "AAA")) 84 }) 85 86 t.Run("hash", func(t *testing.T) { 87 perm := NewPermission(PermissionHash, util.Uint160{}) 88 require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 89 90 t.Run("restrict methods", func(t *testing.T) { 91 perm.Methods.Restrict() 92 require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 93 perm.Methods.Add("AAA") 94 require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 95 }) 96 }) 97 98 t.Run("invalid hash", func(t *testing.T) { 99 perm := NewPermission(PermissionHash, util.Uint160{1}) 100 require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 101 }) 102 103 priv, err := keys.NewPrivateKey() 104 require.NoError(t, err) 105 manifest.Groups = []Group{{PublicKey: priv.PublicKey()}} 106 107 t.Run("group", func(t *testing.T) { 108 perm := NewPermission(PermissionGroup, priv.PublicKey()) 109 require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 110 111 priv2, err := keys.NewPrivateKey() 112 require.NoError(t, err) 113 114 perm = NewPermission(PermissionGroup, priv2.PublicKey()) 115 require.False(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 116 117 manifest.Groups = append(manifest.Groups, Group{PublicKey: priv2.PublicKey()}) 118 perm = NewPermission(PermissionGroup, priv2.PublicKey()) 119 require.True(t, perm.IsAllowed(util.Uint160{}, manifest, "AAA")) 120 }) 121 } 122 123 func TestIsValid(t *testing.T) { 124 contractHash := util.Uint160{1, 2, 3} 125 m := &Manifest{} 126 127 t.Run("invalid, no name", func(t *testing.T) { 128 require.Error(t, m.IsValid(contractHash, true)) 129 }) 130 131 m = NewManifest("Test") 132 133 t.Run("invalid, no ABI methods", func(t *testing.T) { 134 require.Error(t, m.IsValid(contractHash, true)) 135 }) 136 137 m.ABI.Methods = append(m.ABI.Methods, Method{ 138 Name: "dummy", 139 ReturnType: smartcontract.VoidType, 140 Parameters: []Parameter{}, 141 }) 142 143 t.Run("valid, no groups/events", func(t *testing.T) { 144 require.NoError(t, m.IsValid(contractHash, true)) 145 }) 146 147 m.ABI.Events = append(m.ABI.Events, Event{ 148 Name: "itHappened", 149 Parameters: []Parameter{}, 150 }) 151 152 t.Run("valid, with events", func(t *testing.T) { 153 require.NoError(t, m.IsValid(contractHash, true)) 154 }) 155 156 m.ABI.Events = append(m.ABI.Events, Event{ 157 Name: "itHappened", 158 Parameters: []Parameter{ 159 NewParameter("qwerty", smartcontract.IntegerType), 160 NewParameter("qwerty", smartcontract.IntegerType), 161 }, 162 }) 163 164 t.Run("invalid, bad event", func(t *testing.T) { 165 require.Error(t, m.IsValid(contractHash, true)) 166 }) 167 m.ABI.Events = m.ABI.Events[:1] 168 169 m.Permissions = append(m.Permissions, *NewPermission(PermissionHash, util.Uint160{1, 2, 3})) 170 t.Run("valid, with permissions", func(t *testing.T) { 171 require.NoError(t, m.IsValid(contractHash, true)) 172 }) 173 174 m.Permissions = append(m.Permissions, *NewPermission(PermissionHash, util.Uint160{1, 2, 3})) 175 t.Run("invalid, with permissions", func(t *testing.T) { 176 require.Error(t, m.IsValid(contractHash, true)) 177 }) 178 m.Permissions = m.Permissions[:1] 179 180 m.SupportedStandards = append(m.SupportedStandards, "NEP-17") 181 t.Run("valid, with standards", func(t *testing.T) { 182 require.NoError(t, m.IsValid(contractHash, true)) 183 }) 184 185 m.SupportedStandards = append(m.SupportedStandards, "") 186 t.Run("invalid, with nameless standard", func(t *testing.T) { 187 require.Error(t, m.IsValid(contractHash, true)) 188 }) 189 m.SupportedStandards = m.SupportedStandards[:1] 190 191 m.SupportedStandards = append(m.SupportedStandards, "NEP-17") 192 t.Run("invalid, with duplicate standards", func(t *testing.T) { 193 require.Error(t, m.IsValid(contractHash, true)) 194 }) 195 m.SupportedStandards = m.SupportedStandards[:1] 196 197 d := PermissionDesc{Type: PermissionHash, Value: util.Uint160{1, 2, 3}} 198 m.Trusts.Add(d) 199 t.Run("valid, with trust", func(t *testing.T) { 200 require.NoError(t, m.IsValid(contractHash, true)) 201 }) 202 203 m.Trusts.Add(PermissionDesc{Type: PermissionHash, Value: util.Uint160{3, 2, 1}}) 204 t.Run("valid, with trusts", func(t *testing.T) { 205 require.NoError(t, m.IsValid(contractHash, true)) 206 }) 207 208 m.Trusts.Add(d) 209 t.Run("invalid, with trusts", func(t *testing.T) { 210 require.Error(t, m.IsValid(contractHash, true)) 211 }) 212 m.Trusts.Restrict() 213 214 t.Run("with groups", func(t *testing.T) { 215 m.Groups = make([]Group, 3) 216 pks := make([]*keys.PrivateKey, 3) 217 for i := range pks { 218 pk, err := keys.NewPrivateKey() 219 require.NoError(t, err) 220 pks[i] = pk 221 m.Groups[i] = Group{ 222 PublicKey: pk.PublicKey(), 223 Signature: pk.Sign(contractHash.BytesBE()), 224 } 225 } 226 227 t.Run("valid", func(t *testing.T) { 228 require.NoError(t, m.IsValid(contractHash, true)) 229 }) 230 231 t.Run("invalid, wrong contract hash", func(t *testing.T) { 232 require.Error(t, m.IsValid(util.Uint160{4, 5, 6}, true)) 233 }) 234 235 t.Run("invalid, wrong group signature", func(t *testing.T) { 236 pk, err := keys.NewPrivateKey() 237 require.NoError(t, err) 238 m.Groups = append(m.Groups, Group{ 239 PublicKey: pk.PublicKey(), 240 // actually, there shouldn't be such situation, as Signature is always the signature 241 // of the contract hash. 242 Signature: pk.Sign([]byte{1, 2, 3}), 243 }) 244 require.Error(t, m.IsValid(contractHash, true)) 245 }) 246 }) 247 m.Groups = m.Groups[:0] 248 249 t.Run("invalid, unserializable", func(t *testing.T) { 250 for i := 0; i < stackitem.MaxSerialized; i++ { 251 m.ABI.Events = append(m.ABI.Events, Event{ 252 Name: fmt.Sprintf("Event%d", i), 253 Parameters: []Parameter{}, 254 }) 255 } 256 err := m.IsValid(contractHash, true) 257 require.ErrorIs(t, err, stackitem.ErrTooBig) 258 }) 259 } 260 261 func TestManifestToStackItem(t *testing.T) { 262 check := func(t *testing.T, expected *Manifest) { 263 item, err := expected.ToStackItem() 264 require.NoError(t, err) 265 actual := new(Manifest) 266 require.NoError(t, actual.FromStackItem(item)) 267 require.Equal(t, expected, actual) 268 } 269 270 t.Run("default", func(t *testing.T) { 271 expected := DefaultManifest("manifest") 272 check(t, expected) 273 }) 274 275 t.Run("full", func(t *testing.T) { 276 pk, _ := keys.NewPrivateKey() 277 expected := &Manifest{ 278 Name: "manifest", 279 ABI: ABI{ 280 Methods: []Method{{ 281 Name: "method", 282 Offset: 15, 283 Parameters: []Parameter{{ 284 Name: "param", 285 Type: smartcontract.StringType, 286 }}, 287 ReturnType: smartcontract.BoolType, 288 Safe: true, 289 }}, 290 Events: []Event{{ 291 Name: "event", 292 Parameters: []Parameter{{ 293 Name: "param", 294 Type: smartcontract.BoolType, 295 }}, 296 }}, 297 }, 298 Features: json.RawMessage("{}"), 299 Groups: []Group{{ 300 PublicKey: pk.PublicKey(), 301 Signature: make([]byte, keys.SignatureLen), 302 }}, 303 Permissions: []Permission{*NewPermission(PermissionWildcard)}, 304 SupportedStandards: []string{"NEP-17"}, 305 Trusts: WildPermissionDescs{ 306 Value: []PermissionDesc{ 307 { 308 Type: PermissionHash, 309 Value: util.Uint160{1, 2, 3}, 310 }, 311 { 312 Type: PermissionGroup, 313 Value: pk.PublicKey(), 314 }, 315 }, 316 }, 317 Extra: []byte(`"even string allowed"`), 318 } 319 check(t, expected) 320 }) 321 322 t.Run("compat", func(t *testing.T) { 323 // Compatibility test with NeoC#, see https://github.com/neo-project/neo/pull/2948. 324 var mJSON = `{"name":"CallOracleContract-6","groups":[],"features":{},"supportedstandards":[],"abi":{"methods":[{"name":"request","parameters":[{"name":"url","type":"String"},{"name":"filter","type":"String"},{"name":"gasForResponse","type":"Integer"}],"returntype":"Void","offset":0,"safe":false},{"name":"callback","parameters":[{"name":"url","type":"String"},{"name":"userData","type":"Any"},{"name":"responseCode","type":"Integer"},{"name":"response","type":"ByteArray"}],"returntype":"Void","offset":86,"safe":false},{"name":"getStoredUrl","parameters":[],"returntype":"String","offset":129,"safe":false},{"name":"getStoredResponseCode","parameters":[],"returntype":"Integer","offset":142,"safe":false},{"name":"getStoredResponse","parameters":[],"returntype":"ByteArray","offset":165,"safe":false}],"events":[]},"permissions":[{"contract":"0xfe924b7cfe89ddd271abaf7210a80a7e11178758","methods":"*"},{"contract":"*","methods":"*"}],"trusts":["0xfe924b7cfe89ddd271abaf7210a80a7e11178758","*"],"extra":{}}` 325 c := NewManifest("Test") 326 require.NoError(t, json.Unmarshal([]byte(mJSON), c)) 327 328 si, err := c.ToStackItem() 329 require.NoError(t, err) 330 actual := new(Manifest) 331 require.NoError(t, actual.FromStackItem(si)) 332 require.NotEqual(t, actual.Permissions[0].Contract.Type, PermissionWildcard) 333 require.True(t, actual.Permissions[0].Methods.IsWildcard()) 334 require.Equal(t, actual.Permissions[1].Contract.Type, PermissionWildcard) 335 require.True(t, actual.Permissions[1].Methods.IsWildcard()) 336 337 require.NotEqual(t, actual.Trusts.Value[0].Type, PermissionWildcard) 338 require.Equal(t, actual.Trusts.Value[1].Type, PermissionWildcard) 339 }) 340 } 341 342 func TestManifest_FromStackItemErrors(t *testing.T) { 343 name := stackitem.NewByteArray([]byte{}) 344 groups := stackitem.NewArray([]stackitem.Item{}) 345 features := stackitem.NewMap() 346 sStandards := stackitem.NewArray([]stackitem.Item{}) 347 abi := stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{})}) 348 permissions := stackitem.NewArray([]stackitem.Item{}) 349 trusts := stackitem.NewArray([]stackitem.Item{}) 350 extra := stackitem.NewByteArray([]byte{}) 351 352 // check OK 353 goodSI := []stackitem.Item{name, groups, features, sStandards, abi, permissions, trusts, extra} 354 m := new(Manifest) 355 require.NoError(t, m.FromStackItem(stackitem.NewStruct(goodSI))) 356 357 errCases := map[string]stackitem.Item{ 358 "not a struct": stackitem.NewArray([]stackitem.Item{}), 359 "invalid length": stackitem.NewStruct([]stackitem.Item{}), 360 "invalid name type": stackitem.NewStruct(append([]stackitem.Item{stackitem.NewInterop(nil)}, goodSI[1:]...)), 361 "invalid Groups type": stackitem.NewStruct(append([]stackitem.Item{name, stackitem.Null{}}, goodSI[2:]...)), 362 "invalid group": stackitem.NewStruct(append([]stackitem.Item{name, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[2:]...)), 363 "invalid Features type": stackitem.NewStruct(append([]stackitem.Item{name, groups, stackitem.Null{}}, goodSI[3:]...)), 364 "invalid supported standards type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.Null{}}, goodSI[4:]...)), 365 "invalid supported standard": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[4:]...)), 366 "invalid ABI": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, stackitem.Null{}}, goodSI[5:]...)), 367 "invalid Permissions type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.Null{}}, goodSI[6:]...)), 368 "invalid permission": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}, goodSI[6:]...)), 369 "invalid Trusts type": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewInterop(nil)}, goodSI[7:]...)), 370 "invalid trust": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewInterop(nil)})}, goodSI[7:]...)), 371 "invalid Uint160 trust": stackitem.NewStruct(append([]stackitem.Item{name, groups, features, sStandards, abi, permissions, stackitem.NewArray([]stackitem.Item{stackitem.NewByteArray([]byte{1, 2, 3})})}, goodSI[7:]...)), 372 "invalid extra type": stackitem.NewStruct([]stackitem.Item{name, groups, features, sStandards, abi, permissions, trusts, stackitem.Null{}}), 373 } 374 for name, errCase := range errCases { 375 t.Run(name, func(t *testing.T) { 376 p := new(Manifest) 377 require.Error(t, p.FromStackItem(errCase)) 378 }) 379 } 380 } 381 382 func TestABI_ToStackItemFromStackItem(t *testing.T) { 383 a := &ABI{ 384 Methods: []Method{{ 385 Name: "mur", 386 Offset: 5, 387 Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}}, 388 ReturnType: smartcontract.StringType, 389 Safe: true, 390 }}, 391 Events: []Event{{ 392 Name: "mur", 393 Parameters: []Parameter{{Name: "p1", Type: smartcontract.BoolType}}, 394 }}, 395 } 396 expected := stackitem.NewStruct([]stackitem.Item{ 397 stackitem.NewArray([]stackitem.Item{ 398 stackitem.NewStruct([]stackitem.Item{ 399 stackitem.NewByteArray([]byte("mur")), 400 stackitem.NewArray([]stackitem.Item{ 401 stackitem.NewStruct([]stackitem.Item{ 402 stackitem.NewByteArray([]byte("p1")), 403 stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))), 404 }), 405 }), 406 stackitem.NewBigInteger(big.NewInt(int64(smartcontract.StringType))), 407 stackitem.NewBigInteger(big.NewInt(int64(5))), 408 stackitem.NewBool(true), 409 }), 410 }), 411 stackitem.NewArray([]stackitem.Item{ 412 stackitem.NewStruct([]stackitem.Item{ 413 stackitem.NewByteArray([]byte("mur")), 414 stackitem.NewArray([]stackitem.Item{ 415 stackitem.NewStruct([]stackitem.Item{ 416 stackitem.NewByteArray([]byte("p1")), 417 stackitem.NewBigInteger(big.NewInt(int64(smartcontract.BoolType))), 418 }), 419 }), 420 }), 421 }), 422 }) 423 CheckToFromStackItem(t, a, expected) 424 } 425 426 func TestABI_FromStackItemErrors(t *testing.T) { 427 errCases := map[string]stackitem.Item{ 428 "not a struct": stackitem.NewArray([]stackitem.Item{}), 429 "invalid length": stackitem.NewStruct([]stackitem.Item{}), 430 "invalid methods type": stackitem.NewStruct([]stackitem.Item{stackitem.NewInterop(nil), stackitem.Null{}}), 431 "invalid method": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{stackitem.Null{}}), stackitem.Null{}}), 432 "invalid events type": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.Null{}}), 433 "invalid event": stackitem.NewStruct([]stackitem.Item{stackitem.NewArray([]stackitem.Item{}), stackitem.NewArray([]stackitem.Item{stackitem.Null{}})}), 434 } 435 for name, errCase := range errCases { 436 t.Run(name, func(t *testing.T) { 437 p := new(ABI) 438 require.Error(t, p.FromStackItem(errCase)) 439 }) 440 } 441 } 442 443 func TestExtraToStackItem(t *testing.T) { 444 testCases := []struct { 445 raw, expected string 446 }{ 447 {"null", "null"}, 448 {"1", "1"}, 449 {"1.23456789101112131415", "1.23456789101112131415"}, 450 {`"string with space"`, `"string with space"`}, 451 {`{ "a":1, "sss" : {"m" : 1, "a" : 2} , "x" : 2 ,"c" :3,"z":4, "s":"5"}`, 452 `{"a":1,"sss":{"m":1,"a":2},"x":2,"c":3,"z":4,"s":"5"}`}, 453 {` [ 1, "array", { "d": "z", "a":"x", "c" : "y", "b":3}]`, 454 `[1,"array",{"d":"z","a":"x","c":"y","b":3}]`}, 455 { 456 // C# double quotes marshalling compatibility test, ref. #3284. 457 `{"Author":"NEOZEN","Description":"NEO\u0027s First Inscriptions Meta Protocol","Deployment":"{\"p\":\"neoz-20\",\"op\":\"deploy\",\"tick\":\"neoz\",\"max\":\"21000000\",\"lim\":\"1000\"}"}`, 458 `{"Author":"NEOZEN","Description":"NEO\u0027s First Inscriptions Meta Protocol","Deployment":"{\u0022p\u0022:\u0022neoz-20\u0022,\u0022op\u0022:\u0022deploy\u0022,\u0022tick\u0022:\u0022neoz\u0022,\u0022max\u0022:\u002221000000\u0022,\u0022lim\u0022:\u00221000\u0022}"}`, 459 }, 460 } 461 462 for _, tc := range testCases { 463 res := extraToStackItem([]byte(tc.raw)) 464 actual, ok := res.Value().([]byte) 465 require.True(t, ok) 466 require.Equal(t, tc.expected, string(actual)) 467 } 468 } 469 470 func TestManifest_IsStandardSupported(t *testing.T) { 471 m := &Manifest{ 472 SupportedStandards: []string{NEP17StandardName, NEP17Payable, NEP11Payable}, 473 } 474 for _, st := range m.SupportedStandards { 475 require.True(t, m.IsStandardSupported(st)) 476 } 477 require.False(t, m.IsStandardSupported(NEP11StandardName)) 478 require.False(t, m.IsStandardSupported("")) 479 require.False(t, m.IsStandardSupported("unknown standard")) 480 }