Files
atomctl/pkg/ast/provider/validator_test.go
Rogee e1f83ae469 feat: 重构 pkg/ast/provider 模块,优化代码组织逻辑和功能实现
## 主要改进

### 架构重构
- 将单体 provider.go 拆分为多个专门的模块文件
- 实现了清晰的职责分离和模块化设计
- 遵循 SOLID 原则,提高代码可维护性

### 新增功能
- **验证规则系统**: 实现了完整的 provider 验证框架
- **报告生成器**: 支持多种格式的验证报告 (JSON/HTML/Markdown/Text)
- **解析器优化**: 重新设计了解析流程,提高性能和可扩展性
- **错误处理**: 增强了错误处理和诊断能力

### 修复关键 Bug
- 修复 @provider(job) 注解缺失 __job 注入参数的问题
- 统一了 job 和 cronjob 模式的处理逻辑
- 确保了 provider 生成的正确性和一致性

### 代码质量提升
- 添加了完整的测试套件
- 引入了 golangci-lint 代码质量检查
- 优化了代码格式和结构
- 增加了详细的文档和规范

### 文件结构优化
```
pkg/ast/provider/
├── types.go              # 类型定义
├── parser.go             # 解析器实现
├── validator.go          # 验证规则
├── report_generator.go   # 报告生成
├── renderer.go           # 渲染器
├── comment_parser.go     # 注解解析
├── modes.go             # 模式定义
├── errors.go            # 错误处理
└── validator_test.go    # 测试文件
```

### 兼容性
- 保持向后兼容性
- 支持现有的所有 provider 模式
- 优化了 API 设计和用户体验

This completes the implementation of T025-T029 tasks following TDD principles,
including validation rules implementation and critical bug fixes.
2025-09-19 18:58:30 +08:00

310 lines
7.1 KiB
Go

