Files
atomctl/specs/002-refactor-ast-gen/contracts/route_parser_test.go
2025-09-22 14:16:22 +08:00

262 lines
8.3 KiB
Go

package contracts
import (
"testing"
"github.com/stretchr/testify/assert"
)
// RouteParserContract defines the contract tests for RouteParser implementations
type RouteParserContract struct {
parser RouteParser
}
// RouteParser interface definition for contract testing
type RouteParser interface {
ParseFile(filePath string) ([]RouteDefinition, error)
ParseDir(dirPath string) ([]RouteDefinition, error)
ParseString(code string) ([]RouteDefinition, error)
SetConfig(config *RouteParserConfig) error
GetConfig() *RouteParserConfig
GetContext() *ParserContext
GetDiagnostics() []Diagnostic
}
// RouteDefinition represents a route definition (simplified for testing)
type RouteDefinition struct {
StructName string
Path string
Methods []string
Parameters []ParamDefinition
Imports map[string]string
}
// ParamDefinition represents a parameter definition (simplified for testing)
type ParamDefinition struct {
Name string
Position string
Type string
}
// RouteParserConfig represents parser configuration (simplified for testing)
type RouteParserConfig struct {
StrictMode bool
ParseComments bool
SourceLocations bool
}
// ParserContext represents parser context (simplified for testing)
type ParserContext struct {
WorkingDir string
ModuleName string
}
// Diagnostic represents diagnostic information (simplified for testing)
type Diagnostic struct {
Level string
Message string
File string
}
// NewRouteParserContract creates a new contract test instance
func NewRouteParserContract(parser RouteParser) *RouteParserContract {
return &RouteParserContract{parser: parser}
}
// TestSuite runs all contract tests for RouteParser
func (c *RouteParserContract) TestSuite(t *testing.T) {
t.Run("RouteParser_ParseFile_BasicRoute", c.testParseFileBasicRoute)
t.Run("RouteParser_ParseFile_WithParameters", c.testParseFileWithParameters)
t.Run("RouteParser_ParseFile_InvalidSyntax", c.testParseFileInvalidSyntax)
t.Run("RouteParser_ParseFile_NonexistentFile", c.testParseFileNonexistentFile)
t.Run("RouteParser_ParseString_SimpleRoute", c.testParseStringSimpleRoute)
t.Run("RouteParser_ParseString_MultipleRoutes", c.testParseStringMultipleRoutes)
t.Run("RouteParser_ParseDir_EmptyDirectory", c.testParseDirEmptyDirectory)
t.Run("RouteParser_Configuration", c.testConfiguration)
t.Run("RouteParser_Diagnostics", c.testDiagnostics)
}
// Contract Tests
func (c *RouteParserContract) testParseFileBasicRoute(t *testing.T) {
// GIVEN a Go file with basic route annotation
filePath := "testdata/basic_route.go"
// WHEN parsing the file
routes, err := c.parser.ParseFile(filePath)
// THEN it should succeed and return the route definition
assert.NoError(t, err, "ParseFile should not error")
assert.Len(t, routes, 1, "Should find exactly one route")
route := routes[0]
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")
assert.Empty(t, route.Parameters, "Basic route should have no parameters")
}
func (c *RouteParserContract) testParseFileWithParameters(t *testing.T) {
// GIVEN a Go file with route containing parameters
filePath := "testdata/params_route.go"
// WHEN parsing the file
routes, err := c.parser.ParseFile(filePath)
// THEN it should succeed and extract parameters
assert.NoError(t, err, "ParseFile should not error")
assert.Len(t, routes, 1, "Should find exactly one route")
route := routes[0]
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 *RouteParserContract) testParseFileInvalidSyntax(t *testing.T) {
// GIVEN a Go file with invalid syntax
filePath := "testdata/invalid_syntax.go"
// WHEN parsing the file
routes, err := c.parser.ParseFile(filePath)
// THEN it should fail with appropriate error
assert.Error(t, err, "ParseFile should error on invalid syntax")
assert.Empty(t, routes, "Should return no routes on error")
// Verify diagnostic information
diagnostics := c.parser.GetDiagnostics()
assert.NotEmpty(t, diagnostics, "Should provide diagnostic information")
assert.Contains(t, diagnostics[0].Message, "syntax", "Error message should mention syntax")
}
func (c *RouteParserContract) testParseFileNonexistentFile(t *testing.T) {
// GIVEN a nonexistent file path
filePath := "testdata/nonexistent.go"
// WHEN parsing the file
routes, err := c.parser.ParseFile(filePath)
// THEN it should fail with file not found error
assert.Error(t, err, "ParseFile should error on nonexistent file")
assert.Empty(t, routes, "Should return no routes on error")
}
func (c *RouteParserContract) testParseStringSimpleRoute(t *testing.T) {
// GIVEN Go code string with simple route
code := `
package main
// @Router /users [get]
type UserController struct {}
`
// WHEN parsing the string
routes, err := c.parser.ParseString(code)
// THEN it should succeed and return the route
assert.NoError(t, err, "ParseString should not error")
assert.Len(t, routes, 1, "Should find exactly one route")
route := routes[0]
assert.Equal(t, "UserController", route.StructName, "Should extract correct struct name")
assert.Equal(t, "/users", route.Path, "Should extract correct path")
}
func (c *RouteParserContract) testParseStringMultipleRoutes(t *testing.T) {
// GIVEN Go code string with multiple routes
code := `
package main
// @Router /users [get]
type UserController struct {}
// @Router /products [post]
type ProductController struct {}
`
// WHEN parsing the string
routes, err := c.parser.ParseString(code)
// THEN it should succeed and return all routes
assert.NoError(t, err, "ParseString should not error")
assert.Len(t, routes, 2, "Should find exactly two routes")
// Verify both routes are found
routeNames := []string{routes[0].StructName, routes[1].StructName}
assert.Contains(t, routeNames, "UserController", "Should find UserController")
assert.Contains(t, routeNames, "ProductController", "Should find ProductController")
}
func (c *RouteParserContract) testParseDirEmptyDirectory(t *testing.T) {
// GIVEN an empty directory
dirPath := "testdata/empty"
// WHEN parsing the directory
routes, err := c.parser.ParseDir(dirPath)
// THEN it should succeed with no routes
assert.NoError(t, err, "ParseDir should not error on empty directory")
assert.Empty(t, routes, "Should return no routes from empty directory")
}
func (c *RouteParserContract) testConfiguration(t *testing.T) {
// GIVEN default configuration
config := c.parser.GetConfig()
assert.NotNil(t, config, "Should have default configuration")
// WHEN setting new configuration
newConfig := &RouteParserConfig{
StrictMode: true,
ParseComments: false,
SourceLocations: true,
}
err := c.parser.SetConfig(newConfig)
// THEN configuration should be updated
assert.NoError(t, err, "SetConfig should not error")
updatedConfig := c.parser.GetConfig()
assert.True(t, updatedConfig.StrictMode, "StrictMode should be updated")
assert.False(t, updatedConfig.ParseComments, "ParseComments should be updated")
assert.True(t, updatedConfig.SourceLocations, "SourceLocations should be updated")
}
func (c *RouteParserContract) testDiagnostics(t *testing.T) {
// GIVEN a file with warnings
filePath := "testdata/warnings.go"
// WHEN parsing the file
_, err := c.parser.ParseFile(filePath)
// THEN diagnostics should be available
diagnostics := c.parser.GetDiagnostics()
assert.NotEmpty(t, diagnostics, "Should provide diagnostic information")
// Verify diagnostic structure
for _, diag := range diagnostics {
assert.NotEmpty(t, diag.Level, "Diagnostic should have level")
assert.NotEmpty(t, diag.Message, "Diagnostic should have message")
}
}
// Helper functions
func findParameterByPosition(params []ParamDefinition, position string) *ParamDefinition {
for _, param := range params {
if param.Position == position {
return &param
}
}
return nil
}