Decouple the different contexts from each other (#24786)
Replace #16455 Close #21803 Mixing different Gitea contexts together causes some problems: 1. Unable to respond proper content when error occurs, eg: Web should respond HTML while API should respond JSON 2. Unclear dependency, eg: it's unclear when Context is used in APIContext, which fields should be initialized, which methods are necessary. To make things clear, this PR introduces a Base context, it only provides basic Req/Resp/Data features. This PR mainly moves code. There are still many legacy problems and TODOs in code, leave unrelated changes to future PRs.
This commit is contained in:
parent
6ba4f89723
commit
6b33152b7d
57 changed files with 885 additions and 781 deletions
|
@ -4,7 +4,7 @@
|
|||
package test
|
||||
|
||||
import (
|
||||
scontext "context"
|
||||
gocontext "context"
|
||||
"io"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
|
@ -28,18 +28,7 @@ import (
|
|||
// MockContext mock context for unit tests
|
||||
// TODO: move this function to other packages, because it depends on "models" package
|
||||
func MockContext(t *testing.T, path string) *context.Context {
|
||||
resp := &mockResponseWriter{}
|
||||
ctx := context.Context{
|
||||
Render: &mockRender{},
|
||||
Data: make(middleware.ContextData),
|
||||
Flash: &middleware.Flash{
|
||||
Values: make(url.Values),
|
||||
},
|
||||
Resp: context.NewResponse(resp),
|
||||
Locale: &translation.MockLocale{},
|
||||
}
|
||||
defer ctx.Close()
|
||||
|
||||
resp := httptest.NewRecorder()
|
||||
requestURL, err := url.Parse(path)
|
||||
assert.NoError(t, err)
|
||||
req := &http.Request{
|
||||
|
@ -47,41 +36,105 @@ func MockContext(t *testing.T, path string) *context.Context {
|
|||
Form: url.Values{},
|
||||
}
|
||||
|
||||
base, baseCleanUp := context.NewBaseContext(resp, req)
|
||||
base.Data = middleware.ContextData{}
|
||||
base.Locale = &translation.MockLocale{}
|
||||
ctx := &context.Context{
|
||||
Base: base,
|
||||
Render: &mockRender{},
|
||||
Flash: &middleware.Flash{Values: url.Values{}},
|
||||
}
|
||||
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
|
||||
|
||||
chiCtx := chi.NewRouteContext()
|
||||
req = req.WithContext(scontext.WithValue(req.Context(), chi.RouteCtxKey, chiCtx))
|
||||
ctx.Req = context.WithContext(req, &ctx)
|
||||
return &ctx
|
||||
ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// MockAPIContext mock context for unit tests
|
||||
// TODO: move this function to other packages, because it depends on "models" package
|
||||
func MockAPIContext(t *testing.T, path string) *context.APIContext {
|
||||
resp := httptest.NewRecorder()
|
||||
requestURL, err := url.Parse(path)
|
||||
assert.NoError(t, err)
|
||||
req := &http.Request{
|
||||
URL: requestURL,
|
||||
Form: url.Values{},
|
||||
}
|
||||
|
||||
base, baseCleanUp := context.NewBaseContext(resp, req)
|
||||
base.Data = middleware.ContextData{}
|
||||
base.Locale = &translation.MockLocale{}
|
||||
ctx := &context.APIContext{Base: base}
|
||||
_ = baseCleanUp // during test, it doesn't need to do clean up. TODO: this can be improved later
|
||||
|
||||
chiCtx := chi.NewRouteContext()
|
||||
ctx.Base.AppendContextValue(chi.RouteCtxKey, chiCtx)
|
||||
return ctx
|
||||
}
|
||||
|
||||
// LoadRepo load a repo into a test context.
|
||||
func LoadRepo(t *testing.T, ctx *context.Context, repoID int64) {
|
||||
ctx.Repo = &context.Repository{}
|
||||
ctx.Repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
func LoadRepo(t *testing.T, ctx gocontext.Context, repoID int64) {
|
||||
var doer *user_model.User
|
||||
repo := &context.Repository{}
|
||||
switch ctx := ctx.(type) {
|
||||
case *context.Context:
|
||||
ctx.Repo = repo
|
||||
doer = ctx.Doer
|
||||
case *context.APIContext:
|
||||
ctx.Repo = repo
|
||||
doer = ctx.Doer
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
}
|
||||
|
||||
repo.Repository = unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: repoID})
|
||||
var err error
|
||||
ctx.Repo.Owner, err = user_model.GetUserByID(ctx, ctx.Repo.Repository.OwnerID)
|
||||
repo.Owner, err = user_model.GetUserByID(ctx, repo.Repository.OwnerID)
|
||||
assert.NoError(t, err)
|
||||
ctx.Repo.RepoLink = ctx.Repo.Repository.Link()
|
||||
ctx.Repo.Permission, err = access_model.GetUserRepoPermission(ctx, ctx.Repo.Repository, ctx.Doer)
|
||||
repo.RepoLink = repo.Repository.Link()
|
||||
repo.Permission, err = access_model.GetUserRepoPermission(ctx, repo.Repository, doer)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// LoadRepoCommit loads a repo's commit into a test context.
|
||||
func LoadRepoCommit(t *testing.T, ctx *context.Context) {
|
||||
gitRepo, err := git.OpenRepository(ctx, ctx.Repo.Repository.RepoPath())
|
||||
func LoadRepoCommit(t *testing.T, ctx gocontext.Context) {
|
||||
var repo *context.Repository
|
||||
switch ctx := ctx.(type) {
|
||||
case *context.Context:
|
||||
repo = ctx.Repo
|
||||
case *context.APIContext:
|
||||
repo = ctx.Repo
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
}
|
||||
|
||||
gitRepo, err := git.OpenRepository(ctx, repo.Repository.RepoPath())
|
||||
assert.NoError(t, err)
|
||||
defer gitRepo.Close()
|
||||
branch, err := gitRepo.GetHEADBranch()
|
||||
assert.NoError(t, err)
|
||||
assert.NotNil(t, branch)
|
||||
if branch != nil {
|
||||
ctx.Repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
|
||||
repo.Commit, err = gitRepo.GetBranchCommit(branch.Name)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
}
|
||||
|
||||
// LoadUser load a user into a test context.
|
||||
func LoadUser(t *testing.T, ctx *context.Context, userID int64) {
|
||||
ctx.Doer = unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
|
||||
func LoadUser(t *testing.T, ctx gocontext.Context, userID int64) {
|
||||
doer := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: userID})
|
||||
switch ctx := ctx.(type) {
|
||||
case *context.Context:
|
||||
ctx.Doer = doer
|
||||
case *context.APIContext:
|
||||
ctx.Doer = doer
|
||||
default:
|
||||
assert.Fail(t, "context is not *context.Context or *context.APIContext")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// LoadGitRepo load a git repo into a test context. Requires that ctx.Repo has
|
||||
|
@ -93,32 +146,6 @@ func LoadGitRepo(t *testing.T, ctx *context.Context) {
|
|||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
type mockResponseWriter struct {
|
||||
httptest.ResponseRecorder
|
||||
size int
|
||||
}
|
||||
|
||||
func (rw *mockResponseWriter) Write(b []byte) (int, error) {
|
||||
rw.size += len(b)
|
||||
return rw.ResponseRecorder.Write(b)
|
||||
}
|
||||
|
||||
func (rw *mockResponseWriter) Status() int {
|
||||
return rw.ResponseRecorder.Code
|
||||
}
|
||||
|
||||
func (rw *mockResponseWriter) Written() bool {
|
||||
return rw.ResponseRecorder.Code > 0
|
||||
}
|
||||
|
||||
func (rw *mockResponseWriter) Size() int {
|
||||
return rw.size
|
||||
}
|
||||
|
||||
func (rw *mockResponseWriter) Push(target string, opts *http.PushOptions) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
type mockRender struct{}
|
||||
|
||||
func (tr *mockRender) TemplateLookup(tmpl string) (templates.TemplateExecutor, error) {
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue