// Copyright 2019 Drone.IO Inc. All rights reserved.
// Use of this source code is governed by the Drone Non-Commercial License
// that can be found in the LICENSE file.

package acl

import (
	"context"
	"encoding/json"
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/drone/drone/core"
	"github.com/drone/drone/handler/api/errors"
	"github.com/drone/drone/handler/api/request"
	"github.com/google/go-cmp/cmp"

	"github.com/go-chi/chi"
	"github.com/golang/mock/gomock"
)

var noContext = context.Background()

// this test verifies that a 401 unauthorized error is written to
// the response if the client is not authenticated and repository
// visibility is internal or private.
func TestCheckAccess_Guest_Unauthorized(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithRepo(noContext, mockRepo),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusUnauthorized; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrUnauthorized
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user is not authenticated BUT
// the repository is publicly visible.
func TestCheckAccess_Guest_PublicVisibility(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	mockRepo := *mockRepo
	mockRepo.Visibility = core.VisibilityPublic

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithRepo(noContext, &mockRepo),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 401 unauthorized error is written to
// the response if the repository visibility is internal, and the
// client is not authenticated.
func TestCheckAccess_Guest_InternalVisibility(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	mockRepo := *mockRepo
	mockRepo.Visibility = core.VisibilityInternal

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithRepo(noContext, &mockRepo),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusUnauthorized; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user is authenticated AND
// the repository is publicly visible.
func TestCheckAccess_Authenticated_PublicVisibility(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	mockRepo := *mockRepo
	mockRepo.Visibility = core.VisibilityPublic

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithUser(
			request.WithRepo(noContext, &mockRepo), mockUser),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user is authenticated AND
// the repository has internal visible.
func TestCheckAccess_Authenticated_InternalVisibility(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	mockRepo := *mockRepo
	mockRepo.Visibility = core.VisibilityInternal

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithUser(
			request.WithRepo(noContext, &mockRepo), mockUser),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 404 not found error is written to
// the response if the repository is not found AND the user is
// authenticated.
func TestCheckAccess_Authenticated_RepositoryNotFound(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusNotFound; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrNotFound
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies that a 404 not found error is written to
// the response if the user does not have permissions to access
// the repository.
func TestCheckAccess_Permission_NotFound(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithUser(
			request.WithRepo(noContext, mockRepo), mockUser),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusNotFound; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrNotFound
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user has read access to the
// repository.
func TestCheckReadAccess(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	readAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  false,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithUser(r.Context(), mockUser),
	)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			readAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 404 not found error is written to
// the response if the user lacks read access to the repository.
func TestCheckReadAccess_InsufficientPermissions(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	noAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   false,
		Write:  false,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			noAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckReadAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusNotFound; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrNotFound
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user has write access to the
// repository.
func TestCheckWriteAccess(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	writeAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  true,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			writeAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckWriteAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies the the next handler in the middleware
// chain is not processed if the user has write access BUT
// has been inactivated (e.g. blocked).
func TestCheckWriteAccess_InactiveUser(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	writeAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  true,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUserInactive,
			),
			writeAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckWriteAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Error("should not invoke handler")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusForbidden; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 404 not found error is written to
// the response if the user lacks write access to the repository.
//
// TODO(bradrydzewski) we should consider returning a 403 forbidden
// if the user has read access.
func TestCheckWriteAccess_InsufficientPermissions(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	noAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  false,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			noAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckWriteAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusNotFound; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrNotFound
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the user has admin access to the
// repository.
func TestCheckAdminAccess(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	noAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  true,
		Admin:  true,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			noAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckAdminAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 404 not found error is written to
// the response if the user lacks admin access to the repository.
//
// TODO(bradrydzewski) we should consider returning a 403 forbidden
// if the user has read access.
func TestCheckAdminAccess_InsufficientPermissions(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	noAccess := &core.Perm{
		Synced: time.Now().Unix(),
		Read:   true,
		Write:  true,
		Admin:  false,
	}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithPerm(
			request.WithUser(
				request.WithRepo(noContext, mockRepo),
				mockUser,
			),
			noAccess,
		),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckAdminAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusNotFound; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrNotFound
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies the the next handler in the middleware
// chain is processed if the authenticated user is a system
// administrator.
func TestCheckAdminAccess_SystemAdmin(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	user := &core.User{ID: 1, Admin: true, Active: true}

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithUser(r.Context(), user),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckAdminAccess())
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			w.WriteHeader(http.StatusTeapot)
		})
	})

	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusTeapot; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}
}

// this test verifies that a 401 unauthorized error is written to
// the response if the client is not authenticated and write
// access is required.
func TestCheckAccess_Guest_Write(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithRepo(noContext, mockRepo),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckAccess(true, true, false))
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})
	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusUnauthorized; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrUnauthorized
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// this test verifies that a 401 unauthorized error is written to
// the response if the client is not authenticated and admin
// access is required.
func TestCheckAccess_Guest_Admin(t *testing.T) {
	controller := gomock.NewController(t)
	defer controller.Finish()

	w := httptest.NewRecorder()
	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
	r = r.WithContext(
		request.WithRepo(noContext, mockRepo),
	)

	router := chi.NewRouter()
	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
		router.Use(CheckAccess(true, false, true))
		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
			t.Errorf("Must not invoke next handler in middleware chain")
		})
	})
	router.ServeHTTP(w, r)

	if got, want := w.Code, http.StatusUnauthorized; got != want {
		t.Errorf("Want status code %d, got %d", want, got)
	}

	got, want := new(errors.Error), errors.ErrUnauthorized
	json.NewDecoder(w.Body).Decode(got)
	if diff := cmp.Diff(got, want); len(diff) != 0 {
		t.Errorf(diff)
	}
}

