github.com/m3db/m3@v1.5.0/src/dbnode/namespace/schema_test.go (about) 1 // Copyright (c) 2019 Uber Technologies, Inc. 2 // 3 // Permission is hereby granted, free of charge, to any person obtaining a copy 4 // of this software and associated documentation files (the "Software"), to deal 5 // in the Software without restriction, including without limitation the rights 6 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 // copies of the Software, and to permit persons to whom the Software is 8 // furnished to do so, subject to the following conditions: 9 // 10 // The above copyright notice and this permission notice shall be included in 11 // all copies or substantial portions of the Software. 12 // 13 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 19 // THE SOFTWARE. 20 21 package namespace 22 23 import ( 24 "testing" 25 26 nsproto "github.com/m3db/m3/src/dbnode/generated/proto/namespace" 27 xerrors "github.com/m3db/m3/src/x/errors" 28 29 "github.com/stretchr/testify/require" 30 ) 31 32 var ( 33 testSchemaOptions = GenTestSchemaOptions("mainpkg/main.proto", "testdata") 34 ) 35 36 func TestLoadSchemaHistory(t *testing.T) { 37 testSchemaReg, err := LoadSchemaHistory(testSchemaOptions) 38 require.NoError(t, err) 39 40 testSchema, found := testSchemaReg.Get("first") 41 require.True(t, found) 42 require.NotNil(t, testSchema) 43 require.EqualValues(t, testSchemaOptions.DefaultMessageName, testSchema.Get().GetFullyQualifiedName()) 44 45 latestSchema, found := testSchemaReg.GetLatest() 46 require.True(t, found) 47 require.NotNil(t, latestSchema) 48 require.EqualValues(t, testSchemaOptions.DefaultMessageName, latestSchema.Get().GetFullyQualifiedName()) 49 } 50 51 func TestParseProto(t *testing.T) { 52 out, err := parseProto("mainpkg/main.proto", nil, "testdata") 53 require.NoError(t, err) 54 require.Len(t, out, 3) 55 for _, o := range out { 56 t.Log(o.GetFullyQualifiedName()) 57 } 58 require.NotNil(t, out[0].FindMessage("mainpkg.ImportedMessage")) 59 require.NotNil(t, out[1].FindMessage("otherpkg.MessageFromOtherPkg")) 60 require.NotNil(t, out[2].FindMessage("mainpkg.NestedMessage")) 61 require.NotNil(t, out[2].FindMessage("mainpkg.TestMessage")) 62 } 63 64 func TestDistinctIndirectDependency(t *testing.T) { 65 out, err := parseProto("deduppkg/main.proto", nil, "testdata") 66 require.NoError(t, err) 67 require.Len(t, out, 4) 68 for _, o := range out { 69 t.Log(o.GetFullyQualifiedName()) 70 } 71 require.NotNil(t, out[0].FindMessage("otherpkg.MessageFromOtherPkg")) 72 require.NotNil(t, out[1].FindMessage("deduppkg.IndirectMessage")) 73 require.NotNil(t, out[2].FindMessage("deduppkg.ImportedMessage")) 74 require.NotNil(t, out[3].FindMessage("deduppkg.TestMessage")) 75 76 // test it can be loaded back correctly 77 dlist, _ := marshalFileDescriptors(out) 78 79 schemaOpt := &nsproto.SchemaOptions{ 80 History: &nsproto.SchemaHistory{ 81 Versions: []*nsproto.FileDescriptorSet{ 82 {DeployId: "first", Descriptors: dlist}, 83 }, 84 }, 85 DefaultMessageName: "deduppkg.TestMessage", 86 } 87 _, err = LoadSchemaHistory(schemaOpt) 88 require.NoError(t, err) 89 } 90 91 func TestInvalidSchemaOptions(t *testing.T) { 92 out, _ := parseProto("mainpkg/main.proto", nil, "testdata") 93 94 dlist, _ := marshalFileDescriptors(out) 95 96 // missing dependency 97 schemaOpt1 := &nsproto.SchemaOptions{ 98 History: &nsproto.SchemaHistory{ 99 Versions: []*nsproto.FileDescriptorSet{ 100 {DeployId: "first", Descriptors: dlist[1:]}, 101 }, 102 }, 103 DefaultMessageName: "mainpkg.TestMessage", 104 } 105 _, err := LoadSchemaHistory(schemaOpt1) 106 require.Error(t, err) 107 108 // wrong message name 109 schemaOpt2 := &nsproto.SchemaOptions{ 110 History: &nsproto.SchemaHistory{ 111 Versions: []*nsproto.FileDescriptorSet{ 112 {DeployId: "first", Descriptors: dlist}, 113 }, 114 }, 115 DefaultMessageName: "WrongMessage", 116 } 117 _, err = LoadSchemaHistory(schemaOpt2) 118 require.Error(t, err) 119 require.Equal(t, errInvalidSchemaOptions, xerrors.InnerError(err)) 120 121 // reverse the list (so that it is not topologically sorted) 122 dlistR := make([][]byte, len(dlist)) 123 for i := 0; i < len(dlist); i++ { 124 dlistR[i] = dlist[len(dlist)-i-1] 125 } 126 127 schemaOpt3 := &nsproto.SchemaOptions{ 128 History: &nsproto.SchemaHistory{ 129 Versions: []*nsproto.FileDescriptorSet{ 130 {DeployId: "first", Descriptors: dlistR}, 131 }, 132 }, 133 DefaultMessageName: "mainpkg.TestMessage", 134 } 135 _, err = LoadSchemaHistory(schemaOpt3) 136 require.Error(t, err) 137 } 138 139 func TestParseNotProto3(t *testing.T) { 140 _, err := parseProto("mainpkg/notproto3.proto", nil, "testdata") 141 require.Error(t, err) 142 require.Equal(t, errSyntaxNotProto3, xerrors.InnerError(err)) 143 } 144 145 func TestSchemaHistorySortedDescending(t *testing.T) { 146 out, _ := parseProto("mainpkg/main.proto", nil, "testdata") 147 148 dlist, _ := marshalFileDescriptors(out) 149 schemaOpt := &nsproto.SchemaOptions{ 150 History: &nsproto.SchemaHistory{ 151 Versions: []*nsproto.FileDescriptorSet{ 152 {DeployId: "third", PrevId: "second", Descriptors: dlist}, 153 {DeployId: "second", PrevId: "first", Descriptors: dlist}, 154 {DeployId: "first", Descriptors: dlist}, 155 }, 156 }, 157 DefaultMessageName: "mainpkg.TestMessage", 158 } 159 _, err := LoadSchemaHistory(schemaOpt) 160 require.Error(t, err) 161 require.Equal(t, errInvalidSchemaOptions, xerrors.InnerError(err)) 162 } 163 164 func TestSchemaOptionsLineageBroken(t *testing.T) { 165 out, _ := parseProto("mainpkg/main.proto", nil, "testdata") 166 167 dlist, _ := marshalFileDescriptors(out) 168 schemaOpt := &nsproto.SchemaOptions{ 169 History: &nsproto.SchemaHistory{ 170 Versions: []*nsproto.FileDescriptorSet{ 171 {DeployId: "first", Descriptors: dlist}, 172 {DeployId: "third", PrevId: "second", Descriptors: dlist}, 173 }, 174 }, 175 DefaultMessageName: "mainpkg.TestMessage", 176 } 177 _, err := LoadSchemaHistory(schemaOpt) 178 require.Error(t, err) 179 require.Equal(t, errInvalidSchemaOptions, xerrors.InnerError(err)) 180 } 181 182 func TestSchemaHistoryCheckLineage(t *testing.T) { 183 out, _ := parseProto("mainpkg/main.proto", nil, "testdata") 184 185 dlist, _ := marshalFileDescriptors(out) 186 187 schemaOpt1 := &nsproto.SchemaOptions{ 188 History: &nsproto.SchemaHistory{ 189 Versions: []*nsproto.FileDescriptorSet{ 190 {DeployId: "first", Descriptors: dlist}, 191 }, 192 }, 193 DefaultMessageName: "mainpkg.TestMessage", 194 } 195 sr1, err := LoadSchemaHistory(schemaOpt1) 196 require.NoError(t, err) 197 198 schemaOpt2 := &nsproto.SchemaOptions{ 199 History: &nsproto.SchemaHistory{ 200 Versions: []*nsproto.FileDescriptorSet{ 201 {DeployId: "first", Descriptors: dlist}, 202 {DeployId: "second", PrevId: "first", Descriptors: dlist}, 203 }, 204 }, 205 DefaultMessageName: "mainpkg.TestMessage", 206 } 207 sr2, err := LoadSchemaHistory(schemaOpt2) 208 require.NoError(t, err) 209 210 require.True(t, sr1.Extends(emptySchemaHistory())) 211 require.True(t, sr2.Extends(emptySchemaHistory())) 212 require.False(t, sr1.Extends(sr2)) 213 require.True(t, sr2.Extends(sr1)) 214 215 schemaOpt3 := &nsproto.SchemaOptions{ 216 History: &nsproto.SchemaHistory{ 217 Versions: []*nsproto.FileDescriptorSet{ 218 {DeployId: "first", Descriptors: dlist}, 219 {DeployId: "third", PrevId: "first", Descriptors: dlist}, 220 }, 221 }, 222 DefaultMessageName: "mainpkg.TestMessage", 223 } 224 sr3, err := LoadSchemaHistory(schemaOpt3) 225 require.NoError(t, err) 226 227 require.True(t, sr3.Extends(sr1)) 228 require.False(t, sr3.Extends(sr2)) 229 } 230 231 const ( 232 mainProtoStr = `syntax = "proto3"; 233 234 package mainpkg; 235 236 import "mainpkg/imported.proto"; 237 238 message TestMessage { 239 double latitude = 1; 240 double longitude = 2; 241 int64 epoch = 3; 242 bytes deliveryID = 4; 243 map<string, string> attributes = 5; 244 ImportedMessage an_imported_message = 6; 245 } 246 ` 247 importedProtoStr = ` 248 syntax = "proto3"; 249 250 package mainpkg; 251 252 message ImportedMessage { 253 double latitude = 1; 254 double longitude = 2; 255 int64 epoch = 3; 256 bytes deliveryID = 4; 257 } 258 ` 259 ) 260 261 func TestAppendInvalidSchemaOptions(t *testing.T) { 262 protoFile := "mainpkg/test.proto" 263 protoMsg := "mainpkg.TestMessage" 264 protoMap := map[string]string{protoFile: mainProtoStr, "mainpkg/imported.proto": importedProtoStr} 265 schemaOpt, err := AppendSchemaOptions(nil, protoFile, protoMsg, protoMap, "first") 266 require.NoError(t, err) 267 require.NotNil(t, schemaOpt) 268 _, err = AppendSchemaOptions(nil, protoFile, protoMsg, protoMap, "") 269 require.Error(t, err) 270 _, err = AppendSchemaOptions(schemaOpt, protoFile, protoMsg, protoMap, "first") 271 require.Error(t, err) 272 273 invalidMsg := "TestMessage" 274 _, err = AppendSchemaOptions(nil, protoFile, invalidMsg, protoMap, "first") 275 require.Error(t, err) 276 _, err = AppendSchemaOptions(schemaOpt, protoFile, invalidMsg, protoMap, "second") 277 require.Error(t, err) 278 279 invalidMap := map[string]string{protoFile: mainProtoStr} 280 _, err = AppendSchemaOptions(nil, protoFile, protoMsg, invalidMap, "first") 281 require.Error(t, err) 282 _, err = AppendSchemaOptions(schemaOpt, protoFile, protoMsg, invalidMap, "second") 283 require.Error(t, err) 284 }