重构 #4
| @@ -14,11 +14,11 @@ import ( | ||||
| func setupTestDB(t *testing.T) *gorm.DB { | ||||
| 	// "file::memory:?cache=shared" 是 GORM 连接内存 SQLite 的标准方式,确保在同一测试中的不同连接可以访问相同的数据。 | ||||
| 	db, err := gorm.Open(sqlite.Open("file::memory:?cache=shared"), &gorm.Config{}) | ||||
| 	assert.NoError(t, err, "连接内存数据库不应出错") | ||||
| 	assert.NoError(t, err, "连接内存数据库时发生错误") | ||||
|  | ||||
| 	// 自动迁移所有需要的表结构 | ||||
| 	err = db.AutoMigrate(&models.User{}, &models.Device{}) | ||||
| 	assert.NoError(t, err, "数据库迁移不应出错") | ||||
| 	err = db.AutoMigrate(&models.User{}, &models.Device{}, &models.SubPlan{}, &models.Task{}, &models.Plan{}) | ||||
| 	assert.NoError(t, err, "数据库迁移时发生错误") | ||||
|  | ||||
| 	return db | ||||
| } | ||||
|   | ||||
							
								
								
									
										49
									
								
								internal/infra/repository/plan_repository.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								internal/infra/repository/plan_repository.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| package repository | ||||
|  | ||||
| import ( | ||||
| 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| // PlanRepository 定义了与计划模型相关的数据库操作接口 | ||||
| // 这是为了让业务逻辑层依赖于抽象,而不是具体的数据库实现 | ||||
| type PlanRepository interface { | ||||
| 	// ListBasicPlans 获取所有计划的基本信息,不包含子计划和任务详情 | ||||
| 	ListBasicPlans() ([]models.Plan, error) | ||||
| 	// GetBasicPlanByID 根据ID获取计划的基本信息,不包含子计划和任务详情 | ||||
| 	GetBasicPlanByID(id uint) (*models.Plan, error) | ||||
| } | ||||
|  | ||||
| // gormPlanRepository 是 PlanRepository 的 GORM 实现 | ||||
| type gormPlanRepository struct { | ||||
| 	db *gorm.DB | ||||
| } | ||||
|  | ||||
| // NewGormPlanRepository 创建一个新的 PlanRepository GORM 实现实例 | ||||
| func NewGormPlanRepository(db *gorm.DB) PlanRepository { | ||||
| 	return &gormPlanRepository{ | ||||
| 		db: db, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // ListBasicPlans 获取所有计划的基本信息,不包含子计划和任务详情 | ||||
| func (r *gormPlanRepository) ListBasicPlans() ([]models.Plan, error) { | ||||
| 	var plans []models.Plan | ||||
| 	// GORM 默认不会加载关联,除非使用 Preload,所以直接 Find 即可满足要求 | ||||
| 	result := r.db.Find(&plans) | ||||
| 	if result.Error != nil { | ||||
| 		return nil, result.Error | ||||
| 	} | ||||
| 	return plans, nil | ||||
| } | ||||
|  | ||||
| // GetBasicPlanByID 根据ID获取计划的基本信息,不包含子计划和任务详情 | ||||
| func (r *gormPlanRepository) GetBasicPlanByID(id uint) (*models.Plan, error) { | ||||
| 	var plan models.Plan | ||||
| 	// GORM 默认不会加载关联,除非使用 Preload,所以直接 First 即可满足要求 | ||||
| 	result := r.db.First(&plan, id) | ||||
| 	if result.Error != nil { | ||||
| 		return nil, result.Error | ||||
| 	} | ||||
| 	return &plan, nil | ||||
| } | ||||
							
								
								
									
										136
									
								
								internal/infra/repository/plan_repository_test.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								internal/infra/repository/plan_repository_test.go
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,136 @@ | ||||
| package repository_test | ||||
|  | ||||
| import ( | ||||
| 	"errors" | ||||
| 	"testing" | ||||
|  | ||||
| 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/models" | ||||
| 	"git.huangwc.com/pig/pig-farm-controller/internal/infra/repository" | ||||
| 	"github.com/stretchr/testify/assert" | ||||
| 	"gorm.io/gorm" | ||||
| ) | ||||
|  | ||||
| // createTestPlan 是一个辅助函数,用于创建测试计划。 | ||||
| func createTestPlan(name, description string, execType models.PlanExecutionType, contentType models.PlanContentType) models.Plan { | ||||
| 	return models.Plan{ | ||||
| 		Name:          name, | ||||
| 		Description:   description, | ||||
| 		ExecutionType: execType, | ||||
| 		ContentType:   contentType, | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TestListBasicPlans 测试 ListBasicPlans 方法,确保它能正确返回所有计划的基本信息。 | ||||
| func TestListBasicPlans(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name          string | ||||
| 		setupPlans    []models.Plan | ||||
| 		expectedCount int | ||||
| 		expectedError error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:          "数据库中没有计划", | ||||
| 			setupPlans:    []models.Plan{}, | ||||
| 			expectedCount: 0, | ||||
| 			expectedError: nil, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name: "数据库中有多个计划", | ||||
| 			setupPlans: []models.Plan{ | ||||
| 				createTestPlan("计划 A", "描述 A", models.PlanExecutionTypeAutomatic, models.PlanContentTypeTasks), | ||||
| 				createTestPlan("计划 B", "描述 B", models.PlanExecutionTypeManual, models.PlanContentTypeSubPlans), | ||||
| 			}, | ||||
| 			expectedCount: 2, | ||||
| 			expectedError: nil, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			db := setupTestDB(t) | ||||
| 			repo := repository.NewGormPlanRepository(db) | ||||
|  | ||||
| 			for i := range tt.setupPlans { | ||||
| 				err := db.Create(&tt.setupPlans[i]).Error | ||||
| 				assert.NoError(t, err, "插入设置计划失败") | ||||
| 			} | ||||
|  | ||||
| 			plans, err := repo.ListBasicPlans() | ||||
|  | ||||
| 			if tt.expectedError != nil { | ||||
| 				assert.Error(t, err) | ||||
| 				assert.True(t, errors.Is(err, tt.expectedError)) | ||||
| 			} else { | ||||
| 				assert.NoError(t, err) | ||||
| 				assert.Len(t, plans, tt.expectedCount) | ||||
| 				if tt.expectedCount > 0 { | ||||
| 					// 验证返回的计划是否包含预期的ID | ||||
| 					var actualIDs []uint | ||||
| 					for _, p := range plans { | ||||
| 						actualIDs = append(actualIDs, p.ID) | ||||
| 						assert.Empty(t, p.SubPlans, "ListBasicPlans 不应加载子计划") | ||||
| 						assert.Empty(t, p.Tasks, "ListBasicPlans 不应加载任务") | ||||
| 					} | ||||
| 					for _, setupPlan := range tt.setupPlans { | ||||
| 						assert.Contains(t, actualIDs, setupPlan.ID, "返回的计划应包含设置计划的ID") | ||||
| 					} | ||||
| 				} | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
|  | ||||
| // TestGetBasicPlanByID 测试 GetBasicPlanByID 方法,确保它能根据ID正确返回计划的基本信息。 | ||||
| func TestGetBasicPlanByID(t *testing.T) { | ||||
| 	tests := []struct { | ||||
| 		name          string | ||||
| 		setupPlan     models.Plan | ||||
| 		idToFetch     uint | ||||
| 		expectFound   bool | ||||
| 		expectedError error | ||||
| 	}{ | ||||
| 		{ | ||||
| 			name:        "通过ID找到计划", | ||||
| 			setupPlan:   createTestPlan("计划 C", "描述 C", models.PlanExecutionTypeAutomatic, models.PlanContentTypeTasks), | ||||
| 			idToFetch:   0, // 创建后设置 | ||||
| 			expectFound: true, | ||||
| 		}, | ||||
| 		{ | ||||
| 			name:          "通过ID未找到计划", | ||||
| 			setupPlan:     models.Plan{}, // 此情况下无需设置计划 | ||||
| 			idToFetch:     999, | ||||
| 			expectFound:   false, | ||||
| 			expectedError: gorm.ErrRecordNotFound, | ||||
| 		}, | ||||
| 	} | ||||
|  | ||||
| 	for _, tt := range tests { | ||||
| 		t.Run(tt.name, func(t *testing.T) { | ||||
| 			db := setupTestDB(t) | ||||
| 			repo := repository.NewGormPlanRepository(db) | ||||
|  | ||||
| 			if tt.setupPlan.Name != "" { // 仅在有效设置时创建计划 | ||||
| 				err := db.Create(&tt.setupPlan).Error | ||||
| 				assert.NoError(t, err, "插入设置计划失败") | ||||
| 				tt.idToFetch = tt.setupPlan.ID // 使用数据库生成的ID | ||||
| 			} | ||||
|  | ||||
| 			fetchedPlan, err := repo.GetBasicPlanByID(tt.idToFetch) | ||||
|  | ||||
| 			if tt.expectedError != nil { | ||||
| 				assert.Error(t, err) | ||||
| 				assert.True(t, errors.Is(err, tt.expectedError), "预期错误类型不匹配") | ||||
| 				assert.Nil(t, fetchedPlan) | ||||
| 			} else { | ||||
| 				assert.NoError(t, err) | ||||
| 				assert.NotNil(t, fetchedPlan) | ||||
| 				assert.Equal(t, tt.setupPlan.Name, fetchedPlan.Name) | ||||
| 				assert.Equal(t, tt.setupPlan.Description, fetchedPlan.Description) | ||||
| 				assert.Equal(t, tt.setupPlan.ExecutionType, fetchedPlan.ExecutionType) | ||||
| 				assert.Equal(t, tt.setupPlan.ContentType, fetchedPlan.ContentType) | ||||
| 				assert.Empty(t, fetchedPlan.SubPlans, "GetBasicPlanByID 不应加载子计划") | ||||
| 				assert.Empty(t, fetchedPlan.Tasks, "GetBasicPlanByID 不应加载任务") | ||||
| 			} | ||||
| 		}) | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user