package repository import ( "errors" "regexp" "testing" "time" "github.com/DATA-DOG/go-sqlmock" "github.com/wmms/wmms/internal/model" "github.com/wmms/wmms/pkg/database" ) func TestListRolesIncludesChinesePermissionNames(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db now := time.Date(2026, 9, 16, 9, 0, 0, 0, time.Local) roleQuery := `SELECT id, code, name, enabled, created_at, updated_at FROM roles ORDER BY id` mock.ExpectQuery(regexp.QuoteMeta(roleQuery)).WillReturnRows( sqlmock.NewRows([]string{"id", "code", "name", "enabled", "created_at", "updated_at"}). AddRow(2, "warehouse_admin", "仓库管理员", true, now, now), ) permissionQuery := `SELECT rp.role_id, p.id, p.code, p.name FROM role_permissions rp JOIN permissions p ON p.id = rp.permission_id ORDER BY rp.role_id, p.code` mock.ExpectQuery(regexp.QuoteMeta(permissionQuery)).WillReturnRows( sqlmock.NewRows([]string{"role_id", "id", "code", "name"}). AddRow(2, 12, "inventory.stock.read", "查看库存"), ) roles, err := NewOrganizationRepository().ListRoles() if err != nil { t.Fatalf("list roles: %v", err) } if len(roles) != 1 || len(roles[0].Permissions) != 1 { t.Fatalf("unexpected roles: %+v", roles) } if roles[0].Permissions[0].Name != "查看库存" { t.Fatalf("permission name not returned: %+v", roles[0].Permissions[0]) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestUpdateRoleRejectsSuperAdminChanges(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectBegin() mock.ExpectQuery(regexp.QuoteMeta(`SELECT code FROM roles WHERE id = ? FOR UPDATE`)).WithArgs(int64(1)).WillReturnRows(sqlmock.NewRows([]string{"code"}).AddRow("super_admin")) mock.ExpectRollback() err = NewOrganizationRepository().UpdateRole(1, "超级管理员", false, nil) if !errors.Is(err, ErrProtectedRole) { t.Fatalf("expected protected role error, got %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } func TestUpdateRoleReplacesPermissionsInTransaction(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectBegin() mock.ExpectQuery(regexp.QuoteMeta(`SELECT code FROM roles WHERE id = ? FOR UPDATE`)).WithArgs(int64(3)).WillReturnRows(sqlmock.NewRows([]string{"code"}).AddRow("purchaser")) mock.ExpectExec(regexp.QuoteMeta(`UPDATE roles SET name = ?, enabled = ? WHERE id = ?`)).WithArgs("采购人员", true, int64(3)).WillReturnResult(sqlmock.NewResult(0, 1)) mock.ExpectExec(regexp.QuoteMeta(`DELETE FROM role_permissions WHERE role_id = ?`)).WithArgs(int64(3)).WillReturnResult(sqlmock.NewResult(0, 2)) mock.ExpectExec(regexp.QuoteMeta(`INSERT INTO role_permissions (role_id, permission_id) SELECT ?, id FROM permissions WHERE code IN (?, ?)`)).WithArgs(int64(3), "inventory.stock.read", "partner.supplier.read").WillReturnResult(sqlmock.NewResult(0, 2)) mock.ExpectCommit() err = NewOrganizationRepository().UpdateRole(3, "采购人员", true, []string{"inventory.stock.read", "partner.supplier.read"}) if err != nil { t.Fatalf("update role: %v", err) } if err := mock.ExpectationsWereMet(); err != nil { t.Fatal(err) } } // 组织不存在时(比如 id 打错)更新影响 0 行。此前返回 nil,接口提示"保存成功" // 而实际什么都没改。 func TestOrganizationUpdateReportsMissingRecord(t *testing.T) { db, mock, err := sqlmock.New() if err != nil { t.Fatalf("open sql mock: %v", err) } t.Cleanup(func() { database.DB = nil; db.Close() }) database.DB = db mock.ExpectExec("UPDATE organizations").WillReturnResult(sqlmock.NewResult(0, 0)) err = NewOrganizationRepository().Update(&model.Organization{ID: 404, Name: "改名后的组织"}) if !errors.Is(err, ErrRecordNotFound) { t.Fatalf("影响 0 行应当返回 ErrRecordNotFound,实际 %v", err) } }