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

     1  package web_test
     2  
     3  import (
     4  	"context"
     5  	"testing"
     6  
     7  	"connectrpc.com/connect"
     8  	"github.com/google/go-cmp/cmp"
     9  	"github.com/quickfeed/quickfeed/internal/qtest"
    10  	"github.com/quickfeed/quickfeed/qf"
    11  	"github.com/quickfeed/quickfeed/web/auth"
    12  	"github.com/quickfeed/quickfeed/web/interceptor"
    13  	"google.golang.org/protobuf/testing/protocmp"
    14  )
    15  
    16  func TestGetUsers(t *testing.T) {
    17  	db, cleanup := qtest.TestDB(t)
    18  	defer cleanup()
    19  	client := MockClient(t, db, nil)
    20  	ctx := context.Background()
    21  
    22  	unexpectedUsers, err := client.GetUsers(ctx, &connect.Request[qf.Void]{Msg: &qf.Void{}})
    23  	if err == nil && unexpectedUsers != nil && len(unexpectedUsers.Msg.GetUsers()) > 0 {
    24  		t.Fatalf("found unexpected users %+v", unexpectedUsers)
    25  	}
    26  
    27  	admin := qtest.CreateFakeUser(t, db)
    28  	user2 := qtest.CreateFakeUser(t, db)
    29  
    30  	foundUsers, err := client.GetUsers(ctx, &connect.Request[qf.Void]{Msg: &qf.Void{}})
    31  	if err != nil {
    32  		t.Fatal(err)
    33  	}
    34  
    35  	wantUsers := make([]*qf.User, 0)
    36  	wantUsers = append(wantUsers, admin, user2)
    37  	gotUsers := foundUsers.Msg.GetUsers()
    38  	if diff := cmp.Diff(wantUsers, gotUsers, protocmp.Transform()); diff != "" {
    39  		t.Errorf("GetUsers() mismatch (-wantUsers +gotUsers):\n%s", diff)
    40  	}
    41  }
    42  
    43  func TestGetEnrollmentsByCourse(t *testing.T) {
    44  	db, cleanup := qtest.TestDB(t)
    45  	defer cleanup()
    46  	client := MockClient(t, db, nil)
    47  	ctx := context.Background()
    48  
    49  	var users []*qf.User
    50  	for i := 0; i < 10; i++ {
    51  		user := qtest.CreateFakeUser(t, db)
    52  		users = append(users, user)
    53  	}
    54  	admin := users[0]
    55  	for _, course := range qtest.MockCourses {
    56  		err := db.CreateCourse(admin.ID, course)
    57  		if err != nil {
    58  			t.Fatal(err)
    59  		}
    60  	}
    61  
    62  	// users to enroll in course DAT520 Distributed Systems
    63  	// (excluding admin because admin is enrolled on creation)
    64  	wantUsers := users[0:6]
    65  	for i, user := range wantUsers {
    66  		if i == 0 {
    67  			// skip enrolling admin as student
    68  			continue
    69  		}
    70  		qtest.EnrollStudent(t, db, user, qtest.MockCourses[0])
    71  	}
    72  
    73  	// users to enroll in course DAT320 Operating Systems
    74  	// (excluding admin because admin is enrolled on creation)
    75  	osUsers := users[3:7]
    76  	for _, user := range osUsers {
    77  		qtest.EnrollStudent(t, db, user, qtest.MockCourses[1])
    78  	}
    79  
    80  	request := &connect.Request[qf.EnrollmentRequest]{
    81  		Msg: &qf.EnrollmentRequest{
    82  			FetchMode: &qf.EnrollmentRequest_CourseID{
    83  				CourseID: qtest.MockCourses[0].ID,
    84  			},
    85  		},
    86  	}
    87  	gotEnrollments, err := client.GetEnrollments(ctx, request)
    88  	if err != nil {
    89  		t.Error(err)
    90  	}
    91  	var gotUsers []*qf.User
    92  	for _, e := range gotEnrollments.Msg.Enrollments {
    93  		gotUsers = append(gotUsers, e.User)
    94  	}
    95  	if diff := cmp.Diff(wantUsers, gotUsers, protocmp.Transform()); diff != "" {
    96  		t.Errorf("GetEnrollmentsByCourse() mismatch (-wantUsers +gotUsers):\n%s", diff)
    97  	}
    98  }
    99  
   100  func TestUpdateUser(t *testing.T) {
   101  	db, cleanup := qtest.TestDB(t)
   102  	defer cleanup()
   103  	logger := qtest.Logger(t)
   104  
   105  	tm, err := auth.NewTokenManager(db)
   106  	if err != nil {
   107  		t.Fatal(err)
   108  	}
   109  	client := MockClient(t, db, connect.WithInterceptors(
   110  		interceptor.NewUserInterceptor(logger, tm),
   111  	))
   112  	ctx := context.Background()
   113  
   114  	firstAdminUser := qtest.CreateFakeUser(t, db)
   115  	nonAdminUser := qtest.CreateFakeUser(t, db)
   116  
   117  	firstAdminCookie, err := tm.NewAuthCookie(firstAdminUser.ID)
   118  	if err != nil {
   119  		t.Fatal(err)
   120  	}
   121  
   122  	// we want to update nonAdminUser to become admin
   123  	nonAdminUser.IsAdmin = true
   124  	err = db.UpdateUser(nonAdminUser)
   125  	if err != nil {
   126  		t.Fatal(err)
   127  	}
   128  
   129  	// we expect the nonAdminUser to now be admin
   130  	admin, err := db.GetUser(nonAdminUser.ID)
   131  	if err != nil {
   132  		t.Fatal(err)
   133  	}
   134  	if !admin.IsAdmin {
   135  		t.Error("expected nonAdminUser to have become admin")
   136  	}
   137  
   138  	nameChangeRequest := connect.NewRequest(&qf.User{
   139  		ID:        nonAdminUser.ID,
   140  		IsAdmin:   nonAdminUser.IsAdmin,
   141  		Name:      "Scrooge McDuck",
   142  		StudentID: "99",
   143  		Email:     "test@test.com",
   144  		AvatarURL: "www.hello.com",
   145  	})
   146  
   147  	nameChangeRequest.Header().Set(auth.Cookie, firstAdminCookie.String())
   148  	_, err = client.UpdateUser(ctx, nameChangeRequest)
   149  	if err != nil {
   150  		t.Error(err)
   151  	}
   152  	gotUser, err := db.GetUser(nonAdminUser.ID)
   153  	if err != nil {
   154  		t.Fatal(err)
   155  	}
   156  	wantUser := &qf.User{
   157  		ID:           gotUser.ID,
   158  		Name:         "Scrooge McDuck",
   159  		IsAdmin:      true,
   160  		StudentID:    "99",
   161  		Email:        "test@test.com",
   162  		AvatarURL:    "www.hello.com",
   163  		RefreshToken: nonAdminUser.RefreshToken,
   164  		ScmRemoteID:  nonAdminUser.ScmRemoteID,
   165  	}
   166  	if diff := cmp.Diff(wantUser, gotUser, protocmp.Transform()); diff != "" {
   167  		t.Errorf("UpdateUser() mismatch (-wantUser +gotUser):\n%s", diff)
   168  	}
   169  }
   170  
   171  func TestUpdateUserFailures(t *testing.T) {
   172  	db, cleanup := qtest.TestDB(t)
   173  	defer cleanup()
   174  	client, tm, _ := MockClientWithUser(t, db)
   175  	ctx := context.Background()
   176  
   177  	admin := qtest.CreateFakeCustomUser(t, db, &qf.User{Name: "admin", Login: "admin"})
   178  	if !admin.IsAdmin {
   179  		t.Fatalf("expected user %v to be admin", admin)
   180  	}
   181  	user := qtest.CreateFakeCustomUser(t, db, &qf.User{Name: "user", Login: "user"})
   182  	if user.IsAdmin {
   183  		t.Fatalf("expected user %v to be non-admin", user)
   184  	}
   185  	userCookie := Cookie(t, tm, user)
   186  	adminCookie := Cookie(t, tm, admin)
   187  	tests := []struct {
   188  		name     string
   189  		cookie   string
   190  		req      *qf.User
   191  		wantUser *qf.User
   192  		wantErr  bool
   193  	}{
   194  		{
   195  			name:   "user demotes admin, must fail",
   196  			cookie: userCookie,
   197  			req: &qf.User{
   198  				ID:        admin.ID,
   199  				IsAdmin:   false,
   200  				Name:      admin.Name,
   201  				Email:     admin.Email,
   202  				StudentID: admin.StudentID,
   203  				AvatarURL: admin.AvatarURL,
   204  			},
   205  			wantErr: true,
   206  		},
   207  		{
   208  			name:   "user promotes self to admin, must fail",
   209  			cookie: userCookie,
   210  			req: &qf.User{
   211  				ID:        user.ID,
   212  				Name:      user.Name,
   213  				Email:     user.Email,
   214  				StudentID: user.StudentID,
   215  				AvatarURL: user.AvatarURL,
   216  				IsAdmin:   true,
   217  			},
   218  			wantErr: true,
   219  		},
   220  		{
   221  			name:   "admin changes own name, must pass",
   222  			cookie: adminCookie,
   223  			req: &qf.User{
   224  				ID:   admin.ID,
   225  				Name: "super user",
   226  			},
   227  			wantUser: &qf.User{
   228  				ID:           admin.ID,
   229  				IsAdmin:      true,
   230  				Login:        admin.Login,
   231  				Name:         "super user",
   232  				RefreshToken: admin.RefreshToken,
   233  				ScmRemoteID:  admin.ScmRemoteID,
   234  			},
   235  			wantErr: false,
   236  		},
   237  	}
   238  	for _, tt := range tests {
   239  		t.Run(tt.name, func(t *testing.T) {
   240  			// UpdateUser returns void, so we cannot check that the user was updated
   241  			_, err := client.UpdateUser(ctx, qtest.RequestWithCookie(tt.req, tt.cookie))
   242  			if (err != nil) != tt.wantErr {
   243  				t.Errorf("%s: expected error: %v, got = %v", tt.name, tt.wantErr, err)
   244  			}
   245  			if !tt.wantErr {
   246  				// Instead (for success cases), get all users and check that the user was updated
   247  				users, err := client.GetUsers(ctx, qtest.RequestWithCookie(&qf.Void{}, tt.cookie))
   248  				if err != nil {
   249  					t.Fatal(err)
   250  				}
   251  				for _, u := range users.Msg.GetUsers() {
   252  					if u.ID == tt.wantUser.ID {
   253  						if diff := cmp.Diff(tt.wantUser, u, protocmp.Transform()); diff != "" {
   254  							t.Errorf("%s: UpdateUser() mismatch (-wantUser +gotUser):\n%s", tt.name, diff)
   255  						}
   256  					}
   257  				}
   258  			}
   259  		})
   260  	}
   261  }