package protoutil_test import ( "testing" "github.com/emicklei/proto" "github.com/stretchr/testify/require" "github.com/ignite/cli/v29/ignite/pkg/protoanalysis/protoutil" ) // Imports. func TestCreateImport(t *testing.T) { cases := []struct { name, path, kind string out *proto.Import }{ { name: "simple import", path: "github.com/emicklei/proto.proto", kind: "weak", out: &proto.Import{ Filename: "github.com/emicklei/proto.proto", Kind: "weak", }, }, { name: "simple import", path: "github.com/emicklei/proto.proto", kind: "public", out: &proto.Import{ Filename: "github.com/emicklei/proto.proto", Kind: "public", }, }, } for _, test := range cases { var spec []protoutil.ImportSpecOptions switch test.kind { case "weak": spec = append(spec, protoutil.Weak()) case "public": spec = append(spec, protoutil.Public()) } imp := protoutil.NewImport(test.path, spec...) require.Equal(t, test.out, imp, "expected %v, got %v", test.out, imp) } } // Packages. func TestCreatePackage(t *testing.T) { cases := []struct { name string out *proto.Package }{ { name: "org.foo.hack", out: &proto.Package{ Name: "org.foo.hack", }, }, { name: "simple.package", out: &proto.Package{ Name: "simple.package", }, }, } for _, test := range cases { p := protoutil.NewPackage(test.name) require.Equal(t, test.out, p, "expected %v, got %v", test.out, p) } } // Options. func TestCreateOption(t *testing.T) { cases := []struct { name, constant, setField string isCustom bool out *proto.Option }{ { name: "my_option", constant: "5", out: &proto.Option{ Name: "my_option", Constant: *protoutil.NewLiteral("5"), }, }, { name: "my_option", constant: "false", isCustom: true, out: &proto.Option{ Name: "(my_option)", Constant: *protoutil.NewLiteral("false"), }, }, { name: "my_option", constant: "2.341", setField: "my_field", isCustom: true, out: &proto.Option{ Name: "(my_option).my_field", Constant: *protoutil.NewLiteral("2.341"), }, }, } for _, test := range cases { var opts []protoutil.OptionSpecOptions if test.isCustom { opts = []protoutil.OptionSpecOptions{protoutil.Custom()} } if test.setField != "" { opts = append(opts, protoutil.SetField(test.setField)) } opt := protoutil.NewOption(test.name, test.constant, opts...) require.Equal(t, test.out, opt, "expected %v, got %v", test.out, opt) } } // RPCs. func TestCreateRPC(t *testing.T) { cases := []struct { name, inputType, outputType string streamsReq, streamsResp bool options []*proto.Option }{ { name: "my_rpc", inputType: "my_input_type", outputType: "my_output_type", }, { name: "my_rpc", inputType: "my_input_type", outputType: "my_output_type", streamsReq: true, streamsResp: true, }, { name: "my_rpc", inputType: "my_input_type", outputType: "my_output_type", options: []*proto.Option{ protoutil.NewOption("my_option", "5"), protoutil.NewOption("gogoproto.nullable", "false", protoutil.Custom(), protoutil.SetField("set")), }, }, } for _, test := range cases { var opts []protoutil.RPCSpecOptions if test.streamsReq { opts = append(opts, protoutil.StreamRequest()) } if test.streamsResp { opts = append(opts, protoutil.StreamResponse()) } if len(test.options) > 0 { opts = append(opts, protoutil.WithRPCOptions(test.options...)) } rpc := protoutil.NewRPC(test.name, test.inputType, test.outputType, opts...) require.Equal(t, test.name, rpc.Name, "expected %v, got %v", test.name, rpc.Name) require.Equal(t, test.inputType, rpc.RequestType, "expected %v, got %v", test.inputType, rpc.ReturnsType) require.Equal(t, test.outputType, rpc.ReturnsType, "expected %v, got %v", test.outputType, rpc.ReturnsType) require.Equal(t, test.streamsReq, rpc.StreamsRequest, "expected %v, got %v", test.streamsReq, rpc.StreamsRequest) require.Equal(t, test.streamsResp, rpc.StreamsReturns, "expected %v, got %v", test.streamsResp, rpc.StreamsReturns) for i, opt := range rpc.Elements { opt, ok := opt.(*proto.Option) require.True(t, ok, "expected option, got %T", opt) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } require.Equal(t, len(test.options), len(rpc.Elements), "expected %v, got %v", len(test.options), len(rpc.Elements)) } } // Services. func TestCreateService(t *testing.T) { cases := []struct { name string rpcs []*proto.RPC options []*proto.Option }{ { name: "my_service", rpcs: []*proto.RPC{ protoutil.NewRPC("my_rpc", "my_input_type", "my_output_type"), protoutil.NewRPC("my_other_rpc", "my_other_input_type", "my_other_output_type", protoutil.StreamRequest(), protoutil.StreamResponse()), }, options: []*proto.Option{protoutil.NewOption("my_option", "with a great value")}, }, } for _, test := range cases { var opts []protoutil.ServiceSpecOptions opts = append(opts, protoutil.WithRPCs(test.rpcs...)) opts = append(opts, protoutil.WithServiceOptions(test.options...)) rpc := protoutil.NewService(test.name, opts...) require.Equal(t, test.name, rpc.Name, "expected %v, got %v", test.name, rpc.Name) // careful, options come first, then rpcs. lenOpts, lenRPCs := len(test.options), len(test.rpcs) require.True(t, len(rpc.Elements) == lenOpts+lenRPCs, "expected %v, got %v", lenOpts+lenRPCs, len(rpc.Elements)) for i, opt := range rpc.Elements { if i < lenOpts { opt, ok := opt.(*proto.Option) require.True(t, ok, "expected option, got %T", opt) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } else { rpc, ok := opt.(*proto.RPC) require.True(t, ok, "expected rpc, got %T", opt) require.Equal(t, test.rpcs[i-lenOpts], rpc, "expected %v, got %v", test.rpcs[i-lenOpts], rpc) } } } } // Fields. func TestCreateField(t *testing.T) { cases := []struct { name, typeName string sequence int repeated, optional, required bool options []*proto.Option }{ { name: "my_field", typeName: "my_type", sequence: 1, repeated: true, }, { name: "my_field", typeName: "my_type", sequence: 2, optional: true, }, { name: "my_field", typeName: "my_type", sequence: 3, required: true, }, { name: "my_field", typeName: "my_type", sequence: 4, options: []*proto.Option{ protoutil.NewOption("my_option", "5"), protoutil.NewOption("gogoproto.nullable", "false", protoutil.Custom(), protoutil.SetField("set")), }, }, } for _, test := range cases { var opts []protoutil.FieldSpecOptions if test.repeated { opts = append(opts, protoutil.Repeated()) } if test.optional { opts = append(opts, protoutil.Optional()) } if test.required { opts = append(opts, protoutil.Required()) } opts = append(opts, protoutil.WithFieldOptions(test.options...)) field := protoutil.NewField(test.name, test.typeName, test.sequence, opts...) require.Equal(t, test.name, field.Name, "expected %v, got %v", test.name, field.Name) require.Equal(t, test.typeName, field.Type, "expected %v, got %v", test.typeName, field.Type) require.Equal(t, test.sequence, field.Sequence, "expected %v, got %v", test.sequence, field.Sequence) require.Equal(t, test.repeated, field.Repeated, "expected %v, got %v", test.repeated, field.Repeated) require.Equal(t, test.optional, field.Optional, "expected %v, got %v", test.optional, field.Optional) require.Equal(t, test.required, field.Required, "expected %v, got %v", test.required, field.Required) for i, opt := range field.Options { require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } require.Equal(t, len(test.options), len(field.Options), "expected %v, got %v", len(test.options), len(field.Options)) } } // Messages. func TestCreateMessage(t *testing.T) { cases := []struct { name string fields []*proto.NormalField enums []*proto.Enum options []*proto.Option isExtend bool }{ { name: "my_message", fields: []*proto.NormalField{ protoutil.NewField("my_field", "my_type", 1), protoutil.NewField("my_other_field", "my_other_type", 2), }, }, { name: "my_message", fields: []*proto.NormalField{ protoutil.NewField("my_field", "my_type", 1), protoutil.NewField("my_other_field", "my_other_type", 2), }, enums: []*proto.Enum{protoutil.NewEnum("my_enum")}, options: []*proto.Option{ protoutil.NewOption("my_option", "with a great value"), protoutil.NewOption("gogoproto.nullable", "false", protoutil.Custom(), protoutil.SetField("set")), }, isExtend: true, }, } for _, test := range cases { var opts []protoutil.MessageSpecOptions opts = append(opts, protoutil.WithFields(test.fields...)) opts = append(opts, protoutil.WithEnums(test.enums...)) opts = append(opts, protoutil.WithMessageOptions(test.options...)) if test.isExtend { opts = append(opts, protoutil.Extend()) } message := protoutil.NewMessage(test.name, opts...) require.Equal(t, test.name, message.Name, "expected %v, got %v", test.name, message.Name) require.Equal(t, test.isExtend, message.IsExtend, "expected %v, got %v", test.isExtend, message.IsExtend) // options added first, then fields and then enums. lenOpts, lenFields, lenEnums := len(test.options), len(test.fields), len(test.enums) for i, field := range message.Elements { switch { case i < lenOpts: opt, ok := field.(*proto.Option) require.True(t, ok, "expected option, got %T", field) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) case i < lenOpts+lenFields: field, ok := field.(*proto.NormalField) require.True(t, ok, "expected field, got %T", field) require.Equal(t, test.fields[i-lenOpts], field, "expected %v, got %v", test.fields[i-lenOpts], field) default: enum, ok := field.(*proto.Enum) require.True(t, ok, "expected enum, got %T", field) require.Equal(t, test.enums[i-lenOpts-lenFields], enum, "expected %v, got %v", test.enums[i-lenOpts-lenFields], enum) } } require.True(t, lenOpts+lenFields+lenEnums == len(message.Elements), "expected %v, got %v", lenOpts+lenFields+lenEnums, len(message.Elements)) } } // Enum fields. func TestCreateEnumField(t *testing.T) { cases := []struct { name string value int options []*proto.Option }{ { name: "my_field", value: 1, }, { name: "my_field", value: 2, options: []*proto.Option{ protoutil.NewOption("my_option", "with a great value"), protoutil.NewOption("gogoproto.nullable", "false", protoutil.Custom(), protoutil.SetField("set")), }, }, } for _, test := range cases { var opts []protoutil.EnumFieldSpecOptions opts = append(opts, protoutil.WithEnumFieldOptions(test.options...)) field := protoutil.NewEnumField(test.name, test.value, opts...) require.Equal(t, test.name, field.Name, "expected %v, got %v", test.name, field.Name) require.Equal(t, test.value, field.Integer, "expected %v, got %v", test.value, field.Integer) for i, opt := range field.Elements { opt, ok := opt.(*proto.Option) require.True(t, ok, "expected option, got %T", opt) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } require.Equal(t, len(test.options), len(field.Elements), "expected %v, got %v", len(test.options), len(field.Elements)) } } // Enums:. func TestCreateEnum(t *testing.T) { cases := []struct { name string options []*proto.Option values []*proto.EnumField }{ { name: "my_enum", values: []*proto.EnumField{ protoutil.NewEnumField("my_value", 1), protoutil.NewEnumField("my_other_value", 2), }, }, { name: "my_enum", values: []*proto.EnumField{ protoutil.NewEnumField("my_value", 1), protoutil.NewEnumField("my_other_value", 2), }, options: []*proto.Option{ protoutil.NewOption("my_option", "with a great value"), }, }, } for _, test := range cases { var opts []protoutil.EnumSpecOpts opts = append(opts, protoutil.WithEnumFields(test.values...)) opts = append(opts, protoutil.WithEnumOptions(test.options...)) enum := protoutil.NewEnum(test.name, opts...) require.Equal(t, test.name, enum.Name, "expected %v, got %v", test.name, enum.Name) lenFields, lenOptions := len(test.values), len(test.options) for i, opt := range enum.Elements[:lenOptions] { opt, ok := opt.(*proto.Option) require.True(t, ok, "expected option, got %T", opt) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } for i, value := range enum.Elements[lenOptions:] { value, ok := value.(*proto.EnumField) require.True(t, ok, "expected enum field, got %T", value) require.Equal(t, test.values[i], value, "expected %v, got %v", test.values[i], value) } require.Equal(t, lenOptions+lenFields, len(enum.Elements), "expected %v, got %v", lenOptions+lenFields, len(enum.Elements)) } } // OneOf fields:. func TestCreateOneofField(t *testing.T) { cases := []struct { name, typeName string sequence int options []*proto.Option }{ { name: "my_field", typeName: "my_type", sequence: 1, }, { name: "my_field", typeName: "my_type", sequence: 4, options: []*proto.Option{ protoutil.NewOption("my_option", "5"), protoutil.NewOption("gogoproto.nullable", "false", protoutil.Custom(), protoutil.SetField("set")), }, }, } for _, test := range cases { opts := []protoutil.OneofFieldOptions{protoutil.WithOneofFieldOptions(test.options...)} field := protoutil.NewOneofField(test.name, test.typeName, test.sequence, opts...) require.Equal(t, test.name, field.Name, "expected %v, got %v", test.name, field.Name) require.Equal(t, test.typeName, field.Type, "expected %v, got %v", test.typeName, field.Type) require.Equal(t, test.sequence, field.Sequence, "expected %v, got %v", test.sequence, field.Sequence) for i, opt := range field.Options { require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } require.Equal(t, len(test.options), len(field.Options), "expected %v, got %v", len(test.options), len(field.Options)) } } // Oneof:. func TestCreateOneof(t *testing.T) { cases := []struct { name string options []*proto.Option values []*proto.OneOfField }{ { name: "oneof_this", values: []*proto.OneOfField{ protoutil.NewOneofField("my_value", "my_type", 1), protoutil.NewOneofField("my_other_value", "my_type", 2), }, }, { name: "oneof_that", values: []*proto.OneOfField{ protoutil.NewOneofField("my_value", "my_type", 1), }, options: []*proto.Option{ protoutil.NewOption("my_option", "with a great value"), }, }, } for _, test := range cases { var opts []protoutil.OneofSpecOptions opts = append(opts, protoutil.WithOneofFields(test.values...)) opts = append(opts, protoutil.WithOneofOptions(test.options...)) oneof := protoutil.NewOneof(test.name, opts...) require.Equal(t, test.name, oneof.Name, "expected %v, got %v", test.name, oneof.Name) lenFields, lenOptions := len(test.values), len(test.options) for i, opt := range oneof.Elements[:lenOptions] { opt, ok := opt.(*proto.Option) require.True(t, ok, "expected option, got %T", opt) require.Equal(t, test.options[i], opt, "expected %v, got %v", test.options[i], opt) } for i, value := range oneof.Elements[lenOptions:] { value, ok := value.(*proto.OneOfField) require.True(t, ok, "expected oneof field, got %T", value) require.Equal(t, test.values[i], value, "expected %v, got %v", test.values[i], value) } require.Equal(t, lenOptions+lenFields, len(oneof.Elements), "expected %v, got %v", lenOptions+lenFields, len(oneof.Elements)) } } func TestAttachComment(t *testing.T) { // Attach comment to message msg := protoutil.NewMessage("my_message") protoutil.AttachComment(msg, "my comment") require.Equal(t, " my comment", msg.Comment.Lines[0], "expected %v, got %v", "my comment", msg.Comment.Lines[0]) // Attach comment to rpc call rpc := protoutil.NewRPC("my_rpc", "my_request", "my_response") protoutil.AttachComment(rpc, "my comment") require.Equal(t, " my comment", rpc.Comment.Lines[0], "expected %v, got %v", "my comment", rpc.Comment.Lines[0]) // Attach comment to a service svc := protoutil.NewService("my_service") protoutil.AttachComment(svc, "my comment") require.Equal(t, " my comment", svc.Comment.Lines[0], "expected %v, got %v", "my comment", svc.Comment.Lines[0]) } // Test literal creation (indirectly tests the isString function.) func TestIsString(t *testing.T) { require.True(t, protoutil.NewLiteral("string").IsString) require.True(t, protoutil.NewLiteral("THIS/PATH/IS/STRING").IsString) // Don't report "true" and "false" as strings require.False(t, protoutil.NewLiteral("true").IsString) require.False(t, protoutil.NewLiteral("false").IsString) // Don't report numbers as strings require.False(t, protoutil.NewLiteral("1").IsString) require.False(t, protoutil.NewLiteral("1.0").IsString) require.False(t, protoutil.NewLiteral("1.0e-10").IsString) require.False(t, protoutil.NewLiteral("1.0e+10").IsString) require.False(t, protoutil.NewLiteral("1.0e10").IsString) require.False(t, protoutil.NewLiteral("3.1929348317293483e-10").IsString) // A single numbers means not a string, parser would fail with that either way. require.True(t, protoutil.NewLiteral("isthisastringohnoitactuallyisn't1.0").IsString) }