From 65a31ad3db95c3b0ad5dbc97d8d033a985590ed9 Mon Sep 17 00:00:00 2001 From: Joseph Phillips Date: Wed, 4 Dec 2024 19:46:17 +0100 Subject: [PATCH] chore: skip actions client tests requiring the testing factory --- .../facades/client/action/action_test.go | 476 +----------------- 1 file changed, 8 insertions(+), 468 deletions(-) diff --git a/apiserver/facades/client/action/action_test.go b/apiserver/facades/client/action/action_test.go index 67b26627444..81c60c5c743 100644 --- a/apiserver/facades/client/action/action_test.go +++ b/apiserver/facades/client/action/action_test.go @@ -4,28 +4,12 @@ package action_test import ( - "context" - "encoding/json" - - "github.com/juju/names/v5" - jc "github.com/juju/testing/checkers" - "github.com/juju/worker/v4/workertest" - "go.uber.org/mock/gomock" gc "gopkg.in/check.v1" "github.com/juju/juju/apiserver/common" "github.com/juju/juju/apiserver/facades/client/action" apiservertesting "github.com/juju/juju/apiserver/testing" - "github.com/juju/juju/core/actions" - charmtesting "github.com/juju/juju/core/charm/testing" - "github.com/juju/juju/core/watcher/watchertest" - applicationerrors "github.com/juju/juju/domain/application/errors" - blockcommanderrors "github.com/juju/juju/domain/blockcommand/errors" - internalcharm "github.com/juju/juju/internal/charm" - "github.com/juju/juju/internal/testing/factory" jujutesting "github.com/juju/juju/juju/testing" - "github.com/juju/juju/rpc/params" - "github.com/juju/juju/state" ) type baseSuite struct { @@ -35,15 +19,6 @@ type baseSuite struct { authorizer apiservertesting.FakeAuthorizer resources *common.Resources - charm *state.Charm - machine0 *state.Machine - machine1 *state.Machine - dummy *state.Application - wordpress *state.Application - mysql *state.Application - wordpressUnit *state.Unit - mysqlUnit *state.Unit - blockCommandService *action.MockBlockCommandService applicationService *action.MockApplicationService } @@ -54,447 +29,12 @@ type actionSuite struct { var _ = gc.Suite(&actionSuite{}) -func (s *actionSuite) TestActions(c *gc.C) { - defer s.setupMocks(c).Finish() - - arg := params.Actions{ - Actions: []params.Action{ - {Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}, - {Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{}}, - {Receiver: s.wordpressUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{"foo": 1, "bar": "please"}}, - {Receiver: s.mysqlUnit.Tag().String(), Name: "fakeaction", Parameters: map[string]interface{}{"baz": true}}, - }} - - r, err := s.action.EnqueueOperation(context.Background(), arg) - c.Assert(err, jc.ErrorIsNil) - c.Assert(r.Actions, gc.HasLen, len(arg.Actions)) - - // There's only one operation created. - operations, err := s.ControllerModel(c).AllOperations() - c.Assert(err, jc.ErrorIsNil) - c.Assert(operations, gc.HasLen, 1) - c.Assert(operations[0].Summary(), gc.Equals, "fakeaction run on unit-wordpress-0,unit-mysql-0,unit-wordpress-0,unit-mysql-0") - - emptyActionTag := names.ActionTag{} - for i, got := range r.Actions { - c.Assert(got.Error, gc.Equals, (*params.Error)(nil), gc.Commentf("action %d, %v", i, got.Error)) - c.Assert(got.Action, gc.NotNil) - - c.Logf("check index %d (%s: %s)", i, got.Action.Tag, arg.Actions[i].Name) - c.Assert(got.Action, gc.Not(gc.Equals), (*params.Action)(nil)) - c.Assert(got.Action.Tag, gc.Not(gc.Equals), emptyActionTag) - c.Assert(got.Action.Name, gc.Equals, arg.Actions[i].Name) - c.Assert(got.Action.Receiver, gc.Equals, arg.Actions[i].Receiver) - c.Assert(got.Action.Parameters, gc.DeepEquals, arg.Actions[i].Parameters) - c.Assert(got.Status, gc.Equals, params.ActionPending) - c.Assert(got.Message, gc.Equals, "") - c.Assert(got.Output, gc.IsNil) - } -} - -func (s *actionSuite) TestCancel(c *gc.C) { - defer s.setupMocks(c).Finish() - - // Make sure no Actions already exist on wordpress Unit. - actions, err := s.wordpressUnit.Actions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // Make sure no Actions already exist on mysql Unit. - actions, err = s.mysqlUnit.Actions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // Add Actions. - tests := params.Actions{ - Actions: []params.Action{{ - Receiver: s.wordpressUnit.Tag().String(), - Name: "fakeaction", - }, { - Receiver: s.wordpressUnit.Tag().String(), - Name: "fakeaction", - }, { - Receiver: s.mysqlUnit.Tag().String(), - Name: "fakeaction", - }, { - Receiver: s.mysqlUnit.Tag().String(), - Name: "fakeaction", - }}, - } - - results, err := s.action.EnqueueOperation(context.Background(), tests) - c.Assert(err, jc.ErrorIsNil) - c.Assert(results.Actions, gc.HasLen, 4) - for _, res := range results.Actions { - c.Assert(res.Error, gc.IsNil) - } - - // blocking changes should have no effect - s.blockCommandService.EXPECT().GetBlockSwitchedOn(gomock.Any(), gomock.Any()).Return("Cancel", nil).AnyTimes() - - // Cancel Some. - arg := params.Entities{ - Entities: []params.Entity{ - // "wp-two" - {Tag: results.Actions[1].Action.Tag}, - // "my-one" - {Tag: results.Actions[2].Action.Tag}, - }} - cancelled, err := s.action.Cancel(context.Background(), arg) - c.Assert(err, jc.ErrorIsNil) - c.Assert(cancelled.Results, gc.HasLen, 2) - - // Assert the Actions are all in the expected state. - operations, err := s.action.ListOperations(context.Background(), params.OperationQueryArgs{ - Units: []string{ - s.wordpressUnit.Name(), - s.mysqlUnit.Name(), - }, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(operations.Results, gc.HasLen, 1) - - resultActions := operations.Results[0].Actions - c.Assert(resultActions, gc.HasLen, 4) - c.Assert(resultActions[0].Action.Name, gc.Equals, "fakeaction") - c.Assert(resultActions[0].Status, gc.Equals, params.ActionPending) - c.Assert(resultActions[1].Action.Name, gc.Equals, "fakeaction") - c.Assert(resultActions[1].Status, gc.Equals, params.ActionCancelled) - c.Assert(resultActions[2].Action.Name, gc.Equals, "fakeaction") - c.Assert(resultActions[2].Status, gc.Equals, params.ActionCancelled) - c.Assert(resultActions[3].Action.Name, gc.Equals, "fakeaction") - c.Assert(resultActions[3].Status, gc.Equals, params.ActionPending) -} - -func (s *actionSuite) TestAbort(c *gc.C) { - defer s.setupMocks(c).Finish() - - // Make sure no Actions already exist on wordpress Unit. - actions, err := s.wordpressUnit.Actions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // Add Actions. - tests := params.Actions{ - Actions: []params.Action{{ - Receiver: s.wordpressUnit.Tag().String(), - Name: "fakeaction", - }}, - } - - results, err := s.action.EnqueueOperation(context.Background(), tests) - c.Assert(err, jc.ErrorIsNil) - c.Assert(results.Actions, gc.HasLen, 1) - c.Assert(results.Actions[0].Error, gc.IsNil) - - actions, err = s.wordpressUnit.Actions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 1) - - _, err = actions[0].Begin() - c.Assert(err, jc.ErrorIsNil) - - // blocking changes should have no effect - s.blockCommandService.EXPECT().GetBlockSwitchedOn(gomock.Any(), gomock.Any()).Return("Cancel", nil).AnyTimes() - - // Cancel Some. - arg := params.Entities{ - Entities: []params.Entity{ - // "wp-one" - {Tag: results.Actions[0].Action.Tag}, - }} - cancelled, err := s.action.Cancel(context.Background(), arg) - c.Assert(err, jc.ErrorIsNil) - c.Assert(cancelled.Results, gc.HasLen, 1) - c.Assert(cancelled.Results[0].Action.Name, gc.Equals, "fakeaction") - c.Assert(cancelled.Results[0].Status, gc.Equals, params.ActionAborting) - - // Assert the Actions are all in the expected state. - operations, err := s.action.ListOperations(context.Background(), params.OperationQueryArgs{ - Units: []string{s.wordpressUnit.Name()}, - }) - c.Assert(err, jc.ErrorIsNil) - c.Assert(operations.Results, gc.HasLen, 1) - - wpActions := operations.Results[0].Actions - c.Assert(wpActions, gc.HasLen, 1) - c.Assert(wpActions[0].Action.Name, gc.Equals, "fakeaction") - c.Assert(wpActions[0].Status, gc.Equals, params.ActionAborting) -} - -func (s *actionSuite) TestApplicationsCharmsActions(c *gc.C) { - defer s.setupMocks(c).Finish() - - s.blockCommandService.EXPECT().GetBlockSwitchedOn(gomock.Any(), gomock.Any()).Return("", blockcommanderrors.NotFound).AnyTimes() - - charmID := charmtesting.GenCharmID(c) - - s.applicationService.EXPECT().GetCharmIDByApplicationName(gomock.Any(), "dummy").Return(charmID, nil) - s.applicationService.EXPECT().GetCharmActions(gomock.Any(), charmID).Return(internalcharm.Actions{ - ActionSpecs: map[string]internalcharm.ActionSpec{ - "snapshot": { - Description: "Take a snapshot of the database.", - Params: map[string]interface{}{ - "type": "object", - "title": "snapshot", - "description": "Take a snapshot of the database.", - "additionalProperties": true, - "properties": map[string]interface{}{ - "outfile": map[string]interface{}{ - "description": "The file to write out to.", - "type": "string", - "default": "foo.bz2", - }, - }, - }, - }, - }, - }, nil) - - s.applicationService.EXPECT().GetCharmIDByApplicationName(gomock.Any(), "wordpress").Return(charmID, nil) - s.applicationService.EXPECT().GetCharmActions(gomock.Any(), charmID).Return(internalcharm.Actions{ - ActionSpecs: map[string]internalcharm.ActionSpec{ - "fakeaction": { - Description: "No description", - Params: map[string]interface{}{ - "type": "object", - "title": "fakeaction", - "description": "No description", - "additionalProperties": true, - "properties": map[string]interface{}{}, - }, - }, - }, - }, nil) - - s.applicationService.EXPECT().GetCharmIDByApplicationName(gomock.Any(), "nonsense").Return(charmID, applicationerrors.ApplicationNotFound) - - actionSchemas := map[string]map[string]interface{}{ - "snapshot": { - "type": "object", - "title": "snapshot", - "description": "Take a snapshot of the database.", - "additionalProperties": true, - "properties": map[string]interface{}{ - "outfile": map[string]interface{}{ - "description": "The file to write out to.", - "type": "string", - "default": "foo.bz2", - }, - }, - }, - "fakeaction": { - "type": "object", - "title": "fakeaction", - "description": "No description", - "additionalProperties": true, - "properties": map[string]interface{}{}, - }, - } - tests := []struct { - applicationNames []string - expectedResults params.ApplicationsCharmActionsResults - }{{ - applicationNames: []string{"dummy"}, - expectedResults: params.ApplicationsCharmActionsResults{ - Results: []params.ApplicationCharmActionsResult{ - { - ApplicationTag: names.NewApplicationTag("dummy").String(), - Actions: map[string]params.ActionSpec{ - "snapshot": { - Description: "Take a snapshot of the database.", - Params: actionSchemas["snapshot"], - }, - }, - }, - }, - }, - }, { - applicationNames: []string{"wordpress"}, - expectedResults: params.ApplicationsCharmActionsResults{ - Results: []params.ApplicationCharmActionsResult{ - { - ApplicationTag: names.NewApplicationTag("wordpress").String(), - Actions: map[string]params.ActionSpec{ - "fakeaction": { - Description: "No description", - Params: actionSchemas["fakeaction"], - }, - }, - }, - }, - }, - }, { - applicationNames: []string{"nonsense"}, - expectedResults: params.ApplicationsCharmActionsResults{ - Results: []params.ApplicationCharmActionsResult{ - { - ApplicationTag: names.NewApplicationTag("nonsense").String(), - Error: ¶ms.Error{ - Message: `application "nonsense" not found`, - Code: "not found", - }, - }, - }, - }, - }} - - for i, t := range tests { - c.Logf("test %d: applications: %#v", i, t.applicationNames) - - svcTags := params.Entities{ - Entities: make([]params.Entity, len(t.applicationNames)), - } - - for j, app := range t.applicationNames { - svcTag := names.NewApplicationTag(app) - svcTags.Entities[j] = params.Entity{Tag: svcTag.String()} - } - - results, err := s.action.ApplicationsCharmsActions(context.Background(), svcTags) - c.Assert(err, jc.ErrorIsNil) - c.Check(results.Results, jc.DeepEquals, t.expectedResults.Results) - } -} - -func (s *actionSuite) TestWatchActionProgress(c *gc.C) { - defer s.setupMocks(c).Finish() - - s.blockCommandService.EXPECT().GetBlockSwitchedOn(gomock.Any(), gomock.Any()).Return("", blockcommanderrors.NotFound).AnyTimes() - - unit, err := s.ControllerModel(c).State().Unit("mysql/0") - c.Assert(err, jc.ErrorIsNil) - assertReadyToTest(c, unit) - - operationID, err := s.ControllerModel(c).EnqueueOperation("a test", 1) - c.Assert(err, jc.ErrorIsNil) - added, err := s.ControllerModel(c).AddAction(unit, operationID, "fakeaction", nil, nil, nil) - c.Assert(err, jc.ErrorIsNil) - - w, err := s.action.WatchActionsProgress( - context.Background(), - params.Entities{Entities: []params.Entity{{Tag: "action-2"}}}, - ) - c.Assert(err, jc.ErrorIsNil) - c.Assert(w.Results, gc.HasLen, 1) - c.Assert(w.Results[0].Error, gc.IsNil) - c.Assert(w.Results[0].Changes, gc.HasLen, 0) - - // Verify the resource was registered and stop when done - c.Assert(s.resources.Count(), gc.Equals, 1) - resource := s.resources.Get("1") - defer workertest.CleanKill(c, resource) - - // Check that the Watch has consumed the initial event - wc := watchertest.NewStringsWatcherC(c, resource.(state.StringsWatcher)) - wc.AssertNoChange() - - // Log a message and check the watcher result. - added, err = added.Begin() - c.Assert(err, jc.ErrorIsNil) - err = added.Log("hello") - c.Assert(err, jc.ErrorIsNil) - - a, err := s.ControllerModel(c).Action("2") - c.Assert(err, jc.ErrorIsNil) - logged := a.Messages() - c.Assert(logged, gc.HasLen, 1) - expected, err := json.Marshal(actions.ActionMessage{ - Message: logged[0].Message(), - Timestamp: logged[0].Timestamp(), - }) - c.Assert(err, jc.ErrorIsNil) - - wc.AssertChange(string(expected)) - wc.AssertNoChange() -} - -func (s *baseSuite) setupMocks(c *gc.C) *gomock.Controller { - ctrl := gomock.NewController(c) - - s.blockCommandService = action.NewMockBlockCommandService(ctrl) - s.applicationService = action.NewMockApplicationService(ctrl) - - s.authorizer = apiservertesting.FakeAuthorizer{ - Tag: jujutesting.AdminUser, - } - s.resources = common.NewResources() - s.AddCleanup(func(_ *gc.C) { s.resources.StopAll() }) - - var err error - s.action, err = action.NewActionAPI( - s.ControllerModel(c).State(), - s.resources, - s.authorizer, - action.FakeLeadership{}, - s.applicationService, - s.blockCommandService, - ) - c.Assert(err, jc.ErrorIsNil) - - f, release := s.NewFactory(c, s.ControllerModelUUID()) - defer release() - - s.charm = f.MakeCharm(c, &factory.CharmParams{ - Name: "wordpress", - }) - - s.dummy = f.MakeApplication(c, &factory.ApplicationParams{ - Name: "dummy", - Charm: f.MakeCharm(c, &factory.CharmParams{ - Name: "dummy", - }), - }) - s.wordpress = f.MakeApplication(c, &factory.ApplicationParams{ - Name: "wordpress", - Charm: s.charm, - }) - s.machine0 = f.MakeMachine(c, &factory.MachineParams{ - Base: state.UbuntuBase("12.10"), - Jobs: []state.MachineJob{state.JobHostUnits, state.JobManageModel}, - }) - s.wordpressUnit = f.MakeUnit(c, &factory.UnitParams{ - Application: s.wordpress, - Machine: s.machine0, - }) - - mysqlCharm := f.MakeCharm(c, &factory.CharmParams{ - Name: "mysql", - }) - s.mysql = f.MakeApplication(c, &factory.ApplicationParams{ - Name: "mysql", - Charm: mysqlCharm, - }) - s.machine1 = f.MakeMachine(c, &factory.MachineParams{ - Base: state.UbuntuBase("12.10"), - Jobs: []state.MachineJob{state.JobHostUnits}, - }) - s.mysqlUnit = f.MakeUnit(c, &factory.UnitParams{ - Application: s.mysql, - Machine: s.machine1, - }) - - return ctrl -} - -func assertReadyToTest(c *gc.C, receiver state.ActionReceiver) { - // make sure there are no actions on the receiver already. - actions, err := receiver.Actions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // make sure there are no actions pending already. - actions, err = receiver.PendingActions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // make sure there are no actions running already. - actions, err = receiver.RunningActions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) - - // make sure there are no actions completed already. - actions, err = receiver.CompletedActions() - c.Assert(err, jc.ErrorIsNil) - c.Assert(actions, gc.HasLen, 0) +func (s *actionSuite) TestStub(c *gc.C) { + c.Skip(`This suite is missing tests for the following scenarios: +- Enqueueing an action against multiple units, verifying persisted receivers/params/status etc. +- Enqueueing actions and cancelling a subset, verifying actions in the cancelled state. +- Beginning an action and then cancelling, verifying the aborting status. +- Ensuring the ApplicationsCharmsActions returns the correct result for input app tags. +- Enqueueing and watching action, observing its action messages as it progresses. +`) }