package provider
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestGoValidator_Basic(t *testing.T) {
validator := NewGoValidator()
// Test with a valid provider
validProvider := &Provider{
StructName: "UserService",
ReturnType: "UserService",
Mode: ProviderModeBasic,
PkgName: "services",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "DB",
Package: "database/sql",
PackageAlias: "sql",
},
},
Imports: map[string]string{
"sql": "database/sql",
},
Location: SourceLocation{
File: "user_service.go",
Line: 10,
Column: 1,
},
}
err := validator.Validate(validProvider)
assert.NoError(t, err, "Valid provider should not return error")
// Test with an invalid provider
invalidProvider := &Provider{
StructName: "", // Empty struct name
ReturnType: "",
Mode: "invalid-mode",
PkgName: "",
ProviderFile: "",
InjectParams: map[string]InjectParam{},
Imports: map[string]string{},
}
err = validator.Validate(invalidProvider)
assert.Error(t, err, "Invalid provider should return error")
}
func TestGoValidator_ValidateAll(t *testing.T) {
validator := NewGoValidator()
providers := []*Provider{
{
StructName: "UserService",
ReturnType: "UserService",
Mode: ProviderModeBasic,
PkgName: "services",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "DB",
Package: "database/sql",
PackageAlias: "sql",
},
},
Imports: map[string]string{
"sql": "database/sql",
},
},
{
StructName: "JobProcessor",
ReturnType: "JobProcessor",
Mode: ProviderModeJob,
PkgName: "jobs",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"__job": {
Type: "Job",
},
},
Imports: map[string]string{},
},
{
StructName: "", // Invalid
ReturnType: "",
Mode: "invalid-mode",
PkgName: "",
ProviderFile: "",
InjectParams: map[string]InjectParam{},
Imports: map[string]string{},
},
}
report := validator.ValidateAll(providers)
assert.NotNil(t, report)
assert.Equal(t, 3, report.TotalProviders)
assert.Equal(t, 1, report.ValidCount)
assert.Equal(t, 2, report.InvalidCount)
assert.False(t, report.IsValid)
assert.Len(t, report.Errors, 4)
}
func TestGoValidator_ReportGeneration(t *testing.T) {
validator := NewGoValidator()
providers := []*Provider{
{
StructName: "UserService",
ReturnType: "UserService",
Mode: ProviderModeBasic,
PkgName: "services",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "DB",
Package: "database/sql",
PackageAlias: "sql",
},
},
Imports: map[string]string{
"sql": "database/sql",
},
},
}
// Test text report generation
report, err := validator.GenerateReport(providers, ReportFormatText)
assert.NoError(t, err)
assert.Contains(t, report, "Provider Validation Report")
assert.Contains(t, report, "Total Providers: 1")
assert.Contains(t, report, "Valid Providers: 1")
// Test JSON report generation
jsonReport, err := validator.GenerateReport(providers, ReportFormatJSON)
assert.NoError(t, err)
assert.Contains(t, jsonReport, `"total_providers": 1`)
assert.Contains(t, jsonReport, `"valid_count": 1`)
// Test Markdown report generation
markdownReport, err := validator.GenerateReport(providers, ReportFormatMarkdown)
assert.NoError(t, err)
assert.Contains(t, markdownReport, "# Provider Validation Report")
assert.Contains(t, markdownReport, "**Total Providers:** 1")
}
func TestValidationReport_Statistics(t *testing.T) {
validator := NewGoValidator()
providers := []*Provider{
{
StructName: "UserService",
ReturnType: "UserService",
Mode: ProviderModeBasic,
PkgName: "services",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "DB",
Package: "database/sql",
PackageAlias: "sql",
},
},
Imports: map[string]string{
"sql": "database/sql",
},
},
{
StructName: "JobProcessor",
ReturnType: "JobProcessor",
Mode: ProviderModeJob,
PkgName: "jobs",
ProviderFile: "providers.gen.go",
InjectParams: map[string]InjectParam{
"__job": {
Type: "Job",
},
},
Imports: map[string]string{},
},
}
report := validator.ValidateWithDetails(providers)
assert.NotNil(t, report.Statistics)
assert.Len(t, report.Statistics.ProvidersByMode, 2)
assert.Contains(t, report.Statistics.ProvidersByMode, "basic")
assert.Contains(t, report.Statistics.ProvidersByMode, "job")
}
func TestStructNameRule(t *testing.T) {
rule := &StructNameRule{}
tests := []struct {
name string
provider *Provider
expectError bool
}{
{
name: "Valid struct name",
provider: &Provider{
StructName: "UserService",
},
expectError: false,
},
{
name: "Empty struct name",
provider: &Provider{
StructName: "",
},
expectError: true,
},
{
name: "Unexported struct name",
provider: &Provider{
StructName: "userService",
},
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := rule.Validate(tt.provider)
if tt.expectError {
assert.NotNil(t, err, "Expected validation error but got nil")
} else {
assert.Nil(t, err, "Expected no validation error but got one")
}
})
}
}
func TestInjectionParamsRule(t *testing.T) {
rule := &InjectionParamsRule{}
tests := []struct {
name string
provider *Provider
expectError bool
}{
{
name: "Valid injection params",
provider: &Provider{
StructName: "UserService",
Mode: ProviderModeBasic,
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "DB",
Package: "database/sql",
PackageAlias: "sql",
},
},
},
expectError: false,
},
{
name: "Empty parameter type",
provider: &Provider{
StructName: "UserService",
Mode: ProviderModeBasic,
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "",
Package: "database/sql",
PackageAlias: "sql",
},
},
},
expectError: true,
},
{
name: "Missing package alias",
provider: &Provider{
StructName: "UserService",
Mode: ProviderModeBasic,
InjectParams: map[string]InjectParam{
"DB": {
Star: "*",
Type: "sql.DB",
Package: "database/sql",
PackageAlias: "",
},
},
},
expectError: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := rule.Validate(tt.provider)
if tt.expectError {
assert.NotNil(t, err, "Expected validation error but got nil")
} else {
assert.Nil(t, err, "Expected no validation error but got one")
}
})
}
}