Files
team/server/cmd/mmctl/commands/job_e2e_test.go
eb0e00db97 Add job create, show, and cancel commands to mmctl (#37280)
* Add create, show, and cancel subcommands to mmctl job

The `mmctl job` command previously only supported `list` and `update`.
This adds three subcommands backed by existing client methods:

- `job create [type] [--data k=v]` triggers a new job (CreateJob)
- `job show [job]` displays a single job (GetJob)
- `job cancel [job]` requests cancellation via the dedicated cancel
  endpoint (CancelJob), which uses the create-job permission and emits
  its own audit event, unlike `update [job] cancel_requested`

Also fixes the leftover "import jobs" wording in the `list` flag help
and regenerates the mmctl docs.

* Exercise --data flag parsing in mmctl job create test

The create-job test passed the Data map as the flag's default value, so
GetStringToString returned the default without ever exercising pflag's
stringToString parser. Declare the flag empty and Set the value
explicitly so the test validates the real parse path.

* Don't gate mmctl job create on model.AllJobTypes

model.AllJobTypes (which IsValidJobType checks against) is a curated
subset that omits creatable job types such as access_control_sync, so
validating the type client-side wrongly rejected jobs the server can
schedule. Drop the client-side check and let the server validate the
type per its create-job permissions; it returns a clear error for
anything unschedulable. Tab-completion still suggests AllJobTypes as an
advisory hint.

* Remove explanatory AllJobTypes comments from mmctl job

* Add e2e tests for mmctl job create, show, and cancel

Cover the no-permission, not-found, and success paths for the new
generic job subcommands against a running server, mirroring the existing
export job e2e tests.

---------

Co-authored-by: Hanzo Dev <[email protected]>
2026-07-06 19:07:08 +02:00

143 lines
4.4 KiB
Go

// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
// See LICENSE.txt for license information.
package commands
import (
"time"
"github.com/mattermost/mattermost/server/public/model"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/client"
"github.com/mattermost/mattermost/server/v8/cmd/mmctl/printer"
"github.com/spf13/cobra"
)
func (s *MmctlE2ETestSuite) TestCreateJobCmdF() {
s.SetupTestHelper().InitBasic(s.T())
newCmd := func() *cobra.Command {
cmd := &cobra.Command{}
cmd.Flags().StringToString("data", nil, "")
return cmd
}
s.Run("create a job without permissions", func() {
printer.Clean()
err := createJobCmdF(s.th.Client, newCmd(), []string{model.JobTypeExportProcess})
s.Require().EqualError(err, "failed to create job: You do not have the appropriate permissions.")
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
})
s.RunForSystemAdminAndLocal("create a job", func(c client.Client) {
printer.Clean()
err := createJobCmdF(c, newCmd(), []string{model.JobTypeExportProcess})
s.Require().Nil(err)
s.Require().Empty(printer.GetErrorLines())
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(model.JobTypeExportProcess, printer.GetLines()[0].(*model.Job).Type)
})
s.RunForSystemAdminAndLocal("create a job with data", func(c client.Client) {
printer.Clean()
cmd := newCmd()
err := cmd.Flags().Set("data", "include_attachments=true")
s.Require().NoError(err)
err = createJobCmdF(c, cmd, []string{model.JobTypeExportProcess})
s.Require().Nil(err)
s.Require().Empty(printer.GetErrorLines())
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal("true", printer.GetLines()[0].(*model.Job).Data["include_attachments"])
})
}
func (s *MmctlE2ETestSuite) TestShowJobCmdF() {
s.SetupTestHelper().InitBasic(s.T())
job, appErr := s.th.App.CreateJob(s.th.Context, &model.Job{
Type: model.JobTypeExportProcess,
})
s.Require().Nil(appErr)
s.Run("show a job without permissions", func() {
printer.Clean()
err := showJobCmdF(s.th.Client, &cobra.Command{}, []string{job.Id})
s.Require().EqualError(err, "failed to get job: You do not have the appropriate permissions.")
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
})
s.RunForSystemAdminAndLocal("show a job that does not exist", func(c client.Client) {
printer.Clean()
err := showJobCmdF(c, &cobra.Command{}, []string{model.NewId()})
s.Require().ErrorContains(err, "failed to get job: Unable to get the job.")
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
})
s.RunForSystemAdminAndLocal("show a job", func(c client.Client) {
printer.Clean()
err := showJobCmdF(c, &cobra.Command{}, []string{job.Id})
s.Require().Nil(err)
s.Require().Empty(printer.GetErrorLines())
s.Require().Len(printer.GetLines(), 1)
s.Require().Equal(job, printer.GetLines()[0].(*model.Job))
})
}
func (s *MmctlE2ETestSuite) TestCancelJobCmdF() {
s.SetupTestHelper().InitBasic(s.T())
s.Run("cancel a job without permissions", func() {
printer.Clean()
job, appErr := s.th.App.CreateJob(s.th.Context, &model.Job{
Type: model.JobTypeExportProcess,
})
s.Require().Nil(appErr)
err := cancelJobCmdF(s.th.Client, &cobra.Command{}, []string{job.Id})
s.Require().EqualError(err, "failed to cancel job: You do not have the appropriate permissions.")
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
})
s.RunForSystemAdminAndLocal("cancel a job that does not exist", func(c client.Client) {
printer.Clean()
err := cancelJobCmdF(c, &cobra.Command{}, []string{model.NewId()})
s.Require().ErrorContains(err, "failed to cancel job: Unable to get the job.")
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
})
s.RunForSystemAdminAndLocal("cancel a job", func(c client.Client) {
printer.Clean()
job, appErr := s.th.App.CreateJob(s.th.Context, &model.Job{
Type: model.JobTypeExportProcess,
})
s.Require().Nil(appErr)
time.Sleep(time.Millisecond)
err := cancelJobCmdF(c, &cobra.Command{}, []string{job.Id})
s.Require().Nil(err)
s.Require().Empty(printer.GetLines())
s.Require().Empty(printer.GetErrorLines())
// Refresh the job to confirm it was canceled.
job, appErr = s.th.App.GetJob(s.th.Context, job.Id)
s.Require().Nil(appErr)
s.Require().Equal(model.JobStatusCanceled, job.Status)
})
}