// // this test verifies the the next handler in the middleware
// // chain is processed if the authenticated has read permissions
// // that are successfully synchronized with the source.
// func TestCheckAccess_RefreshPerms(t *testing.T) {
// 	controller := gomock.NewController(t)
// 	defer controller.Finish()

// 	expiredAccess := &core.Perm{
// 		Synced: 0,
// 		Read:   false,
// 		Write:  false,
// 		Admin:  false,
// 	}

// 	updatedAccess := &core.Perm{
// 		Read:  true,
// 		Write: true,
// 		Admin: true,
// 	}

// 	checkPermUpdate := func(ctx context.Context, perm *core.Perm) {
// 		if perm.Synced == 0 {
// 			t.Errorf("Expect synced timestamp updated")
// 		}
// 		if perm.Read == false {
// 			t.Errorf("Expect Read flag updated")
// 		}
// 		if perm.Write == false {
// 			t.Errorf("Expect Write flag updated")
// 		}
// 		if perm.Admin == false {
// 			t.Errorf("Expect Admin flag updated")
// 		}
// 	}

// 	repos := mock.NewMockRepositoryStore(controller)
// 	repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil)

// 	perms := mock.NewMockPermStore(controller)
// 	perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil)
// 	perms.EXPECT().Update(gomock.Any(), expiredAccess).Return(nil).Do(checkPermUpdate)

// 	service := mock.NewMockRepositoryService(controller)
// 	service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(updatedAccess, nil)

// 	factory := mock.NewMockRepositoryServiceFactory(controller)
// 	factory.EXPECT().Create(mockUser).Return(service)

// 	w := httptest.NewRecorder()
// 	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
// 	r = r.WithContext(
// 		request.WithUser(r.Context(), mockUser),
// 	)

// 	router := chi.NewRouter()
// 	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
// 		router.Use(CheckReadAccess(factory, repos, perms))
// 		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
// 			w.WriteHeader(http.StatusTeapot)
// 		})
// 	})

// 	router.ServeHTTP(w, r)

// 	if got, want := w.Code, http.StatusTeapot; got != want {
// 		t.Errorf("Want status code %d, got %d", want, got)
// 	}
// }

// // this test verifies that a 404 not found error is written to
// // the response if the user permissions are expired and the
// // updated permissions cannot be fetched.
// func TestCheckAccess_RefreshPerms_Error(t *testing.T) {
// 	controller := gomock.NewController(t)
// 	defer controller.Finish()

// 	expiredAccess := &core.Perm{
// 		Synced: 0,
// 		Read:   false,
// 		Write:  false,
// 		Admin:  false,
// 	}

// 	repos := mock.NewMockRepositoryStore(controller)
// 	repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil)

// 	perms := mock.NewMockPermStore(controller)
// 	perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil)

// 	service := mock.NewMockRepositoryService(controller)
// 	service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(nil, io.EOF)

// 	factory := mock.NewMockRepositoryServiceFactory(controller)
// 	factory.EXPECT().Create(mockUser).Return(service)

// 	w := httptest.NewRecorder()
// 	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
// 	r = r.WithContext(
// 		request.WithUser(r.Context(), mockUser),
// 	)

// 	router := chi.NewRouter()
// 	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
// 		router.Use(CheckReadAccess(factory, repos, perms))
// 		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
// 			w.WriteHeader(http.StatusTeapot)
// 		})
// 	})

// 	router.ServeHTTP(w, r)
// 	if got, want := w.Code, 404; got != want {
// 		t.Errorf("Want status code %d, got %d", want, got)
// 	}
// }

// // this test verifies the the next handler in the middleware
// // chain is processed if the user permissions are expired,
// // updated permissions are fetched, but fail the changes fail
// // to persist to the database. We know the user has access,
// // so we allow them to proceed even in the event of a failure.
// func TestCheckAccess_RefreshPerms_CannotSave(t *testing.T) {
// 	controller := gomock.NewController(t)
// 	defer controller.Finish()

// 	expiredAccess := &core.Perm{
// 		Synced: 0,
// 		Read:   false,
// 		Write:  false,
// 		Admin:  false,
// 	}

// 	updatedAccess := &core.Perm{
// 		Read:  true,
// 		Write: true,
// 		Admin: true,
// 	}

// 	service := mock.NewMockRepositoryService(controller)
// 	service.EXPECT().FindPerm(gomock.Any(), "octocat/hello-world").Return(updatedAccess, nil)

// 	factory := mock.NewMockRepositoryServiceFactory(controller)
// 	factory.EXPECT().Create(mockUser).Return(service)

// 	repos := mock.NewMockRepositoryStore(controller)
// 	repos.EXPECT().FindName(gomock.Any(), "octocat", "hello-world").Return(mockRepo, nil)

// 	perms := mock.NewMockPermStore(controller)
// 	perms.EXPECT().Find(gomock.Any(), mockRepo.UID, mockUser.ID).Return(expiredAccess, nil)
// 	perms.EXPECT().Update(gomock.Any(), expiredAccess).Return(io.EOF)

// 	w := httptest.NewRecorder()
// 	r := httptest.NewRequest("GET", "/api/repos/octocat/hello-world", nil)
// 	r = r.WithContext(
// 		request.WithUser(r.Context(), mockUser),
// 	)

// 	router := chi.NewRouter()
// 	router.Route("/api/repos/{owner}/{name}", func(router chi.Router) {
// 		router.Use(CheckReadAccess(factory, repos, perms))
// 		router.Get("/", func(w http.ResponseWriter, r *http.Request) {
// 			w.WriteHeader(http.StatusTeapot)
// 		})
// 	})

// 	router.ServeHTTP(w, r)
// 	if got, want := w.Code, http.StatusTeapot; got != want {
// 		t.Errorf("Want status code %d, got %d", want, got)
// 	}
// }