github.com/oinume/lekcije@v0.0.0-20231017100347-5b4c5eb6ab24/backend/infrastructure/mysql/lesson_test.go (about)

     1  package mysql_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  	"time"
     7  
     8  	"github.com/google/go-cmp/cmp/cmpopts"
     9  
    10  	"github.com/oinume/lekcije/backend/domain/config"
    11  	"github.com/oinume/lekcije/backend/infrastructure/mysql"
    12  	"github.com/oinume/lekcije/backend/internal/assertion"
    13  	"github.com/oinume/lekcije/backend/internal/modeltest"
    14  	"github.com/oinume/lekcije/backend/internal/mysqltest"
    15  	"github.com/oinume/lekcije/backend/model2"
    16  	"github.com/oinume/lekcije/backend/randoms"
    17  )
    18  
    19  func Test_lessonRepository_FindAllByTeacherIDAndDatetimeAsMap(t *testing.T) {
    20  	repo := mysql.NewLessonRepository(helper.DB(t).DB())
    21  	repos := mysqltest.NewRepositories(helper.DB(t).DB())
    22  
    23  	type testCase struct {
    24  		teacherID  uint
    25  		lessonArgs []*model2.Lesson
    26  	}
    27  	tests := map[string]struct {
    28  		setup   func(ctx context.Context) *testCase
    29  		wantErr bool
    30  	}{
    31  		"normal": {
    32  			setup: func(ctx context.Context) *testCase {
    33  				//boil.DebugMode = true
    34  				//boil.DebugWriter = os.Stdout
    35  				teacher := modeltest.NewTeacher()
    36  				repos.CreateTeachers(ctx, t, teacher)
    37  
    38  				l1 := modeltest.NewLesson(func(l *model2.Lesson) {
    39  					l.TeacherID = teacher.ID
    40  					l.Datetime = time.Date(2022, 11, 1, 10, 0, 0, 0, time.UTC)
    41  				})
    42  				l2 := modeltest.NewLesson(func(l *model2.Lesson) {
    43  					l.TeacherID = teacher.ID
    44  					l.Datetime = time.Date(2022, 11, 1, 10, 30, 0, 0, time.UTC)
    45  				})
    46  				lessons := []*model2.Lesson{l1, l2}
    47  				repos.CreateLessons(ctx, t, lessons...)
    48  
    49  				return &testCase{
    50  					teacherID:  teacher.ID,
    51  					lessonArgs: lessons,
    52  				}
    53  			},
    54  		},
    55  	}
    56  	for name, tt := range tests {
    57  		t.Run(name, func(t *testing.T) {
    58  			ctx := context.Background()
    59  			tc := tt.setup(ctx)
    60  			gotLessonsMap, err := repo.FindAllByTeacherIDAndDatetimeAsMap(ctx, tc.teacherID, tc.lessonArgs)
    61  			if (err != nil) != tt.wantErr {
    62  				t.Fatalf("FindAllByTeacherIDAndDatetimeAsMap() error = %v, wantErr %v", err, tt.wantErr)
    63  			}
    64  
    65  			assertion.AssertEqual(t, len(tc.lessonArgs), len(gotLessonsMap), "lesson length doesn't match")
    66  			for _, l := range tc.lessonArgs {
    67  				datetime := model2.LessonDatetime(l.Datetime).String()
    68  				_, ok := gotLessonsMap[datetime]
    69  				if !ok {
    70  					t.Errorf("key %q must exist in result map", datetime)
    71  				}
    72  			}
    73  		})
    74  	}
    75  }
    76  
    77  func Test_lessonRepository_FindAllByTeacherIDsDatetimeBetween(t *testing.T) {
    78  	repo := mysql.NewLessonRepository(helper.DB(t).DB())
    79  	repos := mysqltest.NewRepositories(helper.DB(t).DB())
    80  
    81  	type testCase struct {
    82  		teacherID uint
    83  		fromDate  time.Time
    84  		toDate    time.Time
    85  		want      []*model2.Lesson
    86  	}
    87  	modeltest.NewFollowingTeacher()
    88  	tests := map[string]struct {
    89  		setup   func(ctx context.Context) *testCase
    90  		wantErr bool
    91  	}{
    92  		"normal": {
    93  			setup: func(ctx context.Context) *testCase {
    94  				teacherID := uint(randoms.MustNewInt64(10000000))
    95  				l1 := modeltest.NewLesson(func(l *model2.Lesson) {
    96  					l.TeacherID = teacherID
    97  					l.Datetime = time.Date(2022, 11, 1, 10, 0, 0, 0, config.DefaultVars.LocalLocation)
    98  				})
    99  				l2 := modeltest.NewLesson(func(l *model2.Lesson) {
   100  					l.TeacherID = teacherID
   101  					l.Datetime = time.Date(2022, 11, 2, 10, 30, 0, 0, config.DefaultVars.LocalLocation)
   102  				})
   103  				l3 := modeltest.NewLesson(func(l *model2.Lesson) {
   104  					l.TeacherID = teacherID
   105  					l.Datetime = time.Date(2022, 11, 5, 10, 30, 0, 0, config.DefaultVars.LocalLocation)
   106  				})
   107  				lessons := []*model2.Lesson{l1, l2, l3}
   108  				repos.CreateLessons(ctx, t, lessons...)
   109  
   110  				return &testCase{
   111  					teacherID: teacherID,
   112  					fromDate:  time.Date(2022, 11, 1, 10, 0, 0, 0, config.DefaultVars.LocalLocation),
   113  					toDate:    time.Date(2022, 11, 2, 10, 30, 0, 0, config.DefaultVars.LocalLocation),
   114  					want:      []*model2.Lesson{l1, l2}, // l3 doesn't hit
   115  				}
   116  			},
   117  		},
   118  		"no records": {
   119  			setup: func(ctx context.Context) *testCase {
   120  				teacherID := uint(randoms.MustNewInt64(10000000))
   121  				l1 := modeltest.NewLesson(func(l *model2.Lesson) {
   122  					l.TeacherID = teacherID
   123  					l.Datetime = time.Date(2022, 11, 1, 10, 0, 0, 0, config.DefaultVars.LocalLocation)
   124  				})
   125  				lessons := []*model2.Lesson{l1}
   126  				repos.CreateLessons(ctx, t, lessons...)
   127  
   128  				return &testCase{
   129  					teacherID: teacherID + 1,
   130  					fromDate:  time.Date(2022, 12, 1, 10, 0, 0, 0, config.DefaultVars.LocalLocation),
   131  					toDate:    time.Date(2022, 12, 2, 10, 30, 0, 0, config.DefaultVars.LocalLocation),
   132  					want:      nil,
   133  				}
   134  			},
   135  		},
   136  	}
   137  	for name, tt := range tests {
   138  		t.Run(name, func(t *testing.T) {
   139  			ctx := context.Background()
   140  			tc := tt.setup(ctx)
   141  			got, err := repo.FindAllByTeacherIDsDatetimeBetween(ctx, tc.teacherID, tc.fromDate, tc.toDate)
   142  			if (err != nil) != tt.wantErr {
   143  				t.Fatalf("FindAllByTeacherIDsDatetimeBetween() error = %v, wantErr %v", err, tt.wantErr)
   144  			}
   145  			assertion.AssertEqual(t, len(tc.want), len(got), "length of want and got is not same", cmpopts.EquateApproxTime(10*time.Second))
   146  			assertion.AssertEqual(t, tc.want, got, "", cmpopts.EquateApproxTime(10*time.Second))
   147  		})
   148  	}
   149  }
   150  
   151  func Test_lessonRepository_GetNewAvailableLessons(t *testing.T) {
   152  	repo := mysql.NewLessonRepository(helper.DB(t).DB())
   153  
   154  	type testCase struct {
   155  		oldLessons []*model2.Lesson
   156  		newLessons []*model2.Lesson
   157  		want       []*model2.Lesson
   158  	}
   159  	tests := map[string]struct {
   160  		setup func(ctx context.Context) *testCase
   161  	}{
   162  		"one available lessons": {
   163  			setup: func(ctx context.Context) *testCase {
   164  				teacherID := uint(randoms.MustNewInt64(100000))
   165  				datetime := time.Date(2016, 10, 1, 14, 30, 0, 0, config.LocalLocation())
   166  				lessons1 := newLessons(teacherID, datetime, "Reserved", 3)
   167  				lessons2 := newLessons(teacherID, datetime, "Reserved", 3)
   168  				lessons2[1].Status = "Available"
   169  				return &testCase{
   170  					oldLessons: lessons1,
   171  					newLessons: lessons2,
   172  					want: []*model2.Lesson{
   173  						lessons2[1],
   174  					},
   175  				}
   176  			},
   177  		},
   178  		"no available lessons": {
   179  			setup: func(ctx context.Context) *testCase {
   180  				teacherID := uint(randoms.MustNewInt64(100000))
   181  				datetime := time.Date(2016, 10, 1, 14, 30, 0, 0, config.LocalLocation())
   182  				lessons1 := newLessons(teacherID, datetime, "Reserved", 3)
   183  				lessons2 := newLessons(teacherID, datetime, "Reserved", 3)
   184  				// There are available lessons in old, means no difference between lessons1 and lessons2
   185  				lessons1[0].Status = "Available"
   186  				lessons2[0].Status = "Available"
   187  				return &testCase{
   188  					oldLessons: lessons1,
   189  					newLessons: lessons2,
   190  					want:       []*model2.Lesson{},
   191  				}
   192  			},
   193  		},
   194  	}
   195  	for name, tt := range tests {
   196  		t.Run(name, func(t *testing.T) {
   197  			ctx := context.Background()
   198  			tc := tt.setup(ctx)
   199  			got := repo.GetNewAvailableLessons(ctx, tc.oldLessons, tc.newLessons)
   200  			assertion.AssertEqual(t, len(tc.want), len(got), "length of lessons doesn't match")
   201  			assertion.AssertEqual(t, tc.want, got, "")
   202  		})
   203  	}
   204  }
   205  
   206  /* TODO: Add test case
   207  func TestLessonService_GetNewAvailableLessons1(t *testing.T) {
   208  	a := assert.New(t)
   209  
   210  	datetime := time.Date(2016, 10, 1, 14, 30, 0, 0, config.LocalLocation())
   211  	lessons1 := createLessons(1, datetime, "Reserved", 3)
   212  	lessons2 := createLessons(1, datetime, "Reserved", 3)
   213  	lessons2[1].Status = "Available"
   214  	// Test GetNewAvailableLessons returns a lesson when new lesson is "Available"
   215  	availableLessons := lessonService.GetNewAvailableLessons(context.Background(), lessons1, lessons2)
   216  	a.Equal(1, len(availableLessons))
   217  	a.Equal(datetime.Add(1*time.Hour), availableLessons[0].Datetime)
   218  }
   219  
   220  func TestLessonService_GetNewAvailableLessons2(t *testing.T) {
   221  	a := assert.New(t)
   222  
   223  	datetime := time.Date(2016, 10, 1, 14, 30, 0, 0, config.LocalLocation())
   224  	lessons1 := createLessons(1, datetime, "Reserved", 3)
   225  	lessons2 := createLessons(1, datetime, "Reserved", 3)
   226  	lessons1[0].Status = "Available"
   227  	lessons2[0].Status = "Available"
   228  	// Test GetNewAvailableLessons returns nothing when both lessons are "Available"
   229  	availableLessons := lessonService.GetNewAvailableLessons(context.Background(), lessons1, lessons2)
   230  	a.Equal(0, len(availableLessons))
   231  }
   232  */
   233  
   234  func newLessons(teacherID uint, baseDatetime time.Time, status string, length int) []*model2.Lesson {
   235  	lessons := make([]*model2.Lesson, length)
   236  	now := time.Now().UTC()
   237  	for i := range lessons {
   238  		lessons[i] = &model2.Lesson{
   239  			TeacherID: teacherID,
   240  			Datetime:  baseDatetime.Add(time.Duration(i) * time.Hour),
   241  			Status:    status,
   242  			CreatedAt: now,
   243  			UpdatedAt: now,
   244  		}
   245  	}
   246  	return lessons
   247  }
   248  
   249  func Test_lessonRepository_FindOrCreate(t *testing.T) {
   250  	db := helper.DB(t)
   251  	repo := mysql.NewLessonRepository(db.DB())
   252  	repos := mysqltest.NewRepositories(db.DB())
   253  
   254  	type testCase struct {
   255  		lesson *model2.Lesson
   256  	}
   257  	tests := map[string]struct {
   258  		setup func(ctx context.Context) *testCase
   259  	}{
   260  		"create new one": {
   261  			setup: func(ctx context.Context) *testCase {
   262  				l := modeltest.NewLesson()
   263  				return &testCase{
   264  					lesson: l,
   265  				}
   266  			},
   267  		},
   268  		"exist": {
   269  			setup: func(ctx context.Context) *testCase {
   270  				l := modeltest.NewLesson()
   271  				repos.CreateLessons(ctx, t, l)
   272  				return &testCase{
   273  					lesson: l,
   274  				}
   275  			},
   276  		},
   277  	}
   278  	for name, tt := range tests {
   279  		t.Run(name, func(t *testing.T) {
   280  			ctx := context.Background()
   281  			tc := tt.setup(ctx)
   282  			got, err := repo.FindOrCreate(ctx, tc.lesson, true)
   283  			if err != nil {
   284  				t.Fatalf("FindOrCreate failed: unexpected error = %v", err)
   285  			}
   286  			assertion.AssertEqual(t, tc.lesson, got, "", cmpopts.EquateApproxTime(10*time.Second))
   287  		})
   288  	}
   289  }
   290  
   291  func Test_lessonRepository_UpdateStatus(t *testing.T) {
   292  	db := helper.DB(t)
   293  	repo := mysql.NewLessonRepository(db.DB())
   294  	repos := mysqltest.NewRepositories(db.DB())
   295  
   296  	type testCase struct {
   297  		lesson    *model2.Lesson
   298  		newStatus string
   299  	}
   300  	tests := map[string]struct {
   301  		setup func(ctx context.Context) *testCase
   302  	}{
   303  		"normal": {
   304  			setup: func(ctx context.Context) *testCase {
   305  				l := modeltest.NewLesson(func(l *model2.Lesson) {
   306  					l.Status = "available"
   307  				})
   308  				repos.CreateLessons(ctx, t, l)
   309  				return &testCase{
   310  					lesson:    l,
   311  					newStatus: "reserved",
   312  				}
   313  			},
   314  		},
   315  	}
   316  	for name, tt := range tests {
   317  		t.Run(name, func(t *testing.T) {
   318  			ctx := context.Background()
   319  			tc := tt.setup(ctx)
   320  			_, err := repo.UpdateStatus(ctx, tc.lesson.ID, tc.newStatus)
   321  			if err != nil {
   322  				t.Fatalf("UpdateStatus failed: unexpected error = %v", err)
   323  			}
   324  			got, err := repo.FindByID(ctx, tc.lesson.ID)
   325  			if err != nil {
   326  				t.Fatalf("FindByID failed: unexpected error = %v", err)
   327  			}
   328  			assertion.AssertEqual(t, tc.newStatus, got.Status, "status is not updated")
   329  		})
   330  	}
   331  
   332  }