240 lines
7.6 KiB
Go
240 lines
7.6 KiB
Go
package contracts
|
|
|
|
import (
|
|
"go/ast"
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// RouteBuilderContract defines the contract tests for RouteBuilder implementations
|
|
type RouteBuilderContract struct {
|
|
builder RouteBuilder
|
|
}
|
|
|
|
// RouteBuilder interface definition for contract testing
|
|
type RouteBuilder interface {
|
|
BuildFromTypeSpec(typeSpec *ast.TypeSpec, decl *ast.GenDecl, context *BuilderContext) (RouteDefinition, error)
|
|
BuildFromComment(comment string, context *BuilderContext) (RouteDefinition, error)
|
|
ValidateDefinition(def *RouteDefinition) error
|
|
}
|
|
|
|
// BuilderContext represents builder context (simplified for testing)
|
|
type BuilderContext struct {
|
|
FilePath string
|
|
PackageName string
|
|
ImportContext *ImportContext
|
|
ASTFile *ast.File
|
|
}
|
|
|
|
// ImportContext represents import context (simplified for testing)
|
|
type ImportContext struct {
|
|
Imports map[string]string
|
|
}
|
|
|
|
// NewRouteBuilderContract creates a new contract test instance
|
|
func NewRouteBuilderContract(builder RouteBuilder) *RouteBuilderContract {
|
|
return &RouteBuilderContract{builder: builder}
|
|
}
|
|
|
|
// TestSuite runs all contract tests for RouteBuilder
|
|
func (c *RouteBuilderContract) TestSuite(t *testing.T) {
|
|
t.Run("RouteBuilder_BuildFromTypeSpec_BasicRoute", c.testBuildFromTypeSpecBasicRoute)
|
|
t.Run("RouteBuilder_BuildFromTypeSpec_WithParameters", c.testBuildFromTypeSpecWithParameters)
|
|
t.Run("RouteBuilder_BuildFromTypeSpec_InvalidInput", c.testBuildFromTypeSpecInvalidInput)
|
|
t.Run("RouteBuilder_BuildFromComment_SimpleComment", c.testBuildFromCommentSimpleComment)
|
|
t.Run("RouteBuilder_BuildFromComment_ComplexComment", c.testBuildFromCommentComplexComment)
|
|
t.Run("RouteBuilder_ValidateDefinition_ValidRoute", c.testValidateDefinitionValidRoute)
|
|
t.Run("RouteBuilder_ValidateDefinition_InvalidRoute", c.testValidateDefinitionInvalidRoute)
|
|
}
|
|
|
|
// Contract Tests
|
|
|
|
func (c *RouteBuilderContract) testBuildFromTypeSpecBasicRoute(t *testing.T) {
|
|
// GIVEN a valid type specification and declaration
|
|
typeSpec := &ast.TypeSpec{
|
|
Name: &ast.Ident{Name: "UserController"},
|
|
}
|
|
|
|
decl := &ast.GenDecl{
|
|
Doc: &ast.CommentGroup{
|
|
List: []*ast.Comment{
|
|
{Text: "// @Router /users [get]"},
|
|
},
|
|
},
|
|
Specs: []ast.Spec{typeSpec},
|
|
}
|
|
|
|
context := &BuilderContext{
|
|
FilePath: "UserController.go",
|
|
PackageName: "controllers",
|
|
ImportContext: &ImportContext{
|
|
Imports: make(map[string]string),
|
|
},
|
|
}
|
|
|
|
// WHEN building route from type spec
|
|
route, err := c.builder.BuildFromTypeSpec(typeSpec, decl, context)
|
|
|
|
// THEN it should succeed and return valid route definition
|
|
assert.NoError(t, err, "BuildFromTypeSpec should not error")
|
|
assert.NotNil(t, route, "Should return route definition")
|
|
|
|
assert.Equal(t, "UserController", route.StructName, "Should extract correct struct name")
|
|
assert.Equal(t, "/users", route.Path, "Should extract correct path")
|
|
assert.Contains(t, route.Methods, "GET", "Should include GET method")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testBuildFromTypeSpecWithParameters(t *testing.T) {
|
|
// GIVEN a type specification with parameter bindings
|
|
typeSpec := &ast.TypeSpec{
|
|
Name: &ast.Ident{Name: "UserController"},
|
|
}
|
|
|
|
decl := &ast.GenDecl{
|
|
Doc: &ast.CommentGroup{
|
|
List: []*ast.Comment{
|
|
{Text: "// @Router /users/:id [get]"},
|
|
{Text: "// @Bind id (path) model()"},
|
|
{Text: "// @Bind limit (query) model(limit:int)"},
|
|
},
|
|
},
|
|
Specs: []ast.Spec{typeSpec},
|
|
}
|
|
|
|
context := &BuilderContext{
|
|
FilePath: "UserController.go",
|
|
PackageName: "controllers",
|
|
ImportContext: &ImportContext{
|
|
Imports: map[string]string{
|
|
"model": "go.ipao.vip/gen/model",
|
|
},
|
|
},
|
|
}
|
|
|
|
// WHEN building route from type spec
|
|
route, err := c.builder.BuildFromTypeSpec(typeSpec, decl, context)
|
|
|
|
// THEN it should succeed and extract parameters
|
|
assert.NoError(t, err, "BuildFromTypeSpec should not error")
|
|
assert.NotNil(t, route, "Should return route definition")
|
|
|
|
assert.NotEmpty(t, route.Parameters, "Route should have parameters")
|
|
|
|
// Verify path parameter
|
|
pathParam := findParameterByPosition(route.Parameters, "path")
|
|
assert.NotNil(t, pathParam, "Should find path parameter")
|
|
assert.Equal(t, "id", pathParam.Name, "Path parameter should be named 'id'")
|
|
|
|
// Verify query parameter
|
|
queryParam := findParameterByPosition(route.Parameters, "query")
|
|
assert.NotNil(t, queryParam, "Should find query parameter")
|
|
assert.Equal(t, "limit", queryParam.Name, "Query parameter should be named 'limit'")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testBuildFromTypeSpecInvalidInput(t *testing.T) {
|
|
// GIVEN an invalid type specification (nil)
|
|
var typeSpec *ast.TypeSpec = nil
|
|
decl := &ast.GenDecl{}
|
|
context := &BuilderContext{}
|
|
|
|
// WHEN building route from invalid type spec
|
|
route, err := c.builder.BuildFromTypeSpec(typeSpec, decl, context)
|
|
|
|
// THEN it should fail with appropriate error
|
|
assert.Error(t, err, "BuildFromTypeSpec should error on invalid input")
|
|
assert.Equal(t, RouteDefinition{}, route, "Should return empty route on error")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testBuildFromCommentSimpleComment(t *testing.T) {
|
|
// GIVEN a simple route comment
|
|
comment := `@Router /users [get]`
|
|
context := &BuilderContext{
|
|
FilePath: "UserController.go",
|
|
PackageName: "controllers",
|
|
}
|
|
|
|
// WHEN building route from comment
|
|
route, err := c.builder.BuildFromComment(comment, context)
|
|
|
|
// THEN it should succeed and return valid route
|
|
assert.NoError(t, err, "BuildFromComment should not error")
|
|
assert.NotNil(t, route, "Should return route definition")
|
|
|
|
assert.Equal(t, "/users", route.Path, "Should extract correct path")
|
|
assert.Contains(t, route.Methods, "GET", "Should include GET method")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testBuildFromCommentComplexComment(t *testing.T) {
|
|
// GIVEN a complex route comment with parameters
|
|
comment := `@Router /users/:id [get,put]
|
|
@Bind id (path) model()
|
|
@Bind user (body) model(User)`
|
|
context := &BuilderContext{
|
|
FilePath: "UserController.go",
|
|
PackageName: "controllers",
|
|
ImportContext: &ImportContext{
|
|
Imports: map[string]string{
|
|
"model": "go.ipao.vip/gen/model",
|
|
},
|
|
},
|
|
}
|
|
|
|
// WHEN building route from comment
|
|
route, err := c.builder.BuildFromComment(comment, context)
|
|
|
|
// THEN it should succeed and extract all information
|
|
assert.NoError(t, err, "BuildFromComment should not error")
|
|
assert.NotNil(t, route, "Should return route definition")
|
|
|
|
assert.Equal(t, "/users/:id", route.Path, "Should extract correct path")
|
|
assert.Contains(t, route.Methods, "GET", "Should include GET method")
|
|
assert.Contains(t, route.Methods, "PUT", "Should include PUT method")
|
|
|
|
assert.NotEmpty(t, route.Parameters, "Route should have parameters")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testValidateDefinitionValidRoute(t *testing.T) {
|
|
// GIVEN a valid route definition
|
|
route := RouteDefinition{
|
|
StructName: "UserController",
|
|
Path: "/users",
|
|
Methods: []string{"GET", "POST"},
|
|
Parameters: []ParamDefinition{
|
|
{Name: "id", Position: "path", Type: "int"},
|
|
},
|
|
}
|
|
|
|
// WHEN validating the route definition
|
|
err := c.builder.ValidateDefinition(&route)
|
|
|
|
// THEN it should succeed
|
|
assert.NoError(t, err, "ValidateDefinition should not error on valid route")
|
|
}
|
|
|
|
func (c *RouteBuilderContract) testValidateDefinitionInvalidRoute(t *testing.T) {
|
|
// GIVEN an invalid route definition (empty path)
|
|
route := RouteDefinition{
|
|
StructName: "UserController",
|
|
Path: "", // Empty path is invalid
|
|
Methods: []string{"GET"},
|
|
}
|
|
|
|
// WHEN validating the route definition
|
|
err := c.builder.ValidateDefinition(&route)
|
|
|
|
// THEN it should fail
|
|
assert.Error(t, err, "ValidateDefinition should error on invalid route")
|
|
}
|
|
|
|
// Helper functions
|
|
|
|
func findParameterByPosition(params []ParamDefinition, position string) *ParamDefinition {
|
|
for _, param := range params {
|
|
if param.Position == position {
|
|
return ¶m
|
|
}
|
|
}
|
|
return nil
|
|
}
|