github.com/quickfeed/quickfeed@v0.0.0-20240507093252-ed8ca812a09c/web/groups_test.go (about)

     1  package web_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"github.com/google/go-cmp/cmp"
     8  	"google.golang.org/protobuf/testing/protocmp"
     9  
    10  	"github.com/quickfeed/quickfeed/internal/qtest"
    11  	"github.com/quickfeed/quickfeed/qf"
    12  )
    13  
    14  func TestNewGroup(t *testing.T) {
    15  	db, cleanup := qtest.TestDB(t)
    16  	defer cleanup()
    17  
    18  	client, tm, _ := MockClientWithUser(t, db)
    19  
    20  	admin := qtest.CreateFakeUser(t, db)
    21  	var course qf.Course
    22  	// only created 1 directory, if we had created two directories ID would be 2
    23  	course.ScmOrganizationID = 1
    24  	course.ScmOrganizationName = "test"
    25  	if err := db.CreateCourse(admin.ID, &course); err != nil {
    26  		t.Fatal(err)
    27  	}
    28  	user := qtest.CreateFakeUser(t, db)
    29  	qtest.EnrollStudent(t, db, user, &course)
    30  
    31  	ctx := context.Background()
    32  	// current user must be in the group being created
    33  	createGroupRequest := qtest.RequestWithCookie(&qf.Group{Name: "Heins-Group", CourseID: course.ID, Users: []*qf.User{{ID: user.ID}}}, Cookie(t, tm, user))
    34  	wantGroup, err := client.CreateGroup(ctx, createGroupRequest)
    35  	if err != nil {
    36  		t.Error(err)
    37  	}
    38  	gotGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{CourseID: course.ID, GroupID: wantGroup.Msg.ID}, Cookie(t, tm, user)))
    39  	if err != nil {
    40  		t.Error(err)
    41  	}
    42  	if diff := cmp.Diff(wantGroup.Msg, gotGroup.Msg, protocmp.Transform()); diff != "" {
    43  		t.Errorf("CreateGroup() mismatch (-wantGroup +gotGroup):\n%s", diff)
    44  	}
    45  }
    46  
    47  func TestCreateGroupWithMissingFields(t *testing.T) {
    48  	db, cleanup := qtest.TestDB(t)
    49  	defer cleanup()
    50  
    51  	client, tm, _ := MockClientWithUser(t, db)
    52  
    53  	admin := qtest.CreateFakeUser(t, db)
    54  	var course qf.Course
    55  	// only created 1 directory, if we had created two directories ID would be 2
    56  	course.ScmOrganizationID = 1
    57  	if err := db.CreateCourse(admin.ID, &course); err != nil {
    58  		t.Fatal(err)
    59  	}
    60  	user := qtest.CreateFakeUser(t, db)
    61  	qtest.EnrollStudent(t, db, user, &course)
    62  
    63  	users := []*qf.User{{ID: user.ID}}
    64  
    65  	ctx := context.Background()
    66  
    67  	// current user must be in the group being created
    68  	group_wo_course_id := qtest.RequestWithCookie(&qf.Group{Name: "Hein's Group", Users: users}, Cookie(t, tm, user))
    69  	_, err := client.CreateGroup(ctx, group_wo_course_id)
    70  	if err == nil {
    71  		t.Fatal("expected CreateGroup to fail without a course ID")
    72  	}
    73  	group_wo_name := qtest.RequestWithCookie(&qf.Group{CourseID: course.ID, Users: users}, Cookie(t, tm, user))
    74  	if group_wo_name.Msg.IsValid() {
    75  		// emulate CreateGroup check without name
    76  		t.Fatal("expected CreateGroup to fail without group name")
    77  	}
    78  	group_wo_users := qtest.RequestWithCookie(&qf.Group{Name: "Hein's Group", CourseID: course.ID}, Cookie(t, tm, user))
    79  	_, err = client.CreateGroup(ctx, group_wo_users)
    80  	if err == nil {
    81  		t.Fatal("expected CreateGroup to fail without users")
    82  	}
    83  }
    84  
    85  func TestNewGroupTeacherCreator(t *testing.T) {
    86  	db, cleanup := qtest.TestDB(t)
    87  	defer cleanup()
    88  
    89  	client, tm, _ := MockClientWithUser(t, db)
    90  
    91  	admin := qtest.CreateFakeUser(t, db)
    92  	var course qf.Course
    93  	// only created 1 directory, if we had created two directories ID would be 2
    94  	course.ScmOrganizationID = 1
    95  	if err := db.CreateCourse(admin.ID, &course); err != nil {
    96  		t.Fatal(err)
    97  	}
    98  
    99  	teacher := qtest.CreateFakeUser(t, db)
   100  	qtest.EnrollTeacher(t, db, teacher, &course)
   101  
   102  	user := qtest.CreateFakeUser(t, db)
   103  	qtest.EnrollStudent(t, db, user, &course)
   104  
   105  	users := []*qf.User{{ID: user.ID}}
   106  	ctx := context.Background()
   107  
   108  	// current user must be in the group being created
   109  	wantGroup, err := client.CreateGroup(ctx, qtest.RequestWithCookie(&qf.Group{Name: "HeinsGroup", CourseID: course.ID, Users: users}, Cookie(t, tm, user)))
   110  	if err != nil {
   111  		t.Error(err)
   112  	}
   113  	// check that group member (user) can access group
   114  	groupReq := &qf.GroupRequest{CourseID: course.ID, GroupID: wantGroup.Msg.ID}
   115  	gotGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(groupReq, Cookie(t, tm, user)))
   116  	if err != nil {
   117  		t.Error(err)
   118  	}
   119  	// check that teacher can access group
   120  	_, err = client.GetGroup(ctx, qtest.RequestWithCookie(groupReq, Cookie(t, tm, teacher)))
   121  	if err != nil {
   122  		t.Error(err)
   123  	}
   124  	// check that admin can access group
   125  	_, err = client.GetGroup(ctx, qtest.RequestWithCookie(groupReq, Cookie(t, tm, admin)))
   126  	if err != nil {
   127  		t.Error(err)
   128  	}
   129  
   130  	if diff := cmp.Diff(wantGroup.Msg, gotGroup.Msg, protocmp.Transform()); diff != "" {
   131  		t.Errorf("CreateGroup() mismatch (-wantGroup +gotGroup):\n%s", diff)
   132  	}
   133  }
   134  
   135  func TestNewGroupStudentCreateGroupWithTeacher(t *testing.T) {
   136  	db, cleanup := qtest.TestDB(t)
   137  	defer cleanup()
   138  
   139  	client, tm, _ := MockClientWithUser(t, db)
   140  
   141  	admin := qtest.CreateFakeUser(t, db)
   142  	var course qf.Course
   143  	// only created 1 directory, if we had created two directories ID would be 2
   144  	course.ScmOrganizationID = 1
   145  	if err := db.CreateCourse(admin.ID, &course); err != nil {
   146  		t.Fatal(err)
   147  	}
   148  
   149  	teacher := qtest.CreateFakeUser(t, db)
   150  	qtest.EnrollTeacher(t, db, teacher, &course)
   151  
   152  	user := qtest.CreateFakeUser(t, db)
   153  	qtest.EnrollStudent(t, db, user, &course)
   154  
   155  	// current user must be in the group being created
   156  	group_req := qtest.RequestWithCookie(&qf.Group{
   157  		Name:     "HeinsGroup",
   158  		CourseID: course.ID,
   159  		Users:    []*qf.User{{ID: user.ID}, {ID: teacher.ID}},
   160  	}, Cookie(t, tm, user))
   161  	_, err := client.CreateGroup(context.Background(), group_req)
   162  	if err != nil {
   163  		t.Error(err)
   164  	}
   165  	// we now allow teacher/student groups to be created,
   166  	// since if undesirable these can be rejected.
   167  }
   168  
   169  func TestStudentCreateNewGroupTeacherUpdateGroup(t *testing.T) {
   170  	db, cleanup := qtest.TestDB(t)
   171  	defer cleanup()
   172  
   173  	client, tm, _ := MockClientWithUser(t, db)
   174  
   175  	admin := qtest.CreateFakeUser(t, db)
   176  	course := qf.Course{ScmOrganizationID: 1, ScmOrganizationName: qtest.MockOrg}
   177  	if err := db.CreateCourse(admin.ID, &course); err != nil {
   178  		t.Fatal(err)
   179  	}
   180  
   181  	teacher := qtest.CreateFakeUser(t, db)
   182  	qtest.EnrollTeacher(t, db, teacher, &course)
   183  
   184  	user1 := qtest.CreateFakeUser(t, db)
   185  	qtest.EnrollStudent(t, db, user1, &course)
   186  
   187  	user2 := qtest.CreateFakeUser(t, db)
   188  	qtest.EnrollStudent(t, db, user2, &course)
   189  
   190  	user3 := qtest.CreateFakeUser(t, db)
   191  	qtest.EnrollStudent(t, db, user3, &course)
   192  
   193  	// set user1 in cookie, which is a group member
   194  	// group with two students
   195  	createGroupRequest := qtest.RequestWithCookie(&qf.Group{
   196  		Name:     "HeinsTwoMemberGroup",
   197  		CourseID: course.ID,
   198  		Users:    []*qf.User{user1, user2},
   199  	}, Cookie(t, tm, user1))
   200  
   201  	ctx := context.Background()
   202  	wantGroup, err := client.CreateGroup(ctx, createGroupRequest)
   203  	if err != nil {
   204  		t.Error(err)
   205  	}
   206  
   207  	gotGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   208  		CourseID: course.ID,
   209  		GroupID:  wantGroup.Msg.ID,
   210  	}, Cookie(t, tm, user1)))
   211  	if err != nil {
   212  		t.Error(err)
   213  	}
   214  
   215  	if diff := cmp.Diff(wantGroup.Msg, gotGroup.Msg, protocmp.Transform()); diff != "" {
   216  		t.Errorf("CreateGroup() mismatch (-wantGroup +gotGroup):\n%s", diff)
   217  	}
   218  
   219  	// ******************* Teacher UpdateGroup *******************
   220  
   221  	// set teacher in cookie
   222  	// group with three students
   223  	updateGroupRequest := qtest.RequestWithCookie(&qf.Group{
   224  		ID:       gotGroup.Msg.ID,
   225  		Name:     "Heins3MemberGroup",
   226  		CourseID: course.ID,
   227  		Users:    []*qf.User{user1, user2, user3},
   228  	}, Cookie(t, tm, teacher))
   229  	gotUpdatedGroup, err := client.UpdateGroup(ctx, updateGroupRequest)
   230  	if err != nil {
   231  		t.Error(err)
   232  	}
   233  
   234  	// check that the group have changed group membership
   235  	userIDs := make([]uint64, 0)
   236  	for _, usr := range updateGroupRequest.Msg.Users {
   237  		userIDs = append(userIDs, usr.ID)
   238  	}
   239  
   240  	grpUsers, err := db.GetUsers(userIDs...)
   241  	if err != nil {
   242  		t.Fatal(err)
   243  	}
   244  
   245  	wantGroup = gotGroup
   246  	wantGroup.Msg.Name = updateGroupRequest.Msg.Name
   247  	wantGroup.Msg.Users = grpUsers
   248  	wantGroup.Msg.ScmTeamID = 1
   249  	// UpdateGroup will autoApprove group on update
   250  	wantGroup.Msg.Status = qf.Group_APPROVED
   251  	// Ignore enrollments in check
   252  	gotUpdatedGroup.Msg.Enrollments = nil
   253  	wantGroup.Msg.Enrollments = nil
   254  
   255  	if diff := cmp.Diff(wantGroup.Msg, gotUpdatedGroup.Msg, protocmp.Transform()); diff != "" {
   256  		t.Errorf("UpdateGroup() mismatch (-wantGroup +gotUpdatedGroup):\n%s", diff)
   257  	}
   258  
   259  	// ******************* Teacher UpdateGroup *******************
   260  
   261  	// change group to only one student
   262  	// name must not update because group team and repo already exist
   263  	updateGroupRequest1 := qtest.RequestWithCookie(&qf.Group{
   264  		ID:       gotGroup.Msg.ID,
   265  		Name:     "Hein's single member Group",
   266  		CourseID: course.ID,
   267  		Users:    []*qf.User{user1},
   268  	}, Cookie(t, tm, teacher))
   269  	gotUpdatedGroup, err = client.UpdateGroup(ctx, updateGroupRequest1)
   270  	if err != nil {
   271  		t.Error(err)
   272  	}
   273  	// check that the group have changed group membership
   274  	userIDs = make([]uint64, 0)
   275  	for _, usr := range updateGroupRequest1.Msg.Users {
   276  		userIDs = append(userIDs, usr.ID)
   277  	}
   278  
   279  	grpUsers, err = db.GetUsers(userIDs...)
   280  	if err != nil {
   281  		t.Fatal(err)
   282  	}
   283  	if len(gotUpdatedGroup.Msg.Users) != 1 {
   284  		t.Errorf("Expected only single member group, got %d members", len(gotUpdatedGroup.Msg.Users))
   285  	}
   286  	wantGroup.Msg = updateGroupRequest.Msg
   287  	wantGroup.Msg.Users = grpUsers
   288  	wantGroup.Msg.ScmTeamID = 1
   289  	// UpdateGroup will autoApprove group on update
   290  	wantGroup.Msg.Status = qf.Group_APPROVED
   291  	gotUpdatedGroup.Msg.Enrollments = nil
   292  	wantGroup.Msg.Enrollments = nil
   293  
   294  	if diff := cmp.Diff(wantGroup.Msg, gotUpdatedGroup.Msg, protocmp.Transform()); diff != "" {
   295  		t.Errorf("UpdateGroup() mismatch (-wantGroup +gotUpdatedGroup):\n%s", diff)
   296  	}
   297  }
   298  
   299  func TestDeleteGroup(t *testing.T) {
   300  	db, cleanup := qtest.TestDB(t)
   301  	defer cleanup()
   302  
   303  	client, tm, _ := MockClientWithUser(t, db)
   304  	admin := qtest.CreateFakeCustomUser(t, db, &qf.User{Name: "admin", Login: "admin"})
   305  
   306  	ctx := context.Background()
   307  	testCourse, err := client.CreateCourse(ctx, qtest.RequestWithCookie(qtest.MockCourses[0], Cookie(t, tm, admin)))
   308  	if err != nil {
   309  		t.Error(err)
   310  	}
   311  	course := testCourse.Msg
   312  
   313  	// create user and enroll as pending (teacher)
   314  	teacher := qtest.CreateFakeCustomUser(t, db, &qf.User{Name: "teacher", Login: "teacher"})
   315  	if _, err := client.CreateEnrollment(ctx, qtest.RequestWithCookie(&qf.Enrollment{
   316  		UserID:   teacher.ID,
   317  		CourseID: course.ID,
   318  	}, Cookie(t, tm, teacher))); err != nil {
   319  		t.Error(err)
   320  	}
   321  
   322  	// update enrollment from pending->student->teacher; must be done by admin
   323  	if _, err := client.UpdateEnrollments(ctx, qtest.RequestWithCookie(&qf.Enrollments{
   324  		Enrollments: []*qf.Enrollment{
   325  			{
   326  				UserID:   teacher.ID,
   327  				CourseID: course.ID,
   328  				Status:   qf.Enrollment_STUDENT,
   329  			},
   330  		},
   331  	}, Cookie(t, tm, admin))); err != nil {
   332  		t.Error(err)
   333  	}
   334  
   335  	// update enrollment to teacher
   336  	if _, err := client.UpdateEnrollments(ctx, qtest.RequestWithCookie(&qf.Enrollments{
   337  		Enrollments: []*qf.Enrollment{
   338  			{
   339  				UserID:   teacher.ID,
   340  				CourseID: course.ID,
   341  				Status:   qf.Enrollment_TEACHER,
   342  			},
   343  		},
   344  	}, Cookie(t, tm, admin))); err != nil {
   345  		t.Error(err)
   346  	}
   347  
   348  	// create user and enroll as pending (student)
   349  	user := qtest.CreateFakeCustomUser(t, db, &qf.User{Name: "student", Login: "student"})
   350  	if _, err := client.CreateEnrollment(ctx, qtest.RequestWithCookie(&qf.Enrollment{
   351  		UserID:   user.ID,
   352  		CourseID: course.ID,
   353  	}, Cookie(t, tm, user))); err != nil {
   354  		t.Error(err)
   355  	}
   356  
   357  	// update pending enrollment to student; must be done by teacher
   358  	if _, err := client.UpdateEnrollments(ctx, qtest.RequestWithCookie(&qf.Enrollments{
   359  		Enrollments: []*qf.Enrollment{
   360  			{
   361  				UserID:   user.ID,
   362  				CourseID: course.ID,
   363  				Status:   qf.Enrollment_STUDENT,
   364  			},
   365  		},
   366  	}, Cookie(t, tm, teacher))); err != nil {
   367  		t.Error(err)
   368  	}
   369  
   370  	// create group as student user
   371  	group := &qf.Group{Name: "TestDeleteGroup", CourseID: course.ID, Users: []*qf.User{user}}
   372  	respGroup, err := client.CreateGroup(ctx, qtest.RequestWithCookie(group, Cookie(t, tm, user)))
   373  	if err != nil {
   374  		t.Fatal(err)
   375  	}
   376  
   377  	// delete group as teacher
   378  	_, err = client.DeleteGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   379  		GroupID:  respGroup.Msg.ID,
   380  		CourseID: course.ID,
   381  	}, Cookie(t, tm, teacher)))
   382  	if err != nil {
   383  		t.Error(err)
   384  	}
   385  }
   386  
   387  func TestGetGroup(t *testing.T) {
   388  	db, cleanup := qtest.TestDB(t)
   389  	defer cleanup()
   390  
   391  	client, tm, _ := MockClientWithUser(t, db)
   392  
   393  	testCourse := qf.Course{
   394  		Name:              "Distributed Systems",
   395  		Code:              "DAT520",
   396  		Year:              2018,
   397  		Tag:               "Spring",
   398  		ScmOrganizationID: 1,
   399  	}
   400  	admin := qtest.CreateFakeUser(t, db)
   401  	if err := db.CreateCourse(admin.ID, &testCourse); err != nil {
   402  		t.Fatal(err)
   403  	}
   404  
   405  	// create user and enroll as student
   406  	user := qtest.CreateFakeUser(t, db)
   407  	qtest.EnrollStudent(t, db, user, &testCourse)
   408  
   409  	ctx := context.Background()
   410  
   411  	group := &qf.Group{Name: "TestGroup", CourseID: testCourse.ID, Users: []*qf.User{user}}
   412  	wantGroup, err := client.CreateGroup(ctx, qtest.RequestWithCookie(group, Cookie(t, tm, user)))
   413  	if err != nil {
   414  		t.Error(err)
   415  	}
   416  
   417  	gotGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   418  		CourseID: testCourse.ID,
   419  		GroupID:  wantGroup.Msg.ID,
   420  	}, Cookie(t, tm, user)))
   421  	if err != nil {
   422  		t.Error(err)
   423  	}
   424  	if diff := cmp.Diff(wantGroup.Msg, gotGroup.Msg, protocmp.Transform()); diff != "" {
   425  		t.Errorf("CreateGroup() mismatch (-wantGroup +gotGroup):\n%s", diff)
   426  	}
   427  }
   428  
   429  func TestPatchGroupStatus(t *testing.T) {
   430  	db, cleanup := qtest.TestDB(t)
   431  	defer cleanup()
   432  
   433  	client, tm, _ := MockClientWithUser(t, db)
   434  
   435  	course := qf.Course{
   436  		Name:                "Distributed Systems",
   437  		Code:                "DAT520",
   438  		Year:                2018,
   439  		Tag:                 "Spring",
   440  		ScmOrganizationID:   1,
   441  		ScmOrganizationName: qtest.MockOrg,
   442  		ID:                  1,
   443  	}
   444  
   445  	admin := qtest.CreateFakeUser(t, db)
   446  	err := db.CreateCourse(admin.ID, &course)
   447  	if err != nil {
   448  		t.Fatal(err)
   449  	}
   450  
   451  	teacher := qtest.CreateFakeUser(t, db)
   452  	qtest.EnrollTeacher(t, db, teacher, &course)
   453  
   454  	if err := db.UpdateUser(&qf.User{ID: teacher.ID, IsAdmin: true}); err != nil {
   455  		t.Fatal(err)
   456  	}
   457  
   458  	ctx := context.Background()
   459  
   460  	user1 := qtest.CreateFakeUser(t, db)
   461  	user2 := qtest.CreateFakeUser(t, db)
   462  
   463  	// enroll users in course and group
   464  	qtest.EnrollStudent(t, db, user1, &course)
   465  	qtest.EnrollStudent(t, db, user2, &course)
   466  
   467  	group := &qf.Group{
   468  		ID:        1,
   469  		Name:      "Test Group",
   470  		CourseID:  course.ID,
   471  		Users:     []*qf.User{user1, user2},
   472  		ScmTeamID: 1,
   473  	}
   474  	err = db.CreateGroup(group)
   475  	if err != nil {
   476  		t.Fatal(err)
   477  	}
   478  	// get the group as stored in db with enrollments
   479  	wantGroup, err := db.GetGroup(group.ID)
   480  	if err != nil {
   481  		t.Fatal(err)
   482  	}
   483  
   484  	wantGroup.Status = qf.Group_APPROVED
   485  	gotGroup, err := client.UpdateGroup(ctx, qtest.RequestWithCookie(wantGroup, Cookie(t, tm, teacher)))
   486  	if err != nil {
   487  		t.Error(err)
   488  	}
   489  
   490  	if diff := cmp.Diff(wantGroup, gotGroup.Msg, protocmp.Transform()); diff != "" {
   491  		t.Errorf("UpdateGroup() mismatch (-wantGroup +gotGroup):\n%s", diff)
   492  	}
   493  }
   494  
   495  func TestGetGroupByUserAndCourse(t *testing.T) {
   496  	db, cleanup := qtest.TestDB(t)
   497  	defer cleanup()
   498  
   499  	client, tm, _ := MockClientWithUser(t, db)
   500  
   501  	course := qf.Course{
   502  		Name:              "Distributed Systems",
   503  		Code:              "DAT520",
   504  		Year:              2018,
   505  		Tag:               "Spring",
   506  		ScmOrganizationID: 1,
   507  		ID:                1,
   508  	}
   509  
   510  	admin := qtest.CreateFakeUser(t, db)
   511  	err := db.CreateCourse(admin.ID, &course)
   512  	if err != nil {
   513  		t.Fatal(err)
   514  	}
   515  
   516  	ctx := context.Background()
   517  
   518  	user1 := qtest.CreateFakeUser(t, db)
   519  	user2 := qtest.CreateFakeUser(t, db)
   520  
   521  	// enroll users in course and group
   522  	qtest.EnrollStudent(t, db, user1, &course)
   523  	qtest.EnrollStudent(t, db, user2, &course)
   524  
   525  	group := &qf.Group{
   526  		CourseID: course.ID,
   527  		Users:    []*qf.User{user1, user2},
   528  	}
   529  	err = db.CreateGroup(group)
   530  	if err != nil {
   531  		t.Fatal(err)
   532  	}
   533  
   534  	wantGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   535  		CourseID: course.ID,
   536  		UserID:   user1.ID,
   537  	}, Cookie(t, tm, admin)))
   538  	if err != nil {
   539  		t.Error(err)
   540  	}
   541  	gotGroup, err := client.GetGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   542  		CourseID: course.ID,
   543  		GroupID:  group.ID,
   544  	}, Cookie(t, tm, admin)))
   545  	if err != nil {
   546  		t.Error(err)
   547  	}
   548  	if diff := cmp.Diff(wantGroup.Msg, gotGroup.Msg, protocmp.Transform()); diff != "" {
   549  		t.Errorf("GetGroupByUserAndCourse() mismatch (-wantGroup +gotGroup):\n%s", diff)
   550  	}
   551  }
   552  
   553  func TestDeleteApprovedGroup(t *testing.T) {
   554  	db, cleanup := qtest.TestDB(t)
   555  	defer cleanup()
   556  
   557  	client, tm, _ := MockClientWithUser(t, db)
   558  
   559  	admin := qtest.CreateFakeUser(t, db)
   560  	course := qtest.MockCourses[0]
   561  	qtest.CreateCourse(t, db, admin, course)
   562  
   563  	user1 := qtest.CreateFakeUser(t, db)
   564  	user2 := qtest.CreateFakeUser(t, db)
   565  
   566  	// enroll users in course and group
   567  	qtest.EnrollStudent(t, db, user1, course)
   568  	qtest.EnrollStudent(t, db, user2, course)
   569  
   570  	group := &qf.Group{
   571  		ID:       1,
   572  		CourseID: course.ID,
   573  		Name:     "TestGroup",
   574  		Users:    []*qf.User{user1, user2},
   575  	}
   576  	// current user1 must be in the group being created
   577  	ctx := context.Background()
   578  	createdGroup, err := client.CreateGroup(ctx, qtest.RequestWithCookie(group, Cookie(t, tm, user1)))
   579  	if err != nil {
   580  		t.Error(err)
   581  	}
   582  
   583  	// first approve the group
   584  	createdGroup.Msg.Status = qf.Group_APPROVED
   585  	// current user must be teacher for the course
   586  	if _, err = client.UpdateGroup(ctx, qtest.RequestWithCookie(createdGroup.Msg, Cookie(t, tm, admin))); err != nil {
   587  		t.Error(err)
   588  	}
   589  
   590  	// then get user enrollments with group ID
   591  	wantEnrollment1, err := db.GetEnrollmentByCourseAndUser(course.ID, user1.ID)
   592  	if err != nil {
   593  		t.Fatal(err)
   594  	}
   595  	wantEnrollment2, err := db.GetEnrollmentByCourseAndUser(course.ID, user2.ID)
   596  	if err != nil {
   597  		t.Fatal(err)
   598  	}
   599  
   600  	// delete the group
   601  	if _, err = client.DeleteGroup(ctx, qtest.RequestWithCookie(&qf.GroupRequest{
   602  		CourseID: course.ID,
   603  		GroupID:  createdGroup.Msg.ID,
   604  	}, Cookie(t, tm, admin))); err != nil {
   605  		t.Error(err)
   606  	}
   607  
   608  	// get updated enrollments of group members
   609  	gotEnrollment1, err := db.GetEnrollmentByCourseAndUser(course.ID, user1.ID)
   610  	if err != nil {
   611  		t.Fatal(err)
   612  	}
   613  	gotEnrollment2, err := db.GetEnrollmentByCourseAndUser(course.ID, user2.ID)
   614  	if err != nil {
   615  		t.Fatal(err)
   616  	}
   617  
   618  	// now nullify manually group ID for original enrollments
   619  	wantEnrollment1.GroupID = 0
   620  	wantEnrollment2.GroupID = 0
   621  
   622  	// then check that new enrollments have group IDs nullified automatically
   623  	if diff := cmp.Diff(wantEnrollment1, gotEnrollment1, protocmp.Transform()); diff != "" {
   624  		t.Errorf("DeleteGroup() mismatch (-wantEnrollment1 +gotEnrollment1):\n%s", diff)
   625  	}
   626  	if diff := cmp.Diff(wantEnrollment2, gotEnrollment2, protocmp.Transform()); diff != "" {
   627  		t.Errorf("DeleteGroup() mismatch (-wantEnrollment2 +gotEnrollment2):\n%s", diff)
   628  	}
   629  }
   630  
   631  func TestGetGroups(t *testing.T) {
   632  	db, cleanup := qtest.TestDB(t)
   633  	defer cleanup()
   634  
   635  	client, tm, _ := MockClientWithUser(t, db)
   636  
   637  	var users []*qf.User
   638  	for i := 0; i < 10; i++ {
   639  		user := qtest.CreateFakeUser(t, db)
   640  		users = append(users, user)
   641  	}
   642  	admin := users[0]
   643  
   644  	// admin will be enrolled as teacher because of course creation below
   645  	course := qtest.MockCourses[1]
   646  	err := db.CreateCourse(admin.ID, course)
   647  	if err != nil {
   648  		t.Fatal(err)
   649  	}
   650  
   651  	// enroll all users in course
   652  	for _, user := range users[1:] {
   653  		qtest.EnrollStudent(t, db, user, course)
   654  	}
   655  	// place some students in groups
   656  	// current user must be in the group being created
   657  	ctx := context.Background()
   658  	group1, err := client.CreateGroup(ctx, qtest.RequestWithCookie(&qf.Group{
   659  		Name:     "Group1",
   660  		CourseID: course.ID,
   661  		Users:    []*qf.User{users[1], users[2]},
   662  	}, Cookie(t, tm, users[2])))
   663  	if err != nil {
   664  		t.Error(err)
   665  	}
   666  	group2, err := client.CreateGroup(ctx, qtest.RequestWithCookie(&qf.Group{
   667  		Name:     "Group2",
   668  		CourseID: course.ID,
   669  		Users:    []*qf.User{users[4], users[5]},
   670  	}, Cookie(t, tm, users[5])))
   671  	if err != nil {
   672  		t.Error(err)
   673  	}
   674  	wantGroups := &qf.Groups{Groups: []*qf.Group{group1.Msg, group2.Msg}}
   675  	for _, grp := range wantGroups.Groups {
   676  		for _, grpEnrol := range grp.Enrollments {
   677  			grpEnrol.UsedSlipDays = []*qf.UsedSlipDays{}
   678  		}
   679  	}
   680  
   681  	// get groups from the database; current user is admin, which is also teacher
   682  	gotGroups, err := client.GetGroupsByCourse(ctx, qtest.RequestWithCookie(&qf.CourseRequest{
   683  		CourseID: course.ID,
   684  	}, Cookie(t, tm, admin)))
   685  	if err != nil {
   686  		t.Error(err)
   687  	}
   688  
   689  	// check that the method returns expected groups
   690  	if diff := cmp.Diff(wantGroups, gotGroups.Msg, protocmp.Transform()); diff != "" {
   691  		t.Errorf("GetGroupsByCourse() mismatch (-wantGroups +gotGroups):\n%s", diff)
   692  	}
   693  }