diff --git a/good_abstracts/adapters/interfaces.go b/good_abstracts/adapters/interfaces.go index 34a46ac..382be42 100644 --- a/good_abstracts/adapters/interfaces.go +++ b/good_abstracts/adapters/interfaces.go @@ -12,3 +12,7 @@ type RotatableObject interface { GetAngle() models.Angle SetAngle(newAngle models.Angle) } + +type CommandInterface interface { + Execute() error +} diff --git a/good_abstracts/commands/commands.go b/good_abstracts/commands/commands.go new file mode 100644 index 0000000..9ae3839 --- /dev/null +++ b/good_abstracts/commands/commands.go @@ -0,0 +1,210 @@ +package commands + +import ( + "fmt" + "math" + + "good_abstracts/actions" + "good_abstracts/adapters" + "good_abstracts/exceptions" + "good_abstracts/models" + "good_abstracts/uobject" +) + +type Command struct { + command adapters.CommandInterface +} + +func NewCommand(command adapters.CommandInterface) *Command { + return &Command{command: command} +} + +func (c *Command) GetCommandType() adapters.CommandInterface { + return c.command +} + +func (c *Command) Execute() error { + err := c.command.Execute() + return err +} + +// ----------------- // + +type MacroCommand struct { + commands []adapters.CommandInterface +} + +func NewMacroCommand(commands []adapters.CommandInterface) *MacroCommand { + return &MacroCommand{commands: commands} +} + +func (m *MacroCommand) Execute() error { + for _, cmd := range m.commands { + if err := cmd.Execute(); err != nil { + return exceptions.NewCommandException( + fmt.Sprintf("MacroCommand failed on %T", cmd), + err, + ) + } + } + return nil +} + +// ----------------- // + +type CheckFuelCommand struct { + uobj uobject.UObject +} + +func NewCheckFuelCommand(uobj *uobject.UObject) *CheckFuelCommand { + return &CheckFuelCommand{uobj: *uobj} +} + +func (c *CheckFuelCommand) Execute() error { + fuelProp := c.uobj.GetProperty("fuel") + burnRateProp := c.uobj.GetProperty("fuel_burn_rate") + + if fuelProp == nil || burnRateProp == nil { + return exceptions.NewCommandException("Не заданы параметры топлива: fuel или fuel_burn_rate") + } + + fuel := fuelProp.(int) + burnRate := burnRateProp.(int) + + if fuel < burnRate { + return exceptions.NewCommandException("Недостаточно топлива для движения") + } + + return nil +} + +// ----------------- // + +type BurnFuelCommand struct { + uobj uobject.UObject +} + +func NewBurnFuelCommand(uobj *uobject.UObject) *BurnFuelCommand { + return &BurnFuelCommand{uobj: *uobj} +} + +func (b *BurnFuelCommand) Execute() error { + fuelProp := b.uobj.GetProperty("fuel") + burnRateProp := b.uobj.GetProperty("fuel_burn_rate") + + if fuelProp == nil || burnRateProp == nil { + return exceptions.NewCommandException("Не заданы параметры топлива: fuel или fuel_burn_rate") + } + + fuel := fuelProp.(int) + burnRate := burnRateProp.(int) + newValue := fuel - burnRate + if newValue < 0 { + newValue = 0 + } + + b.uobj.SetProperty("fuel", newValue) + return nil +} + +// ----------------- // + +type MoveCommand struct { + action *actions.Move +} + +func NewMoveCommand(moving *adapters.MovingObjectAdapter) *MoveCommand { + return &MoveCommand{ + action: actions.NewMove(moving), + } +} + +func (m *MoveCommand) Execute() error { + m.action.Execute() + return nil +} + +// ----------------- // + +type RotateCommand struct { + action *actions.Rotate + deltaAngle models.Angle +} + +func NewRotateCommand(rotatable *adapters.RotatableObjectAdapter, deltaAngle models.Angle) *RotateCommand { + return &RotateCommand{ + action: actions.NewRotate(rotatable), + deltaAngle: deltaAngle, + } +} + +func (r *RotateCommand) Execute() error { + r.action.Execute(r.deltaAngle) + return nil +} + +// ----------------- // + +type ModifyVelocityOnRotateCommand struct { + uobj uobject.UObject +} + +func NewModifyVelocityOnRotateCommand(uobj *uobject.UObject) *ModifyVelocityOnRotateCommand { + return &ModifyVelocityOnRotateCommand{uobj: *uobj} +} + +func (m *ModifyVelocityOnRotateCommand) Execute() error { + speedProp := m.uobj.GetProperty("velocity") + angleProp := m.uobj.GetProperty("angle") + + if speedProp == nil || angleProp == nil { + return nil + } + + speed := speedProp.(float64) + angle := angleProp.(models.Angle) + + if speed == 0 { + return nil + } + + rad := angle.Radians() + vx := int(speed * math.Cos(rad)) + vy := int(speed * math.Sin(rad)) + + m.uobj.SetProperty("velocity_vector", models.Vector{X: vx, Y: vy}) + return nil +} + +// ----------------- // + +type MoveWithFuelMacroCommand struct { + *MacroCommand +} + +func NewMoveWithFuelMacroCommand(uobj *uobject.UObject, moving *adapters.MovingObjectAdapter) *MoveWithFuelMacroCommand { + commands := []adapters.CommandInterface{ + NewCheckFuelCommand(uobj), + NewMoveCommand(moving), + NewBurnFuelCommand(uobj), + } + return &MoveWithFuelMacroCommand{ + MacroCommand: NewMacroCommand(commands), + } +} + +// ----------------- // + +type RotateWithVelocityMacroCommand struct { + *MacroCommand +} + +func NewRotateWithVelocityMacroCommand(uobj *uobject.UObject, rotatable *adapters.RotatableObjectAdapter, deltaAngle models.Angle) *RotateWithVelocityMacroCommand { + commands := []adapters.CommandInterface{ + NewRotateCommand(rotatable, deltaAngle), + NewModifyVelocityOnRotateCommand(uobj), + } + return &RotateWithVelocityMacroCommand{ + MacroCommand: NewMacroCommand(commands), + } +} diff --git a/good_abstracts/commands/commands_test.go b/good_abstracts/commands/commands_test.go new file mode 100644 index 0000000..1c95d36 --- /dev/null +++ b/good_abstracts/commands/commands_test.go @@ -0,0 +1,554 @@ +package commands + +import ( + "errors" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + + "good_abstracts/adapters" + "good_abstracts/exceptions" + "good_abstracts/models" + "good_abstracts/uobject" +) + +type MockCommand struct { + mock.Mock +} + +func (m *MockCommand) Execute() error { + args := m.Called() + return args.Error(0) +} + +type MockMovingObjectAdapter struct { + mock.Mock +} + +func (m *MockMovingObjectAdapter) GetLocation() models.Point { + args := m.Called() + return args.Get(0).(models.Point) +} + +func (m *MockMovingObjectAdapter) GetVelocity() models.Vector { + args := m.Called() + return args.Get(0).(models.Vector) +} + +func (m *MockMovingObjectAdapter) SetLocation(newPoint models.Point) { + m.Called(newPoint) +} + +type MockRotatableObjectAdapter struct { + mock.Mock +} + +func (m *MockRotatableObjectAdapter) GetAngle() models.Angle { + args := m.Called() + return args.Get(0).(models.Angle) +} + +func (m *MockRotatableObjectAdapter) SetAngle(newAngle models.Angle) { + m.Called(newAngle) +} + +// MoveCommand (actions.Move) +type TestMoveAction struct { + mock.Mock + moving adapters.MovingObject +} + +func NewTestMoveAction(moving adapters.MovingObject) *TestMoveAction { + return &TestMoveAction{moving: moving} +} + +func (t *TestMoveAction) Execute() { + t.Called() + location := t.moving.GetLocation() + velocity := t.moving.GetVelocity() + newLocation := location.Add(velocity) + t.moving.SetLocation(newLocation) +} + +// RotateCommand (actions.Rotate) +type TestRotateAction struct { + mock.Mock + rotatable adapters.RotatableObject +} + +func NewTestRotateAction(rotatable adapters.RotatableObject) *TestRotateAction { + return &TestRotateAction{rotatable: rotatable} +} + +func (t *TestRotateAction) Execute(deltaAngle models.Angle) { + t.Called(deltaAngle) + currentAngle := t.rotatable.GetAngle() + newAngle := currentAngle.Add(deltaAngle) + t.rotatable.SetAngle(newAngle) +} + +func TestCommand(t *testing.T) { + t.Run("Execute исполняется корректно", func(t *testing.T) { + mockCmd := new(MockCommand) + mockCmd.On("Execute").Return(nil) + + cmd := NewCommand(mockCmd) + err := cmd.Execute() + + assert.NoError(t, err) + mockCmd.AssertExpectations(t) + }) + + t.Run("Execute вернул ошибку от команды", func(t *testing.T) { + expectedErr := errors.New("test error") + mockCmd := new(MockCommand) + mockCmd.On("Execute").Return(expectedErr) + + cmd := NewCommand(mockCmd) + err := cmd.Execute() + + assert.Equal(t, expectedErr, err) + mockCmd.AssertExpectations(t) + }) + + t.Run("GetCommandType возвращает тип команды", func(t *testing.T) { + mockCmd := new(MockCommand) + cmd := NewCommand(mockCmd) + + result := cmd.GetCommandType() + + assert.Equal(t, mockCmd, result) + }) +} + +func TestMacroCommand(t *testing.T) { + t.Run("Execute все команды, успех", func(t *testing.T) { + cmd1 := new(MockCommand) + cmd2 := new(MockCommand) + cmd3 := new(MockCommand) + + cmd1.On("Execute").Return(nil) + cmd2.On("Execute").Return(nil) + cmd3.On("Execute").Return(nil) + + macro := NewMacroCommand([]adapters.CommandInterface{cmd1, cmd2, cmd3}) + err := macro.Execute() + + assert.NoError(t, err) + cmd1.AssertExpectations(t) + cmd2.AssertExpectations(t) + cmd3.AssertExpectations(t) + }) + + t.Run("Останавливаем выполнение на первой ошибке", func(t *testing.T) { + cmd1 := new(MockCommand) + cmd2 := new(MockCommand) + cmd3 := new(MockCommand) + + expectedErr := errors.New("command 2 failed") + cmd1.On("Execute").Return(nil) + cmd2.On("Execute").Return(expectedErr) + + macro := NewMacroCommand([]adapters.CommandInterface{cmd1, cmd2, cmd3}) + err := macro.Execute() + + assert.Error(t, err) + assert.IsType(t, &exceptions.CommandException{}, err) + assert.Contains(t, err.Error(), "MacroCommand failed") + cmd1.AssertExpectations(t) + cmd2.AssertExpectations(t) + cmd3.AssertNotCalled(t, "Execute") + }) + + t.Run("Пустой список команд в макрокоманде", func(t *testing.T) { + macro := NewMacroCommand([]adapters.CommandInterface{}) + err := macro.Execute() + + assert.NoError(t, err) + }) +} + +func TestCheckFuelCommand(t *testing.T) { + t.Run("Разрешать движение при избыточном кличестве топлива", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 10) + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewCheckFuelCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + }) + + t.Run("Запрещать движение при недостаточном количестве топлива", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 3) + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewCheckFuelCommand(obj) + err := cmd.Execute() + + assert.Error(t, err) + assert.IsType(t, &exceptions.CommandException{}, err) + assert.Contains(t, err.Error(), "Недостаточно топлива для движения") + }) + + t.Run("Выбрасывать ошибку при отсутствии свойства fuel", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewCheckFuelCommand(obj) + err := cmd.Execute() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Не заданы параметры топлива") + }) + + t.Run("Выбрасывать ошибку при отсутствии свойства fuel_burn_rate", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 10) + + cmd := NewCheckFuelCommand(obj) + err := cmd.Execute() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Не заданы параметры топлива") + }) + + t.Run("Разрешать движение, если топлива хатает тютелька в тютельку", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 5) + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewCheckFuelCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + }) +} + +func TestBurnFuelCommand(t *testing.T) { + t.Run("Успешно сожги топливо", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 10) + obj.SetProperty("fuel_burn_rate", 3) + + cmd := NewBurnFuelCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Equal(t, 7, obj.GetProperty("fuel").(int)) + }) + + t.Run("Сожгли все топливо", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 2) + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewBurnFuelCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Equal(t, 0, obj.GetProperty("fuel").(int)) + }) + + t.Run("Куда-то пропало свойство fuel", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel_burn_rate", 5) + + cmd := NewBurnFuelCommand(obj) + err := cmd.Execute() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Не заданы параметры топлива") + }) + + t.Run("Куда-то пропало свойство fuel_burn_rate", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 10) + + cmd := NewBurnFuelCommand(obj) + err := cmd.Execute() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Не заданы параметры топлива") + }) + + t.Run("Сожгли топливо, которого едва хватало", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 8) + obj.SetProperty("fuel_burn_rate", 8) + + cmd := NewBurnFuelCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Equal(t, 0, obj.GetProperty("fuel").(int)) + }) +} + +func TestMoveCommand(t *testing.T) { + t.Run("Выполняем движение с адаптером", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("location", models.Point{X: 0, Y: 0}) + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 5.0) + + movingAdapter := adapters.NewMovingObjectAdapter(obj) + + cmd := NewMoveCommand(movingAdapter) + err := cmd.Execute() + + assert.NoError(t, err) + + finalLocation := obj.GetProperty("location").(models.Point) + assert.NotEqual(t, models.Point{X: 0, Y: 0}, finalLocation) + }) + + t.Run("Создали команду с адаптером", func(t *testing.T) { + obj := uobject.NewUObject() + movingAdapter := adapters.NewMovingObjectAdapter(obj) + + cmd := NewMoveCommand(movingAdapter) + + assert.NotNil(t, cmd) + assert.NotNil(t, cmd.action) + }) +} + +func TestRotateCommand(t *testing.T) { + t.Run("Вращаемся с адаптером", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("angle", models.Angle{Degrees: 0}) + + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + deltaAngle := models.Angle{Degrees: 45} + + cmd := NewRotateCommand(rotatableAdapter, deltaAngle) + err := cmd.Execute() + + assert.NoError(t, err) + + finalAngle := obj.GetProperty("angle").(models.Angle) + assert.Equal(t, models.Angle{Degrees: 45}, finalAngle) + }) + + t.Run("Создали команду с адаптером и еще углом", func(t *testing.T) { + obj := uobject.NewUObject() + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + deltaAngle := models.Angle{Degrees: 30} + + cmd := NewRotateCommand(rotatableAdapter, deltaAngle) + + assert.NotNil(t, cmd) + assert.NotNil(t, cmd.action) + assert.Equal(t, deltaAngle, cmd.deltaAngle) + }) +} + +func TestModifyVelocityOnRotateCommand(t *testing.T) { + t.Run("Изменили скорость", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("velocity", 10.0) + obj.SetProperty("angle", models.Angle{Degrees: 0}) // 0 degrees = right direction + + cmd := NewModifyVelocityOnRotateCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + + velocityVector := obj.GetProperty("velocity_vector").(models.Vector) + assert.Equal(t, models.Vector{X: 10, Y: 0}, velocityVector) + }) + + t.Run("Изменили угол на 90", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("velocity", 5.0) + obj.SetProperty("angle", models.Angle{Degrees: 90}) // 90 degrees = up direction + + cmd := NewModifyVelocityOnRotateCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + + velocityVector := obj.GetProperty("velocity_vector").(models.Vector) + assert.Equal(t, 0, velocityVector.X) + assert.Equal(t, 5, velocityVector.Y) + }) + + t.Run("Изменили на ноль", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("velocity", 0.0) + obj.SetProperty("angle", models.Angle{Degrees: 45}) + + cmd := NewModifyVelocityOnRotateCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Nil(t, obj.GetProperty("velocity_vector")) + }) + + t.Run("пропало свойство velocity", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("angle", models.Angle{Degrees: 45}) + + cmd := NewModifyVelocityOnRotateCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Nil(t, obj.GetProperty("velocity_vector")) + }) + + t.Run("пропало свойство вектор angle", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("velocity", 10.0) + + cmd := NewModifyVelocityOnRotateCommand(obj) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Nil(t, obj.GetProperty("velocity_vector")) + }) +} + +func TestMoveWithFuelMacroCommand(t *testing.T) { + t.Run("Create macro command with correct sequence", func(t *testing.T) { + obj := uobject.NewUObject() + movingAdapter := adapters.NewMovingObjectAdapter(obj) + + cmd := NewMoveWithFuelMacroCommand(obj, movingAdapter) + + assert.NotNil(t, cmd) + assert.NotNil(t, cmd.MacroCommand) + }) + + t.Run("Макрокоманда успешно выполнилась", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 10) + obj.SetProperty("fuel_burn_rate", 3) + obj.SetProperty("location", models.Point{X: 0, Y: 0}) + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 5.0) + + movingAdapter := adapters.NewMovingObjectAdapter(obj) + + cmd := NewMoveWithFuelMacroCommand(obj, movingAdapter) + err := cmd.Execute() + + assert.NoError(t, err) + assert.Equal(t, 7, obj.GetProperty("fuel").(int)) // 10 - 3 = 7 + + location := obj.GetProperty("location").(models.Point) + assert.NotEqual(t, models.Point{X: 0, Y: 0}, location) + }) + + t.Run("Топлива не хватило - макрокоманда", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 2) + obj.SetProperty("fuel_burn_rate", 5) // Not enough fuel + obj.SetProperty("location", models.Point{X: 0, Y: 0}) + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 5.0) + + movingAdapter := adapters.NewMovingObjectAdapter(obj) + + cmd := NewMoveWithFuelMacroCommand(obj, movingAdapter) + err := cmd.Execute() + + assert.Error(t, err) + assert.Contains(t, err.Error(), "Недостаточно топлива") + assert.Equal(t, 2, obj.GetProperty("fuel").(int)) + location := obj.GetProperty("location").(models.Point) + assert.Equal(t, models.Point{X: 0, Y: 0}, location) + }) +} + +func TestRotateWithVelocityMacroCommand(t *testing.T) { + t.Run("макрокоманда", func(t *testing.T) { + obj := uobject.NewUObject() + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + deltaAngle := models.Angle{Degrees: 45} + + cmd := NewRotateWithVelocityMacroCommand(obj, rotatableAdapter, deltaAngle) + + assert.NotNil(t, cmd) + assert.NotNil(t, cmd.MacroCommand) + }) + + t.Run("Успешное выполнение", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 10.0) + + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + deltaAngle := models.Angle{Degrees: 45} + + cmd := NewRotateWithVelocityMacroCommand(obj, rotatableAdapter, deltaAngle) + err := cmd.Execute() + + assert.NoError(t, err) + + finalAngle := obj.GetProperty("angle").(models.Angle) + assert.Equal(t, models.Angle{Degrees: 45}, finalAngle) + + velocityVector := obj.GetProperty("velocity_vector").(models.Vector) + assert.NotEqual(t, models.Vector{X: 0, Y: 0}, velocityVector) + }) + + t.Run("Считаем скорочть после поворота", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 10.0) + + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + deltaAngle := models.Angle{Degrees: 90} + + cmd := NewRotateWithVelocityMacroCommand(obj, rotatableAdapter, deltaAngle) + err := cmd.Execute() + + assert.NoError(t, err) + + velocityVector := obj.GetProperty("velocity_vector").(models.Vector) + assert.InDelta(t, 0, velocityVector.X, 0.1) + assert.InDelta(t, 10, velocityVector.Y, 0.1) + }) +} + +func TestIntegration(t *testing.T) { + t.Run("Интеграционный тест для проверки совместной работы команд", func(t *testing.T) { + obj := uobject.NewUObject() + obj.SetProperty("fuel", 20) + obj.SetProperty("fuel_burn_rate", 2) + obj.SetProperty("location", models.Point{X: 0, Y: 0}) + obj.SetProperty("angle", models.Angle{Degrees: 0}) + obj.SetProperty("velocity", 5.0) + + movingAdapter := adapters.NewMovingObjectAdapter(obj) + rotatableAdapter := adapters.NewRotatableObjectAdapter(obj) + + // Выполняем несколько движений и поворотов + moveCmd := NewMoveWithFuelMacroCommand(obj, movingAdapter) + rotateCmd := NewRotateWithVelocityMacroCommand(obj, rotatableAdapter, models.Angle{Degrees: 90}) + + // Двигаемся + err := moveCmd.Execute() + assert.NoError(t, err) + + // Поворачиваем + err = rotateCmd.Execute() + assert.NoError(t, err) + + // Двигаемся снова (уже с новым направлением) + err = moveCmd.Execute() + assert.NoError(t, err) + + // Проверяем итоговое состояние + assert.Equal(t, 16, obj.GetProperty("fuel").(int)) // 20 - 2*2 = 16 + finalLocation := obj.GetProperty("location").(models.Point) + finalAngle := obj.GetProperty("angle").(models.Angle) + + assert.NotEqual(t, models.Point{X: 0, Y: 0}, finalLocation) + assert.Equal(t, models.Angle{Degrees: 90}, finalAngle) + }) +} diff --git a/good_abstracts/coverage.txt b/good_abstracts/coverage.txt index 830ee89..24e7f34 100644 --- a/good_abstracts/coverage.txt +++ b/good_abstracts/coverage.txt @@ -1,26 +1,4 @@ mode: atomic -good_abstracts/uobject/uobject.go:7.28,11.2 1 4 -good_abstracts/uobject/uobject.go:13.60,15.2 1 6 -good_abstracts/uobject/uobject.go:17.67,19.2 1 6 -good_abstracts/main.go:11.44,16.2 4 0 -good_abstracts/main.go:18.13,45.2 19 0 -good_abstracts/adapters/adapters.go:13.73,15.2 1 5 -good_abstracts/adapters/adapters.go:17.58,19.17 2 2 -good_abstracts/adapters/adapters.go:19.17,20.38 1 1 -good_abstracts/adapters/adapters.go:22.2,22.28 1 1 -good_abstracts/adapters/adapters.go:25.59,29.45 3 2 -good_abstracts/adapters/adapters.go:29.45,30.64 1 0 -good_abstracts/adapters/adapters.go:33.2,40.36 6 2 -good_abstracts/adapters/adapters.go:43.66,45.2 1 1 -good_abstracts/adapters/adapters.go:51.79,53.2 1 3 -good_abstracts/adapters/adapters.go:55.58,57.17 2 2 -good_abstracts/adapters/adapters.go:57.17,58.40 1 1 -good_abstracts/adapters/adapters.go:60.2,60.28 1 1 -good_abstracts/adapters/adapters.go:63.66,65.2 1 1 -good_abstracts/actions/actions.go:12.56,14.2 1 1 -good_abstracts/actions/actions.go:16.26,22.2 4 1 -good_abstracts/actions/actions.go:28.66,30.2 1 2 -good_abstracts/actions/actions.go:32.51,36.2 3 2 good_abstracts/models/models.go:9.36,14.2 1 1 good_abstracts/models/models.go:20.42,25.2 1 1 good_abstracts/models/models.go:31.34,33.2 1 1 @@ -39,3 +17,63 @@ good_abstracts/models/models.go:65.46,66.27 1 2 good_abstracts/models/models.go:67.13,68.32 1 1 good_abstracts/models/models.go:69.11,70.24 1 1 good_abstracts/models/models.go:71.10,72.15 1 0 +good_abstracts/uobject/uobject.go:7.28,11.2 1 4 +good_abstracts/uobject/uobject.go:13.60,15.2 1 6 +good_abstracts/uobject/uobject.go:17.67,19.2 1 6 +good_abstracts/main.go:11.44,16.2 4 0 +good_abstracts/main.go:18.13,45.2 19 0 +good_abstracts/actions/actions.go:12.56,14.2 1 1 +good_abstracts/actions/actions.go:16.26,22.2 4 1 +good_abstracts/actions/actions.go:28.66,30.2 1 2 +good_abstracts/actions/actions.go:32.51,36.2 3 2 +good_abstracts/adapters/adapters.go:13.73,15.2 1 5 +good_abstracts/adapters/adapters.go:17.58,19.17 2 2 +good_abstracts/adapters/adapters.go:19.17,20.38 1 1 +good_abstracts/adapters/adapters.go:22.2,22.28 1 1 +good_abstracts/adapters/adapters.go:25.59,29.45 3 2 +good_abstracts/adapters/adapters.go:29.45,30.64 1 0 +good_abstracts/adapters/adapters.go:33.2,40.36 6 2 +good_abstracts/adapters/adapters.go:43.66,45.2 1 1 +good_abstracts/adapters/adapters.go:51.79,53.2 1 3 +good_abstracts/adapters/adapters.go:55.58,57.17 2 2 +good_abstracts/adapters/adapters.go:57.17,58.40 1 1 +good_abstracts/adapters/adapters.go:60.2,60.28 1 1 +good_abstracts/adapters/adapters.go:63.66,65.2 1 1 +good_abstracts/commands/commands.go:18.61,20.2 1 3 +good_abstracts/commands/commands.go:22.62,24.2 1 1 +good_abstracts/commands/commands.go:26.35,29.2 2 2 +good_abstracts/commands/commands.go:37.74,39.2 1 11 +good_abstracts/commands/commands.go:41.40,42.33 1 10 +good_abstracts/commands/commands.go:42.33,43.39 1 21 +good_abstracts/commands/commands.go:43.39,48.4 1 2 +good_abstracts/commands/commands.go:50.2,50.12 1 8 +good_abstracts/commands/commands.go:59.67,61.2 1 9 +good_abstracts/commands/commands.go:63.44,67.44 3 9 +good_abstracts/commands/commands.go:67.44,69.3 1 2 +good_abstracts/commands/commands.go:71.2,74.21 3 7 +good_abstracts/commands/commands.go:74.21,76.3 1 2 +good_abstracts/commands/commands.go:78.2,78.12 1 5 +good_abstracts/commands/commands.go:87.65,89.2 1 9 +good_abstracts/commands/commands.go:91.43,95.44 3 8 +good_abstracts/commands/commands.go:95.44,97.3 1 2 +good_abstracts/commands/commands.go:99.2,102.18 4 6 +good_abstracts/commands/commands.go:102.18,104.3 1 1 +good_abstracts/commands/commands.go:106.2,107.12 2 6 +good_abstracts/commands/commands.go:116.72,120.2 1 6 +good_abstracts/commands/commands.go:122.39,125.2 2 4 +good_abstracts/commands/commands.go:134.107,139.2 1 6 +good_abstracts/commands/commands.go:141.41,144.2 2 4 +good_abstracts/commands/commands.go:152.93,154.2 1 9 +good_abstracts/commands/commands.go:156.57,160.42 3 8 +good_abstracts/commands/commands.go:160.42,162.3 1 2 +good_abstracts/commands/commands.go:164.2,167.16 3 6 +good_abstracts/commands/commands.go:167.16,169.3 1 1 +good_abstracts/commands/commands.go:171.2,176.12 5 5 +good_abstracts/commands/commands.go:185.121,194.2 2 4 +good_abstracts/commands/commands.go:202.164,210.2 2 4 +good_abstracts/exceptions/exception.go:10.43,11.20 1 0 +good_abstracts/exceptions/exception.go:11.20,13.3 1 0 +good_abstracts/exceptions/exception.go:14.2,14.18 1 0 +good_abstracts/exceptions/exception.go:17.76,18.20 1 0 +good_abstracts/exceptions/exception.go:18.20,20.3 1 0 +good_abstracts/exceptions/exception.go:21.2,21.44 1 0 diff --git a/good_abstracts/exceptions/exception.go b/good_abstracts/exceptions/exception.go new file mode 100644 index 0000000..f0c4e13 --- /dev/null +++ b/good_abstracts/exceptions/exception.go @@ -0,0 +1,22 @@ +package exceptions + +import "fmt" + +type CommandException struct { + Message string + Cause error +} + +func (c *CommandException) Error() string { + if c.Cause != nil { + return fmt.Sprintf("%s: %v", c.Message, c.Cause) + } + return c.Message +} + +func NewCommandException(message string, cause ...error) *CommandException { + if len(cause) > 0 { + return &CommandException{Message: message, Cause: cause[0]} + } + return &CommandException{Message: message} +} diff --git a/good_abstracts/main.go b/good_abstracts/main.go index 2b4f7e9..ab8ef9b 100644 --- a/good_abstracts/main.go +++ b/good_abstracts/main.go @@ -2,6 +2,7 @@ package main import ( "fmt" + "good_abstracts/actions" "good_abstracts/adapters" "good_abstracts/models"