github.com/mattermost/mattermost-server/v5@v5.39.3/store/storetest/job_store.go (about)

     1  // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
     2  // See LICENSE.txt for license information.
     3  
     4  package storetest
     5  
     6  import (
     7  	"errors"
     8  	"testing"
     9  
    10  	"time"
    11  
    12  	"github.com/stretchr/testify/assert"
    13  	"github.com/stretchr/testify/require"
    14  
    15  	"github.com/mattermost/mattermost-server/v5/model"
    16  	"github.com/mattermost/mattermost-server/v5/store"
    17  )
    18  
    19  func TestJobStore(t *testing.T, ss store.Store) {
    20  	t.Run("JobSaveGet", func(t *testing.T) { testJobSaveGet(t, ss) })
    21  	t.Run("JobGetAllByType", func(t *testing.T) { testJobGetAllByType(t, ss) })
    22  	t.Run("JobGetAllByTypePage", func(t *testing.T) { testJobGetAllByTypePage(t, ss) })
    23  	t.Run("JobGetAllByTypesPage", func(t *testing.T) { testJobGetAllByTypesPage(t, ss) })
    24  	t.Run("JobGetAllPage", func(t *testing.T) { testJobGetAllPage(t, ss) })
    25  	t.Run("JobGetAllByStatus", func(t *testing.T) { testJobGetAllByStatus(t, ss) })
    26  	t.Run("GetNewestJobByStatusAndType", func(t *testing.T) { testJobStoreGetNewestJobByStatusAndType(t, ss) })
    27  	t.Run("GetNewestJobByStatusesAndType", func(t *testing.T) { testJobStoreGetNewestJobByStatusesAndType(t, ss) })
    28  	t.Run("GetCountByStatusAndType", func(t *testing.T) { testJobStoreGetCountByStatusAndType(t, ss) })
    29  	t.Run("JobUpdateOptimistically", func(t *testing.T) { testJobUpdateOptimistically(t, ss) })
    30  	t.Run("JobUpdateStatusUpdateStatusOptimistically", func(t *testing.T) { testJobUpdateStatusUpdateStatusOptimistically(t, ss) })
    31  	t.Run("JobDelete", func(t *testing.T) { testJobDelete(t, ss) })
    32  }
    33  
    34  func testJobSaveGet(t *testing.T, ss store.Store) {
    35  	job := &model.Job{
    36  		Id:     model.NewId(),
    37  		Type:   model.NewId(),
    38  		Status: model.NewId(),
    39  		Data: map[string]string{
    40  			"Processed":     "0",
    41  			"Total":         "12345",
    42  			"LastProcessed": "abcd",
    43  		},
    44  	}
    45  
    46  	_, err := ss.Job().Save(job)
    47  	require.NoError(t, err)
    48  
    49  	defer ss.Job().Delete(job.Id)
    50  
    51  	received, err := ss.Job().Get(job.Id)
    52  	require.NoError(t, err)
    53  	require.Equal(t, job.Id, received.Id, "received incorrect job after save")
    54  	require.Equal(t, "12345", received.Data["Total"])
    55  }
    56  
    57  func testJobGetAllByType(t *testing.T, ss store.Store) {
    58  	jobType := model.NewId()
    59  
    60  	jobs := []*model.Job{
    61  		{
    62  			Id:   model.NewId(),
    63  			Type: jobType,
    64  		},
    65  		{
    66  			Id:   model.NewId(),
    67  			Type: jobType,
    68  		},
    69  		{
    70  			Id:   model.NewId(),
    71  			Type: model.NewId(),
    72  		},
    73  	}
    74  
    75  	for _, job := range jobs {
    76  		_, err := ss.Job().Save(job)
    77  		require.NoError(t, err)
    78  		defer ss.Job().Delete(job.Id)
    79  	}
    80  
    81  	received, err := ss.Job().GetAllByType(jobType)
    82  	require.NoError(t, err)
    83  	require.Len(t, received, 2)
    84  	require.ElementsMatch(t, []string{jobs[0].Id, jobs[1].Id}, []string{received[0].Id, received[1].Id})
    85  }
    86  
    87  func testJobGetAllByTypePage(t *testing.T, ss store.Store) {
    88  	jobType := model.NewId()
    89  
    90  	jobs := []*model.Job{
    91  		{
    92  			Id:       model.NewId(),
    93  			Type:     jobType,
    94  			CreateAt: 1000,
    95  		},
    96  		{
    97  			Id:       model.NewId(),
    98  			Type:     jobType,
    99  			CreateAt: 999,
   100  		},
   101  		{
   102  			Id:       model.NewId(),
   103  			Type:     jobType,
   104  			CreateAt: 1001,
   105  		},
   106  		{
   107  			Id:       model.NewId(),
   108  			Type:     model.NewId(),
   109  			CreateAt: 1002,
   110  		},
   111  	}
   112  
   113  	for _, job := range jobs {
   114  		_, err := ss.Job().Save(job)
   115  		require.NoError(t, err)
   116  		defer ss.Job().Delete(job.Id)
   117  	}
   118  
   119  	received, err := ss.Job().GetAllByTypePage(jobType, 0, 2)
   120  	require.NoError(t, err)
   121  	require.Len(t, received, 2)
   122  	require.Equal(t, received[0].Id, jobs[2].Id, "should've received newest job first")
   123  	require.Equal(t, received[1].Id, jobs[0].Id, "should've received second newest job second")
   124  
   125  	received, err = ss.Job().GetAllByTypePage(jobType, 2, 2)
   126  	require.NoError(t, err)
   127  	require.Len(t, received, 1)
   128  	require.Equal(t, received[0].Id, jobs[1].Id, "should've received oldest job last")
   129  }
   130  
   131  func testJobGetAllByTypesPage(t *testing.T, ss store.Store) {
   132  	jobType := model.NewId()
   133  	jobType2 := model.NewId()
   134  
   135  	jobs := []*model.Job{
   136  		{
   137  			Id:       model.NewId(),
   138  			Type:     jobType,
   139  			CreateAt: 1000,
   140  		},
   141  		{
   142  			Id:       model.NewId(),
   143  			Type:     jobType,
   144  			CreateAt: 999,
   145  		},
   146  		{
   147  			Id:       model.NewId(),
   148  			Type:     jobType2,
   149  			CreateAt: 1001,
   150  		},
   151  		{
   152  			Id:       model.NewId(),
   153  			Type:     model.NewId(),
   154  			CreateAt: 1002,
   155  		},
   156  	}
   157  
   158  	for _, job := range jobs {
   159  		_, err := ss.Job().Save(job)
   160  		require.NoError(t, err)
   161  		defer ss.Job().Delete(job.Id)
   162  	}
   163  
   164  	// test return all
   165  	jobTypes := []string{jobType, jobType2}
   166  	received, err := ss.Job().GetAllByTypesPage(jobTypes, 0, 4)
   167  	require.NoError(t, err)
   168  	require.Len(t, received, 3)
   169  	require.Equal(t, received[0].Id, jobs[2].Id, "should've received newest job first")
   170  	require.Equal(t, received[1].Id, jobs[0].Id, "should've received second newest job second")
   171  
   172  	// test paging
   173  	jobTypes = []string{jobType, jobType2}
   174  	received, err = ss.Job().GetAllByTypesPage(jobTypes, 0, 2)
   175  	require.NoError(t, err)
   176  	require.Len(t, received, 2)
   177  	require.Equal(t, received[0].Id, jobs[2].Id, "should've received newest job first")
   178  	require.Equal(t, received[1].Id, jobs[0].Id, "should've received second newest job second")
   179  
   180  	received, err = ss.Job().GetAllByTypesPage(jobTypes, 2, 2)
   181  	require.NoError(t, err)
   182  	require.Len(t, received, 1)
   183  	require.Equal(t, received[0].Id, jobs[1].Id, "should've received oldest job last")
   184  }
   185  
   186  func testJobGetAllPage(t *testing.T, ss store.Store) {
   187  	jobType := model.NewId()
   188  	createAtTime := model.GetMillis()
   189  
   190  	jobs := []*model.Job{
   191  		{
   192  			Id:       model.NewId(),
   193  			Type:     jobType,
   194  			CreateAt: createAtTime + 1,
   195  		},
   196  		{
   197  			Id:       model.NewId(),
   198  			Type:     jobType,
   199  			CreateAt: createAtTime,
   200  		},
   201  		{
   202  			Id:       model.NewId(),
   203  			Type:     jobType,
   204  			CreateAt: createAtTime + 2,
   205  		},
   206  	}
   207  
   208  	for _, job := range jobs {
   209  		_, err := ss.Job().Save(job)
   210  		require.NoError(t, err)
   211  		defer ss.Job().Delete(job.Id)
   212  	}
   213  
   214  	received, err := ss.Job().GetAllPage(0, 2)
   215  	require.NoError(t, err)
   216  	require.Len(t, received, 2)
   217  	require.Equal(t, received[0].Id, jobs[2].Id, "should've received newest job first")
   218  	require.Equal(t, received[1].Id, jobs[0].Id, "should've received second newest job second")
   219  
   220  	received, err = ss.Job().GetAllPage(2, 2)
   221  	require.NoError(t, err)
   222  	require.NotEmpty(t, received)
   223  	require.Equal(t, received[0].Id, jobs[1].Id, "should've received oldest job last")
   224  }
   225  
   226  func testJobGetAllByStatus(t *testing.T, ss store.Store) {
   227  	jobType := model.NewId()
   228  	status := model.NewId()
   229  
   230  	jobs := []*model.Job{
   231  		{
   232  			Id:       model.NewId(),
   233  			Type:     jobType,
   234  			CreateAt: 1000,
   235  			Status:   status,
   236  			Data: map[string]string{
   237  				"test": "data",
   238  			},
   239  		},
   240  		{
   241  			Id:       model.NewId(),
   242  			Type:     jobType,
   243  			CreateAt: 999,
   244  			Status:   status,
   245  		},
   246  		{
   247  			Id:       model.NewId(),
   248  			Type:     jobType,
   249  			CreateAt: 1001,
   250  			Status:   status,
   251  		},
   252  		{
   253  			Id:       model.NewId(),
   254  			Type:     jobType,
   255  			CreateAt: 1002,
   256  			Status:   model.NewId(),
   257  		},
   258  	}
   259  
   260  	for _, job := range jobs {
   261  		_, err := ss.Job().Save(job)
   262  		require.NoError(t, err)
   263  		defer ss.Job().Delete(job.Id)
   264  	}
   265  
   266  	received, err := ss.Job().GetAllByStatus(status)
   267  	require.NoError(t, err)
   268  	require.Len(t, received, 3)
   269  	require.Equal(t, received[0].Id, jobs[1].Id)
   270  	require.Equal(t, received[1].Id, jobs[0].Id)
   271  	require.Equal(t, received[2].Id, jobs[2].Id)
   272  	require.Equal(t, "data", received[1].Data["test"], "should've received job data field back as saved")
   273  }
   274  
   275  func testJobStoreGetNewestJobByStatusAndType(t *testing.T, ss store.Store) {
   276  	jobType1 := model.NewId()
   277  	jobType2 := model.NewId()
   278  	status1 := model.NewId()
   279  	status2 := model.NewId()
   280  
   281  	jobs := []*model.Job{
   282  		{
   283  			Id:       model.NewId(),
   284  			Type:     jobType1,
   285  			CreateAt: 1001,
   286  			Status:   status1,
   287  		},
   288  		{
   289  			Id:       model.NewId(),
   290  			Type:     jobType1,
   291  			CreateAt: 1000,
   292  			Status:   status1,
   293  		},
   294  		{
   295  			Id:       model.NewId(),
   296  			Type:     jobType2,
   297  			CreateAt: 1003,
   298  			Status:   status1,
   299  		},
   300  		{
   301  			Id:       model.NewId(),
   302  			Type:     jobType1,
   303  			CreateAt: 1004,
   304  			Status:   status2,
   305  		},
   306  	}
   307  
   308  	for _, job := range jobs {
   309  		_, err := ss.Job().Save(job)
   310  		require.NoError(t, err)
   311  		defer ss.Job().Delete(job.Id)
   312  	}
   313  
   314  	received, err := ss.Job().GetNewestJobByStatusAndType(status1, jobType1)
   315  	assert.NoError(t, err)
   316  	assert.EqualValues(t, jobs[0].Id, received.Id)
   317  
   318  	received, err = ss.Job().GetNewestJobByStatusAndType(model.NewId(), model.NewId())
   319  	assert.Error(t, err)
   320  	var nfErr *store.ErrNotFound
   321  	assert.True(t, errors.As(err, &nfErr))
   322  	assert.Nil(t, received)
   323  }
   324  
   325  func testJobStoreGetNewestJobByStatusesAndType(t *testing.T, ss store.Store) {
   326  	jobType1 := model.NewId()
   327  	jobType2 := model.NewId()
   328  	status1 := model.NewId()
   329  	status2 := model.NewId()
   330  
   331  	jobs := []*model.Job{
   332  		{
   333  			Id:       model.NewId(),
   334  			Type:     jobType1,
   335  			CreateAt: 1001,
   336  			Status:   status1,
   337  		},
   338  		{
   339  			Id:       model.NewId(),
   340  			Type:     jobType1,
   341  			CreateAt: 1000,
   342  			Status:   status1,
   343  		},
   344  		{
   345  			Id:       model.NewId(),
   346  			Type:     jobType2,
   347  			CreateAt: 1003,
   348  			Status:   status1,
   349  		},
   350  		{
   351  			Id:       model.NewId(),
   352  			Type:     jobType1,
   353  			CreateAt: 1004,
   354  			Status:   status2,
   355  		},
   356  	}
   357  
   358  	for _, job := range jobs {
   359  		_, err := ss.Job().Save(job)
   360  		require.NoError(t, err)
   361  		defer ss.Job().Delete(job.Id)
   362  	}
   363  
   364  	received, err := ss.Job().GetNewestJobByStatusesAndType([]string{status1, status2}, jobType1)
   365  	assert.NoError(t, err)
   366  	assert.EqualValues(t, jobs[3].Id, received.Id)
   367  
   368  	received, err = ss.Job().GetNewestJobByStatusesAndType([]string{model.NewId(), model.NewId()}, model.NewId())
   369  	assert.Error(t, err)
   370  	var nfErr *store.ErrNotFound
   371  	assert.True(t, errors.As(err, &nfErr))
   372  	assert.Nil(t, received)
   373  
   374  	received, err = ss.Job().GetNewestJobByStatusesAndType([]string{status2}, jobType2)
   375  	assert.Error(t, err)
   376  	assert.True(t, errors.As(err, &nfErr))
   377  	assert.Nil(t, received)
   378  
   379  	received, err = ss.Job().GetNewestJobByStatusesAndType([]string{status1}, jobType2)
   380  	assert.NoError(t, err)
   381  	assert.EqualValues(t, jobs[2].Id, received.Id)
   382  
   383  	received, err = ss.Job().GetNewestJobByStatusesAndType([]string{}, jobType1)
   384  	assert.Error(t, err)
   385  	assert.True(t, errors.As(err, &nfErr))
   386  	assert.Nil(t, received)
   387  }
   388  
   389  func testJobStoreGetCountByStatusAndType(t *testing.T, ss store.Store) {
   390  	jobType1 := model.NewId()
   391  	jobType2 := model.NewId()
   392  	status1 := model.NewId()
   393  	status2 := model.NewId()
   394  
   395  	jobs := []*model.Job{
   396  		{
   397  			Id:       model.NewId(),
   398  			Type:     jobType1,
   399  			CreateAt: 1000,
   400  			Status:   status1,
   401  		},
   402  		{
   403  			Id:       model.NewId(),
   404  			Type:     jobType1,
   405  			CreateAt: 999,
   406  			Status:   status1,
   407  		},
   408  		{
   409  			Id:       model.NewId(),
   410  			Type:     jobType2,
   411  			CreateAt: 1001,
   412  			Status:   status1,
   413  		},
   414  		{
   415  			Id:       model.NewId(),
   416  			Type:     jobType1,
   417  			CreateAt: 1002,
   418  			Status:   status2,
   419  		},
   420  	}
   421  
   422  	for _, job := range jobs {
   423  		_, err := ss.Job().Save(job)
   424  		require.NoError(t, err)
   425  		defer ss.Job().Delete(job.Id)
   426  	}
   427  
   428  	count, err := ss.Job().GetCountByStatusAndType(status1, jobType1)
   429  	assert.NoError(t, err)
   430  	assert.EqualValues(t, 2, count)
   431  
   432  	count, err = ss.Job().GetCountByStatusAndType(status2, jobType2)
   433  	assert.NoError(t, err)
   434  	assert.EqualValues(t, 0, count)
   435  
   436  	count, err = ss.Job().GetCountByStatusAndType(status1, jobType2)
   437  	assert.NoError(t, err)
   438  	assert.EqualValues(t, 1, count)
   439  
   440  	count, err = ss.Job().GetCountByStatusAndType(status2, jobType1)
   441  	assert.NoError(t, err)
   442  	assert.EqualValues(t, 1, count)
   443  }
   444  
   445  func testJobUpdateOptimistically(t *testing.T, ss store.Store) {
   446  	job := &model.Job{
   447  		Id:       model.NewId(),
   448  		Type:     model.JOB_TYPE_DATA_RETENTION,
   449  		CreateAt: model.GetMillis(),
   450  		Status:   model.JOB_STATUS_PENDING,
   451  	}
   452  
   453  	_, err := ss.Job().Save(job)
   454  	require.NoError(t, err)
   455  	defer ss.Job().Delete(job.Id)
   456  
   457  	job.LastActivityAt = model.GetMillis()
   458  	job.Status = model.JOB_STATUS_IN_PROGRESS
   459  	job.Progress = 50
   460  	job.Data = map[string]string{
   461  		"Foo": "Bar",
   462  	}
   463  
   464  	updated, err := ss.Job().UpdateOptimistically(job, model.JOB_STATUS_SUCCESS)
   465  	require.False(t, err != nil && updated)
   466  
   467  	time.Sleep(2 * time.Millisecond)
   468  
   469  	updated, err = ss.Job().UpdateOptimistically(job, model.JOB_STATUS_PENDING)
   470  	require.NoError(t, err)
   471  	require.True(t, updated)
   472  
   473  	updatedJob, err := ss.Job().Get(job.Id)
   474  	require.NoError(t, err)
   475  
   476  	require.Equal(t, updatedJob.Type, job.Type)
   477  	require.Equal(t, updatedJob.CreateAt, job.CreateAt)
   478  	require.Equal(t, updatedJob.Status, job.Status)
   479  	require.Greater(t, updatedJob.LastActivityAt, job.LastActivityAt)
   480  	require.Equal(t, updatedJob.Progress, job.Progress)
   481  	require.Equal(t, updatedJob.Data["Foo"], job.Data["Foo"])
   482  }
   483  
   484  func testJobUpdateStatusUpdateStatusOptimistically(t *testing.T, ss store.Store) {
   485  	job := &model.Job{
   486  		Id:       model.NewId(),
   487  		Type:     model.JOB_TYPE_DATA_RETENTION,
   488  		CreateAt: model.GetMillis(),
   489  		Status:   model.JOB_STATUS_SUCCESS,
   490  	}
   491  
   492  	var lastUpdateAt int64
   493  	received, err := ss.Job().Save(job)
   494  	require.NoError(t, err)
   495  	lastUpdateAt = received.LastActivityAt
   496  
   497  	defer ss.Job().Delete(job.Id)
   498  
   499  	time.Sleep(2 * time.Millisecond)
   500  
   501  	received, err = ss.Job().UpdateStatus(job.Id, model.JOB_STATUS_PENDING)
   502  	require.NoError(t, err)
   503  
   504  	require.Equal(t, model.JOB_STATUS_PENDING, received.Status)
   505  	require.Greater(t, received.LastActivityAt, lastUpdateAt)
   506  	lastUpdateAt = received.LastActivityAt
   507  
   508  	time.Sleep(2 * time.Millisecond)
   509  
   510  	updated, err := ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_SUCCESS)
   511  	require.NoError(t, err)
   512  	require.False(t, updated)
   513  
   514  	received, err = ss.Job().Get(job.Id)
   515  	require.NoError(t, err)
   516  
   517  	require.Equal(t, model.JOB_STATUS_PENDING, received.Status)
   518  	require.Equal(t, received.LastActivityAt, lastUpdateAt)
   519  
   520  	time.Sleep(2 * time.Millisecond)
   521  
   522  	updated, err = ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_PENDING, model.JOB_STATUS_IN_PROGRESS)
   523  	require.NoError(t, err)
   524  	require.True(t, updated, "should have succeeded")
   525  
   526  	var startAtSet int64
   527  	received, err = ss.Job().Get(job.Id)
   528  	require.NoError(t, err)
   529  	require.Equal(t, model.JOB_STATUS_IN_PROGRESS, received.Status)
   530  	require.NotEqual(t, 0, received.StartAt)
   531  	require.Greater(t, received.LastActivityAt, lastUpdateAt)
   532  	lastUpdateAt = received.LastActivityAt
   533  	startAtSet = received.StartAt
   534  
   535  	time.Sleep(2 * time.Millisecond)
   536  
   537  	updated, err = ss.Job().UpdateStatusOptimistically(job.Id, model.JOB_STATUS_IN_PROGRESS, model.JOB_STATUS_SUCCESS)
   538  	require.NoError(t, err)
   539  	require.True(t, updated, "should have succeeded")
   540  
   541  	received, err = ss.Job().Get(job.Id)
   542  	require.NoError(t, err)
   543  	require.Equal(t, model.JOB_STATUS_SUCCESS, received.Status)
   544  	require.Equal(t, startAtSet, received.StartAt)
   545  	require.Greater(t, received.LastActivityAt, lastUpdateAt)
   546  }
   547  
   548  func testJobDelete(t *testing.T, ss store.Store) {
   549  	job, err := ss.Job().Save(&model.Job{Id: model.NewId()})
   550  	require.NoError(t, err)
   551  
   552  	_, err = ss.Job().Delete(job.Id)
   553  	assert.NoError(t, err)
   554  }