github.com/xzl8028/xenia-server@v0.0.0-20190809101854-18450a97da63/store/storetest/job_store.go (about)

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