You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
go-zero/tools/goctl/api/parser/g4/test/apiparser_test.go

370 lines
7.3 KiB
Go

package test
import (
"fmt"
"os"
"path/filepath"
"testing"
"github.com/stretchr/testify/assert"
"github.com/zeromicro/go-zero/tools/goctl/api/parser/g4/ast"
"github.com/zeromicro/go-zero/tools/goctl/util/pathx"
)
var (
normalAPI = `
syntax="v1"
info (
foo: bar
)
type Foo {
Bar int
}
@server(
foo: bar
)
service foo-api{
@doc("foo")
@handler foo
post /foo (Foo) returns ([]int)
}
`
missDeclarationAPI = `
@server(
foo: bar
)
service foo-api{
@doc("foo")
@handler foo
post /foo (Foo) returns (Foo)
}
`
missDeclarationInArrayAPI = `
@server(
foo: bar
)
service foo-api{
@doc("foo")
@handler foo
post /foo returns ([]Foo)
}
`
missDeclarationInArrayAPI2 = `
@server(
foo: bar
)
service foo-api{
@doc("foo")
@handler foo
post /foo returns ([]*Foo)
}
`
nestedAPIImport = `
import "foo.api"
`
ambiguousSyntax = `
syntax = "v2"
`
ambiguousService = `
service bar-api{
@handler foo
post /foo
}
`
duplicateHandler = `
service bar-api{
@handler foo
post /foo
}
`
duplicateRoute = `
service bar-api{
@handler bar
post /foo
}
`
duplicateType = `
type Foo int
`
)
func TestApiParser(t *testing.T) {
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationAPI)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayAPI)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("missDeclarationAPI", func(t *testing.T) {
_, err := parser.ParseContent(missDeclarationInArrayAPI2)
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("nestedImport", func(t *testing.T) {
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err := os.WriteFile(file, []byte(nestedAPIImport), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`import "%s"`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("duplicateImport", func(t *testing.T) {
_, err := parser.ParseContent(`
import "foo.api"
import "foo.api"
`)
assert.Error(t, err)
})
t.Run("duplicateKey", func(t *testing.T) {
_, err := parser.ParseContent(`
info (
foo: bar
foo: bar
)
`)
assert.Error(t, err)
})
t.Run("ambiguousSyntax", func(t *testing.T) {
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err := os.WriteFile(file, []byte(ambiguousSyntax), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
syntax = "v1"
import "%s"`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("ambiguousSyntax", func(t *testing.T) {
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err := os.WriteFile(file, []byte(ambiguousSyntax), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
syntax = "v1"
import "%s"`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("ambiguousService", func(t *testing.T) {
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err := os.WriteFile(file, []byte(ambiguousService), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
import "%s"
service foo-api{
@handler foo
post /foo
}
`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("duplicateHandler", func(t *testing.T) {
_, err := parser.ParseContent(`
service foo-api{
@handler foo
post /foo
@handler foo
post /bar
}
`)
assert.Error(t, err)
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err = os.WriteFile(file, []byte(duplicateHandler), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
import "%s"
service bar-api{
@handler foo
post /foo
}
`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("duplicateRoute", func(t *testing.T) {
_, err := parser.ParseContent(`
service foo-api{
@handler foo
post /foo
@handler bar
post /foo
}
`)
assert.Error(t, err)
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err = os.WriteFile(file, []byte(duplicateRoute), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
import "%s"
service bar-api{
@handler foo
post /foo
}
`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("duplicateType", func(t *testing.T) {
_, err := parser.ParseContent(`
type Foo int
type Foo bool
`)
assert.Error(t, err)
file := filepath.Join(pathx.MustTempDir(), "foo.api")
err = os.WriteFile(file, []byte(duplicateType), os.ModePerm)
if err != nil {
return
}
_, err = parser.ParseContent(fmt.Sprintf(`
import "%s"
type Foo bool
`, file))
assert.Error(t, err)
fmt.Printf("%+v\n", err)
})
t.Run("normal", func(t *testing.T) {
v, err := parser.ParseContent(normalAPI)
assert.Nil(t, err)
body := &ast.Body{
Lp: ast.NewTextExpr("("),
Rp: ast.NewTextExpr(")"),
Name: &ast.Literal{Literal: ast.NewTextExpr("Foo")},
}
assert.True(t, v.Equal(&ast.Api{
Syntax: &ast.SyntaxExpr{
Syntax: ast.NewTextExpr("syntax"),
Assign: ast.NewTextExpr("="),
Version: ast.NewTextExpr(`"v1"`),
},
Info: &ast.InfoExpr{
Info: ast.NewTextExpr("info"),
Lp: ast.NewTextExpr("("),
Rp: ast.NewTextExpr(")"),
Kvs: []*ast.KvExpr{
{
Key: ast.NewTextExpr("foo"),
Value: ast.NewTextExpr("bar"),
},
},
},
Type: []ast.TypeExpr{
&ast.TypeStruct{
Name: ast.NewTextExpr("Foo"),
LBrace: ast.NewTextExpr("{"),
RBrace: ast.NewTextExpr("}"),
Fields: []*ast.TypeField{
{
Name: ast.NewTextExpr("Bar"),
DataType: &ast.Literal{Literal: ast.NewTextExpr("int")},
},
},
},
},
Service: []*ast.Service{
{
AtServer: &ast.AtServer{
AtServerToken: ast.NewTextExpr("@server"),
Lp: ast.NewTextExpr("("),
Rp: ast.NewTextExpr(")"),
Kv: []*ast.KvExpr{
{
Key: ast.NewTextExpr("foo"),
Value: ast.NewTextExpr("bar"),
},
},
},
ServiceApi: &ast.ServiceApi{
ServiceToken: ast.NewTextExpr("service"),
Name: ast.NewTextExpr("foo-api"),
Lbrace: ast.NewTextExpr("{"),
Rbrace: ast.NewTextExpr("}"),
ServiceRoute: []*ast.ServiceRoute{
{
AtDoc: &ast.AtDoc{
AtDocToken: ast.NewTextExpr("@doc"),
Lp: ast.NewTextExpr("("),
Rp: ast.NewTextExpr(")"),
LineDoc: ast.NewTextExpr(`"foo"`),
},
AtHandler: &ast.AtHandler{
AtHandlerToken: ast.NewTextExpr("@handler"),
Name: ast.NewTextExpr("foo"),
},
Route: &ast.Route{
Method: ast.NewTextExpr("post"),
Path: ast.NewTextExpr("/foo"),
Req: body,
ReturnToken: ast.NewTextExpr("returns"),
Reply: &ast.Body{
Lp: ast.NewTextExpr("("),
Rp: ast.NewTextExpr(")"),
Name: &ast.Array{
ArrayExpr: ast.NewTextExpr("[]int"),
LBrack: ast.NewTextExpr("["),
RBrack: ast.NewTextExpr("]"),
Literal: &ast.Literal{Literal: ast.NewTextExpr("int")},
},
},
},
},
},
},
},
},
}))
})
}