Some checks are pending
Docs Deploy / build_and_deploy (push) Waiting to run
Generate Docs / cli (push) Waiting to run
Generate Config Doc / cli (push) Waiting to run
Go formatting / go-formatting (push) Waiting to run
Check links / markdown-link-check (push) Waiting to run
Integration / pre-test (push) Waiting to run
Integration / test on (push) Blocked by required conditions
Integration / status (push) Blocked by required conditions
Lint / Lint Go code (push) Waiting to run
Test / test (ubuntu-latest) (push) Waiting to run
563 lines
13 KiB
Go
563 lines
13 KiB
Go
package protoutil
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/emicklei/proto"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
const (
|
|
world = "World"
|
|
elements = "Elements"
|
|
kirby = "Kirby"
|
|
)
|
|
|
|
// Make a simple replacement of package -> import.
|
|
func TestSimpleReplacement(t *testing.T) {
|
|
f, err := parseStringProto(`package "package"`)
|
|
require.NoError(t, err)
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if _, ok := n.(*proto.Package); ok {
|
|
imp := NewImport("that")
|
|
c.Replace(imp)
|
|
}
|
|
|
|
return true
|
|
})
|
|
require.True(t, containsElement(f, NewImport("that")))
|
|
require.False(t, containsElement(f, NewPackage("package")))
|
|
}
|
|
|
|
func TestSimpleInsertAfter(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {
|
|
message World {}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
// keep ref for checking containment.
|
|
var msg *proto.Message
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Message); ok {
|
|
if n.Name == world {
|
|
msg = NewMessage("WeComeInPeace")
|
|
c.InsertAfter(msg)
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
require.True(t, containsElement(f, msg))
|
|
// check that it is inserted after "World"
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Message); ok {
|
|
if n.Name == world {
|
|
next, ok := c.Next()
|
|
require.True(t, ok)
|
|
require.True(t, next.(*proto.Message).Name == "WeComeInPeace")
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
// Can really only panic with comments since
|
|
// other elements in nodes aren't Visitees.
|
|
//
|
|
//nolint:dupword
|
|
func TestInsertAfterPanic(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
// my import
|
|
import "this";
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
// Try calling insertAfter when c is a Comment
|
|
require.Panics(t, func() {
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if _, ok := n.(*proto.Comment); ok {
|
|
c.InsertAfter(NewImport("that"))
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
}
|
|
|
|
func TestSimpleInsertBefore(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Say {}
|
|
message World {}
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
// keep ref for checking containment.
|
|
var msg *proto.Message
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Message); ok {
|
|
if n.Name == world {
|
|
// add hello between say and world
|
|
msg = NewMessage("Hello")
|
|
c.InsertBefore(msg)
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
require.True(t, containsElement(f, msg))
|
|
|
|
// check that it is inserted after "Say"
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Message); ok {
|
|
if n.Name == "Say" {
|
|
next, ok := c.Next()
|
|
require.True(t, ok)
|
|
require.True(t, next.(*proto.Message).Name == "Hello")
|
|
}
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
// Can really only panic with comments since
|
|
// other elements in nodes aren't Visitees.
|
|
//
|
|
//nolint:dupword
|
|
func TestInsertBeforePanic(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
// my import
|
|
import "this";
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
// Try calling insertAfter when c is a Comment
|
|
require.Panics(t, func() {
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if _, ok := n.(*proto.Comment); ok {
|
|
c.InsertBefore(NewImport("that"))
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
}
|
|
|
|
// Build a skeleton of a file by continuous appends on the file.
|
|
func TestAppendFile(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"`)
|
|
require.NoError(t, err)
|
|
|
|
i := NewImport("importpath")
|
|
Append(f, i)
|
|
require.True(t, containsElement(f, i))
|
|
|
|
p := NewPackage("package")
|
|
Append(f, p)
|
|
require.True(t, containsElement(f, p))
|
|
|
|
o := NewOption("this", "that")
|
|
Append(f, o)
|
|
require.True(t, containsElement(f, o))
|
|
|
|
oneofF := NewOneofField("this", "string", 2)
|
|
// Can directly append an option if required:
|
|
opt := NewOption("this", "that")
|
|
Append(oneofF, opt)
|
|
require.True(t, containsElement(oneofF, opt))
|
|
|
|
oneof := NewOneof("myoneof")
|
|
Append(oneof, oneofF)
|
|
require.True(t, containsElement(oneof, oneofF))
|
|
|
|
normalfield := NewField("that", "string", 3)
|
|
|
|
m := NewMessage("Hello")
|
|
Append(m, oneof)
|
|
require.True(t, containsElement(m, oneof))
|
|
Append(m, normalfield)
|
|
require.True(t, containsElement(m, normalfield))
|
|
|
|
Append(f, m)
|
|
require.True(t, containsElement(f, m))
|
|
|
|
// Append an empty service
|
|
s := NewService("Hey")
|
|
Append(f, s)
|
|
require.True(t, containsElement(f, s))
|
|
|
|
// An empty enum
|
|
e := NewEnum("Hey")
|
|
// Add an enum field to it:
|
|
ef := NewEnumField("HEY", 1)
|
|
Append(e, ef)
|
|
require.True(t, containsElement(e, ef))
|
|
|
|
Append(f, e)
|
|
require.True(t, containsElement(f, e))
|
|
}
|
|
|
|
// Append to a node w/o elements panics.
|
|
func TestAppendEdges(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"`)
|
|
require.NoError(t, err)
|
|
|
|
// Can't append to a Syntax node, panic.
|
|
require.Panics(t, func() {
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Syntax); ok {
|
|
Append(n, NewImport("that"))
|
|
}
|
|
return true
|
|
})
|
|
})
|
|
|
|
// Empty append does nothing.
|
|
elems := len(f.Elements)
|
|
Append(f)
|
|
require.True(t, len(f.Elements) == elems)
|
|
|
|
// Appending a non-option to NormalField/OneOfField panics.
|
|
require.Panics(t, func() {
|
|
f := NewField("that", "string", 3)
|
|
Append(f, NewImport("that"))
|
|
})
|
|
}
|
|
|
|
func TestCursorOps(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {}
|
|
message World {
|
|
message Hey {}
|
|
enum E {}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
Apply(f, nil, func(c *Cursor) bool {
|
|
n := c.Node()
|
|
if n, ok := n.(*proto.Message); ok {
|
|
if n.Name == "Hello" {
|
|
require.False(t, c.IsLast())
|
|
n, ok := c.Next()
|
|
require.True(t, ok)
|
|
require.NotNil(t, n)
|
|
|
|
parent, ok := c.Parent().(*proto.Proto)
|
|
require.True(t, ok)
|
|
require.True(t, parent.Filename == "")
|
|
// currently useless.
|
|
require.True(t, c.Name() == elements)
|
|
}
|
|
if n.Name == world {
|
|
require.True(t, c.IsLast())
|
|
n, ok := c.Next()
|
|
require.False(t, ok)
|
|
require.Nil(t, n)
|
|
|
|
parent, ok := c.Parent().(*proto.Proto)
|
|
require.True(t, ok)
|
|
require.True(t, parent.Filename == "")
|
|
// currently useless.
|
|
require.True(t, c.Name() == elements)
|
|
}
|
|
|
|
if n.Name == "Hey" {
|
|
require.False(t, c.IsLast())
|
|
n, ok := c.Next()
|
|
require.True(t, ok)
|
|
require.NotNil(t, n)
|
|
|
|
// parent is the message
|
|
parent, ok := c.Parent().(*proto.Message)
|
|
require.True(t, ok)
|
|
require.True(t, parent.Name == "World")
|
|
// currently useless.
|
|
require.True(t, c.Name() == elements)
|
|
}
|
|
}
|
|
|
|
if _, ok := n.(*proto.Enum); ok {
|
|
require.True(t, c.IsLast())
|
|
n, ok := c.Next()
|
|
require.False(t, ok)
|
|
require.Nil(t, n)
|
|
|
|
// parent is the message
|
|
parent, ok := c.Parent().(*proto.Message)
|
|
require.True(t, ok)
|
|
require.True(t, parent.Name == "World")
|
|
// currently useless.
|
|
require.True(t, c.Name() == elements)
|
|
}
|
|
|
|
// Don't make sense for elements not contained in a slice (currently
|
|
// proto.Proto or comments)
|
|
if _, ok := n.(*proto.Proto); ok {
|
|
require.Panics(t, func() { c.IsLast() })
|
|
require.Panics(t, func() { c.Next() })
|
|
}
|
|
return true
|
|
})
|
|
}
|
|
|
|
// Also test the utilities here.
|
|
|
|
func TestAddImports(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"`)
|
|
require.NoError(t, err)
|
|
|
|
// Add an import
|
|
err = AddImports(f, true, NewImport("this.proto"))
|
|
require.NoError(t, err)
|
|
require.True(t, HasImport(f, "this.proto"))
|
|
// Note: added in reverse order.
|
|
err = AddImports(f, true,
|
|
NewImport("that.proto"),
|
|
NewImport("the.other.proto"),
|
|
NewImport("and.another.proto"),
|
|
)
|
|
require.NoError(t, err)
|
|
require.True(t, HasImport(f, "that.proto"))
|
|
require.True(t, HasImport(f, "the.other.proto"))
|
|
require.True(t, HasImport(f, "and.another.proto"))
|
|
|
|
// Empty import is no-op.
|
|
require.NoError(t, AddImports(f, true))
|
|
// Importing on empty file is currently an error.
|
|
require.Error(t, AddImports(
|
|
&proto.Proto{},
|
|
true,
|
|
NewImport("this.proto"),
|
|
))
|
|
|
|
// Exercise the recursive case:
|
|
f, err = parseStringProto(`syntax = "proto3"`)
|
|
require.NoError(t, err)
|
|
err = AddImports(f, true,
|
|
NewImport("this.proto"),
|
|
NewImport("that.proto"),
|
|
)
|
|
require.NoError(t, err)
|
|
require.True(t, HasImport(f, "this.proto"))
|
|
require.True(t, HasImport(f, "that.proto"))
|
|
|
|
f, err = parseStringProto(`syntax = "proto3";
|
|
package cosmonaut.chainname.chainname;
|
|
|
|
import "gogoproto/gogo.proto";
|
|
import "google/api/annotations.proto";
|
|
import "cosmos/base/query/v1beta1/pagination.proto";
|
|
import "chainname/params.proto";
|
|
`)
|
|
require.NoError(t, err)
|
|
err = AddImports(f, true, NewImport("chainname/bleep.proto"))
|
|
require.NoError(t, err)
|
|
// Add dupes:
|
|
err = AddImports(f, true, NewImport("chainname/bleep.proto"))
|
|
require.NoError(t, err)
|
|
err = AddImports(f, true, NewImport("chainname/bleep.proto"))
|
|
require.NoError(t, err)
|
|
err = AddImports(f, true, NewImport("chainname/params.proto"))
|
|
require.NoError(t, err)
|
|
// just checking that is added last.
|
|
// fmt.Print(Printer(f))
|
|
|
|
// Check that adding duplicates does nothing.
|
|
f, err = parseStringProto(`syntax = "proto3";
|
|
package cosmonaut.chainname.chainname;
|
|
|
|
import "gogoproto/gogo.proto";
|
|
import "google/api/annotations.proto";
|
|
import "cosmos/base/query/v1beta1/pagination.proto";
|
|
import "chainname/params.proto";
|
|
`)
|
|
require.NoError(t, err)
|
|
imports := []*proto.Import{
|
|
NewImport("chainname/params.proto"),
|
|
NewImport("gogoproto/gogo.proto"),
|
|
}
|
|
err = AddImports(f, true, imports...)
|
|
require.NoError(t, err)
|
|
require.Equal(t, len(f.Elements), 6, "The number of elements shouldn't have changed")
|
|
|
|
// Pass an empty import list.
|
|
f, err = parseStringProto(`syntax = "proto3";`)
|
|
require.NoError(t, err)
|
|
ret := AddImports(f, true)
|
|
require.Nil(t, ret)
|
|
|
|
// No imports, no fallback.
|
|
f, err = parseStringProto(`syntax = "proto3";`)
|
|
require.NoError(t, err)
|
|
err = AddImports(f, false, NewImport("this.proto"))
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestHasImport(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
import "this.proto";
|
|
import "that.proto";
|
|
import "the.other.proto"
|
|
`)
|
|
require.NoError(t, err)
|
|
require.True(t, HasImport(f, "this.proto"))
|
|
require.True(t, HasImport(f, "that.proto"))
|
|
require.True(t, HasImport(f, "the.other.proto"))
|
|
require.False(t, HasImport(f, "this.proto.proto"))
|
|
}
|
|
|
|
func TestGetMessage(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {
|
|
message World {
|
|
message WeComeInPeace {
|
|
message TheAnswerToLifeTheUniverseAndEverything {
|
|
message IsActuallyFortyTwo {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
m, err := GetMessageByName(f, "Hello")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Hello", m.Name)
|
|
|
|
m, err = GetMessageByName(f, "World")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "World", m.Name)
|
|
|
|
m, err = GetMessageByName(f, "WeComeInPeace")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "WeComeInPeace", m.Name)
|
|
|
|
m, err = GetMessageByName(f, "TheAnswerToLifeTheUniverseAndEverything")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "TheAnswerToLifeTheUniverseAndEverything", m.Name)
|
|
|
|
m, err = GetMessageByName(f, "IsActuallyFortyTwo")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "IsActuallyFortyTwo", m.Name)
|
|
|
|
_, err = GetMessageByName(f, "DoesNotExist")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestHasMessage(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {
|
|
message World {
|
|
message WeComeInPeace {
|
|
message TheAnswerToLifeTheUniverseAndEverything {
|
|
message IsActuallyFortyTwo {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.True(t, HasMessage(f, "Hello"))
|
|
require.True(t, HasMessage(f, "World"))
|
|
require.True(t, HasMessage(f, "WeComeInPeace"))
|
|
require.True(t, HasMessage(f, "TheAnswerToLifeTheUniverseAndEverything"))
|
|
require.True(t, HasMessage(f, "IsActuallyFortyTwo"))
|
|
require.False(t, HasMessage(f, "DoesNotExist"))
|
|
require.False(t, HasMessage(f, "Hello.World"))
|
|
}
|
|
|
|
func TestGetService(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
service Msg {
|
|
}
|
|
service AnotherMsg {}
|
|
service YetAnotherMsg {
|
|
rpc Foo(Bar) returns (Bar) {}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
s, err := GetServiceByName(f, "Msg")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "Msg", s.Name)
|
|
|
|
s, err = GetServiceByName(f, "AnotherMsg")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "AnotherMsg", s.Name)
|
|
|
|
s, err = GetServiceByName(f, "YetAnotherMsg")
|
|
require.NoError(t, err)
|
|
require.Equal(t, "YetAnotherMsg", s.Name)
|
|
|
|
_, err = GetServiceByName(f, "DoesNotExist")
|
|
require.Error(t, err)
|
|
}
|
|
|
|
func TestHasService(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
service Msg {}
|
|
service AnotherMsg {}
|
|
service YetAnotherMsg {}
|
|
`)
|
|
require.NoError(t, err)
|
|
require.True(t, HasService(f, "Msg"))
|
|
require.True(t, HasService(f, "AnotherMsg"))
|
|
require.True(t, HasService(f, "YetAnotherMsg"))
|
|
require.False(t, HasService(f, "DoesNotExist"))
|
|
}
|
|
|
|
func TestGetNextId(t *testing.T) {
|
|
f, err := parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {
|
|
string g = 1;
|
|
message World {
|
|
message WeComeInPeace {
|
|
message TheAnswerToLifeTheUniverseAndEverything {
|
|
message IsActuallyFortyTwo {
|
|
string foo = 1;
|
|
int32 bar = 2;
|
|
int64 baz = 3;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`)
|
|
require.NoError(t, err)
|
|
|
|
m, err := GetMessageByName(f, "IsActuallyFortyTwo")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 4, NextUniqueID(m))
|
|
|
|
m, err = GetMessageByName(f, "Hello")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 2, NextUniqueID(m))
|
|
|
|
f, err = parseStringProto(`syntax = "proto3"
|
|
|
|
message Hello {
|
|
string g = 1;
|
|
string foo = 2;
|
|
int32 bar = 3;
|
|
int64 baz = 5;
|
|
}`)
|
|
require.NoError(t, err)
|
|
m, err = GetMessageByName(f, "Hello")
|
|
require.NoError(t, err)
|
|
require.Equal(t, 6, NextUniqueID(m))
|
|
}
|