github.com/m3db/m3@v1.5.1-0.20231129193456-75a402aa583b/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  }