github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/jobs/jobs_test.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package jobs
     5  
     6  import (
     7  	"testing"
     8  
     9  	"github.com/stretchr/testify/require"
    10  
    11  	"github.com/masterhung0112/hk_server/v5/einterfaces/mocks"
    12  	"github.com/masterhung0112/hk_server/v5/model"
    13  	"github.com/masterhung0112/hk_server/v5/store"
    14  	"github.com/masterhung0112/hk_server/v5/store/storetest/mockstore"
    15  	"github.com/masterhung0112/hk_server/v5/utils/testutils"
    16  )
    17  
    18  func makeJobServer(t *testing.T) (*JobServer, *mockstore.Store, *mocks.MetricsInterface) {
    19  	configService := &testutils.StaticConfigService{}
    20  
    21  	mockStore := &mockstore.Store{}
    22  	t.Cleanup(func() {
    23  		mockStore.AssertExpectations(t)
    24  	})
    25  
    26  	mockMetrics := &mocks.MetricsInterface{}
    27  	t.Cleanup(func() {
    28  		mockMetrics.AssertExpectations(t)
    29  	})
    30  
    31  	jobServer := NewJobServer(configService, mockStore, mockMetrics)
    32  
    33  	return jobServer, mockStore, mockMetrics
    34  }
    35  
    36  func expectErrorId(t *testing.T, errId string, appErr *model.AppError) {
    37  	t.Helper()
    38  	require.NotNil(t, appErr)
    39  	require.Equal(t, errId, appErr.Id)
    40  }
    41  
    42  func makeTeamEditionJobServer(t *testing.T) (*JobServer, *mockstore.Store) {
    43  	configService := &testutils.StaticConfigService{}
    44  
    45  	mockStore := &mockstore.Store{}
    46  	t.Cleanup(func() {
    47  		mockStore.AssertExpectations(t)
    48  	})
    49  
    50  	jobServer := NewJobServer(configService, mockStore, nil)
    51  
    52  	return jobServer, mockStore
    53  }
    54  
    55  func TestClaimJob(t *testing.T) {
    56  	t.Run("error claiming job", func(t *testing.T) {
    57  		jobServer, mockStore, _ := makeJobServer(t)
    58  
    59  		job := &model.Job{
    60  			Id:   "job_id",
    61  			Type: "job_type",
    62  		}
    63  
    64  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS).Return(false, &model.AppError{Message: "message"})
    65  
    66  		updated, err := jobServer.ClaimJob(job)
    67  		expectErrorId(t, "app.job.update.app_error", err)
    68  		require.False(t, updated)
    69  	})
    70  
    71  	t.Run("no existing job to update", func(t *testing.T) {
    72  		jobServer, mockStore, _ := makeJobServer(t)
    73  
    74  		job := &model.Job{
    75  			Id:   "job_id",
    76  			Type: "job_type",
    77  		}
    78  
    79  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS).Return(false, nil)
    80  
    81  		updated, err := jobServer.ClaimJob(job)
    82  		require.Nil(t, err)
    83  		require.False(t, updated)
    84  	})
    85  
    86  	t.Run("pending job updated", func(t *testing.T) {
    87  		jobServer, mockStore, mockMetrics := makeJobServer(t)
    88  
    89  		job := &model.Job{
    90  			Id:   "job_id",
    91  			Type: "job_type",
    92  		}
    93  
    94  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
    95  		mockMetrics.On("IncrementJobActive", "job_type")
    96  
    97  		updated, err := jobServer.ClaimJob(job)
    98  		require.Nil(t, err)
    99  		require.True(t, updated)
   100  	})
   101  
   102  	t.Run("pending job updated, nil metrics service", func(t *testing.T) {
   103  		jobServer, mockStore := makeTeamEditionJobServer(t)
   104  
   105  		job := &model.Job{
   106  			Id:   "job_id",
   107  			Type: "job_type",
   108  		}
   109  
   110  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
   111  
   112  		updated, err := jobServer.ClaimJob(job)
   113  		require.Nil(t, err)
   114  		require.True(t, updated)
   115  	})
   116  }
   117  
   118  func TestSetJobProgress(t *testing.T) {
   119  	t.Run("error setting progress", func(t *testing.T) {
   120  		jobServer, mockStore, _ := makeJobServer(t)
   121  
   122  		progress := int64(50)
   123  		job := &model.Job{
   124  			Id:   "job_id",
   125  			Type: "job_type",
   126  		}
   127  
   128  		job.Status = model.JOB_STATUS_IN_PROGRESS
   129  		job.Progress = progress
   130  
   131  		mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, &model.AppError{Message: "message"})
   132  
   133  		err := jobServer.SetJobProgress(job, progress)
   134  		expectErrorId(t, "app.job.update.app_error", err)
   135  	})
   136  
   137  	t.Run("progress updated", func(t *testing.T) {
   138  		jobServer, mockStore, _ := makeJobServer(t)
   139  
   140  		progress := int64(50)
   141  		job := &model.Job{
   142  			Id:   "job_id",
   143  			Type: "job_type",
   144  		}
   145  
   146  		job.Status = model.JOB_STATUS_IN_PROGRESS
   147  		job.Progress = progress
   148  
   149  		mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
   150  
   151  		err := jobServer.SetJobProgress(job, progress)
   152  		require.Nil(t, err)
   153  	})
   154  }
   155  
   156  func TestSetJobWarning(t *testing.T) {
   157  	t.Run("error setting status", func(t *testing.T) {
   158  		jobServer, mockStore, _ := makeJobServer(t)
   159  
   160  		job := &model.Job{
   161  			Id:   "job_id",
   162  			Type: "job_type",
   163  		}
   164  
   165  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_WARNING).Return(job, &model.AppError{Message: "message"})
   166  
   167  		err := jobServer.SetJobWarning(job)
   168  		expectErrorId(t, "app.job.update.app_error", err)
   169  	})
   170  
   171  	t.Run("status updated", func(t *testing.T) {
   172  		jobServer, mockStore, _ := makeJobServer(t)
   173  
   174  		job := &model.Job{
   175  			Id:   "job_id",
   176  			Type: "job_type",
   177  		}
   178  
   179  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_WARNING).Return(job, nil)
   180  
   181  		err := jobServer.SetJobWarning(job)
   182  		require.Nil(t, err)
   183  	})
   184  }
   185  
   186  func TestSetJobSuccess(t *testing.T) {
   187  	t.Run("error setting status", func(t *testing.T) {
   188  		jobServer, mockStore, _ := makeJobServer(t)
   189  
   190  		job := &model.Job{
   191  			Id:   "job_id",
   192  			Type: "job_type",
   193  		}
   194  
   195  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_SUCCESS).Return(job, &model.AppError{Message: "message"})
   196  
   197  		err := jobServer.SetJobSuccess(job)
   198  		expectErrorId(t, "app.job.update.app_error", err)
   199  	})
   200  
   201  	t.Run("status updated", func(t *testing.T) {
   202  		jobServer, mockStore, mockMetrics := makeJobServer(t)
   203  
   204  		job := &model.Job{
   205  			Id:   "job_id",
   206  			Type: "job_type",
   207  		}
   208  
   209  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_SUCCESS).Return(job, nil)
   210  		mockMetrics.On("DecrementJobActive", "job_type")
   211  
   212  		err := jobServer.SetJobSuccess(job)
   213  		require.Nil(t, err)
   214  	})
   215  
   216  	t.Run("status updated, nil metrics service", func(t *testing.T) {
   217  		jobServer, mockStore := makeTeamEditionJobServer(t)
   218  
   219  		job := &model.Job{
   220  			Id:   "job_id",
   221  			Type: "job_type",
   222  		}
   223  
   224  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_SUCCESS).Return(job, nil)
   225  
   226  		err := jobServer.SetJobSuccess(job)
   227  		require.Nil(t, err)
   228  	})
   229  }
   230  
   231  func TestSetJobError(t *testing.T) {
   232  	t.Run("nil provided job error", func(t *testing.T) {
   233  		t.Run("error setting status", func(t *testing.T) {
   234  			jobServer, mockStore, _ := makeJobServer(t)
   235  
   236  			job := &model.Job{
   237  				Id:   "job_id",
   238  				Type: "job_type",
   239  			}
   240  
   241  			mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_ERROR).Return(job, &model.AppError{Message: "message"})
   242  
   243  			err := jobServer.SetJobError(job, nil)
   244  			expectErrorId(t, "app.job.update.app_error", err)
   245  		})
   246  
   247  		t.Run("status updated", func(t *testing.T) {
   248  			jobServer, mockStore, mockMetrics := makeJobServer(t)
   249  
   250  			job := &model.Job{
   251  				Id:   "job_id",
   252  				Type: "job_type",
   253  			}
   254  
   255  			mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_ERROR).Return(job, nil)
   256  			mockMetrics.On("DecrementJobActive", "job_type")
   257  
   258  			err := jobServer.SetJobError(job, nil)
   259  			require.Nil(t, err)
   260  		})
   261  
   262  		t.Run("status updated, nil metrics service", func(t *testing.T) {
   263  			jobServer, mockStore := makeTeamEditionJobServer(t)
   264  
   265  			job := &model.Job{
   266  				Id:   "job_id",
   267  				Type: "job_type",
   268  			}
   269  
   270  			mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_ERROR).Return(job, nil)
   271  
   272  			err := jobServer.SetJobError(job, nil)
   273  			require.Nil(t, err)
   274  		})
   275  	})
   276  
   277  	t.Run("provided job error", func(t *testing.T) {
   278  		t.Run("error setting status", func(t *testing.T) {
   279  			jobServer, mockStore, _ := makeJobServer(t)
   280  
   281  			jobError := &model.AppError{Message: "message"}
   282  
   283  			job := &model.Job{
   284  				Id:       "job_id",
   285  				Type:     "job_type",
   286  				Progress: -1,
   287  				Data:     map[string]string{"error": jobError.Message},
   288  			}
   289  
   290  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, &model.AppError{Message: "message"})
   291  
   292  			err := jobServer.SetJobError(job, jobError)
   293  			expectErrorId(t, "app.job.update.app_error", err)
   294  		})
   295  
   296  		t.Run("status updated", func(t *testing.T) {
   297  			jobServer, mockStore, mockMetrics := makeJobServer(t)
   298  
   299  			jobError := &model.AppError{Message: "message"}
   300  
   301  			job := &model.Job{
   302  				Id:       "job_id",
   303  				Type:     "job_type",
   304  				Progress: -1,
   305  				Data:     map[string]string{"error": jobError.Message},
   306  			}
   307  
   308  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
   309  			mockMetrics.On("DecrementJobActive", "job_type")
   310  
   311  			err := jobServer.SetJobError(job, jobError)
   312  			require.Nil(t, err)
   313  		})
   314  
   315  		t.Run("status updated, nil metrics service", func(t *testing.T) {
   316  			jobServer, mockStore := makeTeamEditionJobServer(t)
   317  
   318  			jobError := &model.AppError{Message: "message"}
   319  
   320  			job := &model.Job{
   321  				Id:       "job_id",
   322  				Type:     "job_type",
   323  				Progress: -1,
   324  				Data:     map[string]string{"error": jobError.Message},
   325  			}
   326  
   327  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
   328  
   329  			err := jobServer.SetJobError(job, jobError)
   330  			require.Nil(t, err)
   331  		})
   332  
   333  		t.Run("status not updated, request cancellation, error setting status", func(t *testing.T) {
   334  			jobServer, mockStore, _ := makeJobServer(t)
   335  
   336  			jobError := &model.AppError{Message: "message"}
   337  
   338  			job := &model.Job{
   339  				Id:       "job_id",
   340  				Type:     "job_type",
   341  				Progress: -1,
   342  				Data:     map[string]string{"error": jobError.Message},
   343  			}
   344  
   345  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, nil)
   346  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_CANCEL_REQUESTED).Return(false, &model.AppError{Message: "message"})
   347  
   348  			err := jobServer.SetJobError(job, jobError)
   349  			expectErrorId(t, "app.job.update.app_error", err)
   350  		})
   351  
   352  		t.Run("status not updated, request cancellation, status not updated", func(t *testing.T) {
   353  			jobServer, mockStore, _ := makeJobServer(t)
   354  
   355  			jobError := &model.AppError{Message: "message"}
   356  
   357  			job := &model.Job{
   358  				Id:       "job_id",
   359  				Type:     "job_type",
   360  				Progress: -1,
   361  				Data:     map[string]string{"error": jobError.Message},
   362  			}
   363  
   364  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, nil)
   365  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_CANCEL_REQUESTED).Return(false, nil)
   366  
   367  			err := jobServer.SetJobError(job, jobError)
   368  			expectErrorId(t, "jobs.set_job_error.update.error", err)
   369  		})
   370  
   371  		t.Run("status not updated, request cancellation, status updated", func(t *testing.T) {
   372  			jobServer, mockStore, _ := makeJobServer(t)
   373  
   374  			jobError := &model.AppError{Message: "message"}
   375  
   376  			job := &model.Job{
   377  				Id:       "job_id",
   378  				Type:     "job_type",
   379  				Progress: -1,
   380  				Data:     map[string]string{"error": jobError.Message},
   381  			}
   382  
   383  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, nil)
   384  			mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_CANCEL_REQUESTED).Return(true, nil)
   385  
   386  			err := jobServer.SetJobError(job, jobError)
   387  			require.Nil(t, err)
   388  		})
   389  	})
   390  }
   391  
   392  func TestSetJobCanceled(t *testing.T) {
   393  	t.Run("error setting status", func(t *testing.T) {
   394  		jobServer, mockStore, _ := makeJobServer(t)
   395  
   396  		job := &model.Job{
   397  			Id:   "job_id",
   398  			Type: "job_type",
   399  		}
   400  
   401  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_CANCELED).Return(job, &model.AppError{Message: "message"})
   402  
   403  		err := jobServer.SetJobCanceled(job)
   404  		expectErrorId(t, "app.job.update.app_error", err)
   405  	})
   406  
   407  	t.Run("status updated", func(t *testing.T) {
   408  		jobServer, mockStore, mockMetrics := makeJobServer(t)
   409  
   410  		job := &model.Job{
   411  			Id:   "job_id",
   412  			Type: "job_type",
   413  		}
   414  
   415  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_CANCELED).Return(job, nil)
   416  		mockMetrics.On("DecrementJobActive", "job_type")
   417  
   418  		err := jobServer.SetJobCanceled(job)
   419  		require.Nil(t, err)
   420  	})
   421  
   422  	t.Run("status updated, nil metrics service", func(t *testing.T) {
   423  		jobServer, mockStore := makeTeamEditionJobServer(t)
   424  
   425  		job := &model.Job{
   426  			Id:   "job_id",
   427  			Type: "job_type",
   428  		}
   429  
   430  		mockStore.JobStore.On("UpdateStatus", "job_id", model.JOB_STATUS_CANCELED).Return(job, nil)
   431  
   432  		err := jobServer.SetJobCanceled(job)
   433  		require.Nil(t, err)
   434  	})
   435  }
   436  
   437  func TestUpdateInProgressJobData(t *testing.T) {
   438  	t.Run("error updating", func(t *testing.T) {
   439  		jobServer, mockStore, _ := makeJobServer(t)
   440  
   441  		job := &model.Job{
   442  			Id:   "job_id",
   443  			Type: "job_type",
   444  		}
   445  
   446  		job.Status = model.JOB_STATUS_IN_PROGRESS
   447  
   448  		mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(false, &model.AppError{Message: "message"})
   449  
   450  		err := jobServer.UpdateInProgressJobData(job)
   451  		expectErrorId(t, "app.job.update.app_error", err)
   452  	})
   453  
   454  	t.Run("progress updated", func(t *testing.T) {
   455  		jobServer, mockStore, _ := makeJobServer(t)
   456  
   457  		job := &model.Job{
   458  			Id:   "job_id",
   459  			Type: "job_type",
   460  		}
   461  
   462  		job.Status = model.JOB_STATUS_IN_PROGRESS
   463  
   464  		mockStore.JobStore.On("UpdateOptimistically", job, model.JOB_STATUS_IN_PROGRESS).Return(true, nil)
   465  
   466  		err := jobServer.UpdateInProgressJobData(job)
   467  		require.Nil(t, err)
   468  	})
   469  }
   470  
   471  func TestRequestCancellation(t *testing.T) {
   472  	t.Run("error cancelling", func(t *testing.T) {
   473  		jobServer, mockStore, _ := makeJobServer(t)
   474  
   475  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(false, &model.AppError{Message: "message"})
   476  
   477  		err := jobServer.RequestCancellation("job_id")
   478  		expectErrorId(t, "app.job.update.app_error", err)
   479  	})
   480  
   481  	t.Run("cancelled, job not found", func(t *testing.T) {
   482  		jobServer, mockStore, _ := makeJobServer(t)
   483  
   484  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(true, nil)
   485  		mockStore.JobStore.On("Get", "job_id").Return(nil, &store.ErrNotFound{})
   486  
   487  		err := jobServer.RequestCancellation("job_id")
   488  		expectErrorId(t, "app.job.update.app_error", err)
   489  	})
   490  
   491  	t.Run("cancelled, success", func(t *testing.T) {
   492  		jobServer, mockStore, mockMetrics := makeJobServer(t)
   493  
   494  		job := &model.Job{
   495  			Id:   "job_id",
   496  			Type: "job_type",
   497  		}
   498  
   499  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(true, nil)
   500  		mockStore.JobStore.On("Get", "job_id").Return(job, nil)
   501  		mockMetrics.On("DecrementJobActive", "job_type")
   502  
   503  		err := jobServer.RequestCancellation("job_id")
   504  		require.Nil(t, err)
   505  	})
   506  
   507  	t.Run("cancelled, success, nil metrics service", func(t *testing.T) {
   508  		jobServer, mockStore := makeTeamEditionJobServer(t)
   509  
   510  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(true, nil)
   511  
   512  		err := jobServer.RequestCancellation("job_id")
   513  		require.Nil(t, err)
   514  	})
   515  
   516  	t.Run("unable to cancel, requesting cancellation instead, error setting status", func(t *testing.T) {
   517  		jobServer, mockStore, _ := makeJobServer(t)
   518  
   519  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(false, nil)
   520  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_CANCEL_REQUESTED).Return(false, &model.AppError{Message: "message"})
   521  
   522  		err := jobServer.RequestCancellation("job_id")
   523  		expectErrorId(t, "app.job.update.app_error", err)
   524  	})
   525  
   526  	t.Run("unable to cancel, requesting cancellation instead, success", func(t *testing.T) {
   527  		jobServer, mockStore, _ := makeJobServer(t)
   528  
   529  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(false, nil)
   530  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_CANCEL_REQUESTED).Return(true, nil)
   531  
   532  		err := jobServer.RequestCancellation("job_id")
   533  		require.Nil(t, err)
   534  	})
   535  
   536  	t.Run("unable to cancel, requesting cancellation instead, unexpected state", func(t *testing.T) {
   537  		jobServer, mockStore, _ := makeJobServer(t)
   538  
   539  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_PENDING, model.JOB_STATUS_CANCELED).Return(false, nil)
   540  		mockStore.JobStore.On("UpdateStatusOptimistically", "job_id", model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_CANCEL_REQUESTED).Return(false, nil)
   541  
   542  		err := jobServer.RequestCancellation("job_id")
   543  		expectErrorId(t, "jobs.request_cancellation.status.error", err)
   544  	})
   545  }