f:has.owners supports assigned owners (#52219)

This pull request integrates assigned owners (and the `Bag` into search
filtering).

<img width="1125" alt="Screenshot 2023-05-19 at 9 47 20 PM"
src="https://github.com/sourcegraph/sourcegraph/assets/153410/5f6b1579-9d48-49fc-8629-cfe396554a14">

## Test plan

Extend filter_job_test.go.
This commit is contained in:
Cezary Bartoszuk 2023-05-23 07:39:29 +02:00 committed by GitHub
parent e349ccfd3b
commit 2bc23d7a71
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 627 additions and 50 deletions

View File

@ -92,7 +92,6 @@ type Bag interface {
func ByTextReference(ctx context.Context, db edb.EnterpriseDB, text ...string) (Bag, error) {
var multiBag bag
for _, t := range text {
t = strings.TrimPrefix(t, "@")
if _, err := mail.ParseAddress(t); err == nil {
b, err := ByEmailReference(ctx, db, t)
if err != nil {
@ -100,7 +99,7 @@ func ByTextReference(ctx context.Context, db edb.EnterpriseDB, text ...string) (
}
multiBag = append(multiBag, b...)
}
b, err := ByUserHandleReference(ctx, db, t)
b, err := ByUserHandleReference(ctx, db, strings.TrimPrefix(t, "@"))
if err != nil {
return nil, err
}

View File

@ -10,6 +10,7 @@ go_library(
importpath = "github.com/sourcegraph/sourcegraph/enterprise/internal/own/search",
visibility = ["//:__subpackages__"],
deps = [
"//enterprise/internal/database",
"//enterprise/internal/own",
"//enterprise/internal/own/codeowners",
"//enterprise/internal/own/codeowners/v1:codeowners",
@ -35,6 +36,7 @@ go_test(
embed = [":search"],
deps = [
"//enterprise/internal/database",
"//enterprise/internal/own",
"//internal/api",
"//internal/authz",
"//internal/database",

View File

@ -3,12 +3,12 @@ package search
import (
"context"
"fmt"
"strings"
"sync"
"go.opentelemetry.io/otel/attribute"
codeownerspb "github.com/sourcegraph/sourcegraph/enterprise/internal/own/codeowners/v1"
"github.com/sourcegraph/sourcegraph/enterprise/internal/database"
"github.com/sourcegraph/sourcegraph/enterprise/internal/own"
"github.com/sourcegraph/sourcegraph/internal/search"
"github.com/sourcegraph/sourcegraph/internal/search/job"
"github.com/sourcegraph/sourcegraph/internal/search/result"
@ -55,9 +55,19 @@ func (s *fileHasOwnersJob) Run(ctx context.Context, clients job.RuntimeClients,
rules := NewRulesCache(clients.Gitserver, clients.DB)
// Bag the search terms
includeBag, err := own.ByTextReference(ctx, database.NewEnterpriseDB(clients.DB), s.includeOwners...)
if err != nil {
return nil, errors.Wrap(err, "failure trying to resolve search term")
}
excludeBag, err := own.ByTextReference(ctx, database.NewEnterpriseDB(clients.DB), s.excludeOwners...)
if err != nil {
return nil, errors.Wrap(err, "failure trying to resolve search term")
}
filteredStream := streaming.StreamFunc(func(event streaming.SearchEvent) {
var err error
event.Results, err = applyCodeOwnershipFiltering(ctx, &rules, s.includeOwners, s.excludeOwners, event.Results)
event.Results, err = applyCodeOwnershipFiltering(ctx, &rules, includeBag, s.includeOwners, excludeBag, s.excludeOwners, event.Results)
if err != nil {
mu.Lock()
errs = errors.Append(errs, err)
@ -103,8 +113,10 @@ func (s *fileHasOwnersJob) MapChildren(fn job.MapFunc) job.Job {
func applyCodeOwnershipFiltering(
ctx context.Context,
rules *RulesCache,
includeOwners,
excludeOwners []string,
include own.Bag,
includeTerms []string,
exclude own.Bag,
excludeTerms []string,
matches []result.Match,
) ([]result.Match, error) {
var errs error
@ -124,21 +136,12 @@ matchesLoop:
errs = errors.Append(errs, err)
continue matchesLoop
}
rule := file.Match(mm.File.Path)
var owners []*codeownerspb.Owner
// If match.
if rule != nil {
owners = rule.GetOwner()
fileOwners := file.Match(mm.File.Path)
if len(includeTerms) > 0 && !containsOwner(fileOwners, includeTerms, include) {
continue matchesLoop
}
for _, owner := range includeOwners {
if !containsOwner(owners, owner) {
continue matchesLoop
}
}
for _, notOwner := range excludeOwners {
if containsOwner(owners, notOwner) {
continue matchesLoop
}
if len(excludeTerms) > 0 && containsOwner(fileOwners, excludeTerms, exclude) {
continue matchesLoop
}
filtered = append(filtered, m)
@ -150,21 +153,10 @@ matchesLoop:
// containsOwner searches within emails and handles in a case-insensitive
// manner. Empty string passed as search term means any, so the predicate
// returns true if there is at least one owner, and false otherwise.
func containsOwner(owners []*codeownerspb.Owner, owner string) bool {
if owner == "" {
return len(owners) > 0
func containsOwner(owners fileOwnershipData, searchTerms []string, bag own.Bag) bool {
// Empty search terms means any owner matches.
if len(searchTerms) == 1 && searchTerms[0] == "" {
return owners.NonEmpty()
}
isHandle := strings.HasPrefix(owner, "@")
owner = strings.ToLower(strings.TrimPrefix(owner, "@"))
for _, o := range owners {
if strings.ToLower(o.Handle) == owner {
return true
}
// Prefixing the search term with `@` indicates intent to match a handle,
// so we do not match email in that case.
if !isHandle && (strings.ToLower(o.Email) == owner) {
return true
}
}
return false
return owners.Contains(bag)
}

View File

@ -11,12 +11,15 @@ import (
"github.com/stretchr/testify/require"
edb "github.com/sourcegraph/sourcegraph/enterprise/internal/database"
"github.com/sourcegraph/sourcegraph/enterprise/internal/own"
"github.com/sourcegraph/sourcegraph/internal/api"
"github.com/sourcegraph/sourcegraph/internal/authz"
"github.com/sourcegraph/sourcegraph/internal/database"
"github.com/sourcegraph/sourcegraph/internal/gitserver"
"github.com/sourcegraph/sourcegraph/internal/search"
"github.com/sourcegraph/sourcegraph/internal/search/job"
"github.com/sourcegraph/sourcegraph/internal/search/result"
"github.com/sourcegraph/sourcegraph/internal/types"
)
func TestFeatureFlaggedFileHasOwnerJob(t *testing.T) {
@ -45,9 +48,10 @@ func TestApplyCodeOwnershipFiltering(t *testing.T) {
repoContent map[string]string
}
tests := []struct {
name string
args args
want autogold.Value
name string
args args
setup func(db *edb.MockEnterpriseDB)
want autogold.Value
}{
{
// TODO: We should display an error in search describing why the result is empty.
@ -285,6 +289,48 @@ func TestApplyCodeOwnershipFiltering(t *testing.T) {
},
}),
},
{
name: "selects result with assigned owner",
args: args{
includeOwners: []string{"test"},
excludeOwners: []string{},
matches: []result.Match{
&result.FileMatch{
File: result.File{
Path: "src/main/README.md",
},
},
},
// No CODEOWNERS
repoContent: map[string]string{},
},
setup: func(db *edb.MockEnterpriseDB) {
user := &types.User{
ID: 42,
Username: "test",
}
assignedOwners := []*database.AssignedOwnerSummary{
{
OwnerUserID: user.ID,
FilePath: "src/main",
},
}
usersStore := database.NewMockUserStore()
usersStore.GetByUsernameFunc.SetDefaultReturn(user, nil)
usersStore.GetByVerifiedEmailFunc.SetDefaultReturn(nil, nil)
db.UsersFunc.SetDefaultReturn(usersStore)
assignedOwnersStore := database.NewMockAssignedOwnersStore()
assignedOwnersStore.ListAssignedOwnersForRepoFunc.SetDefaultReturn(assignedOwners, nil)
db.AssignedOwnersFunc.SetDefaultReturn(assignedOwnersStore)
},
want: autogold.Expect([]result.Match{
&result.FileMatch{
File: result.File{
Path: "src/main/README.md",
},
},
}),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
@ -303,11 +349,38 @@ func TestApplyCodeOwnershipFiltering(t *testing.T) {
codeownersStore.GetCodeownersForRepoFunc.SetDefaultReturn(nil, nil)
db := edb.NewMockEnterpriseDB()
db.CodeownersFunc.SetDefaultReturn(codeownersStore)
usersStore := database.NewMockUserStore()
usersStore.GetByUsernameFunc.SetDefaultReturn(nil, nil)
usersStore.GetByVerifiedEmailFunc.SetDefaultReturn(nil, nil)
db.UsersFunc.SetDefaultReturn(usersStore)
usersEmailsStore := database.NewMockUserEmailsStore()
usersEmailsStore.GetVerifiedEmailsFunc.SetDefaultReturn(nil, nil)
db.UserEmailsFunc.SetDefaultReturn(usersEmailsStore)
assignedOwnersStore := database.NewMockAssignedOwnersStore()
assignedOwnersStore.ListAssignedOwnersForRepoFunc.SetDefaultReturn(nil, nil)
db.AssignedOwnersFunc.SetDefaultReturn(assignedOwnersStore)
userExternalAccountsStore := database.NewMockUserExternalAccountsStore()
userExternalAccountsStore.ListFunc.SetDefaultReturn(nil, nil)
db.UserExternalAccountsFunc.SetDefaultReturn(userExternalAccountsStore)
if tt.setup != nil {
tt.setup(db)
}
rules := NewRulesCache(gitserverClient, db)
matches, _ := applyCodeOwnershipFiltering(ctx, &rules, tt.args.includeOwners, tt.args.excludeOwners, tt.args.matches)
include, err := own.ByTextReference(ctx, db, tt.args.includeOwners...)
require.NoError(t, err)
exclude, err := own.ByTextReference(ctx, db, tt.args.excludeOwners...)
require.NoError(t, err)
matches, _ := applyCodeOwnershipFiltering(
ctx,
&rules,
include,
tt.args.includeOwners,
exclude,
tt.args.excludeOwners,
tt.args.matches)
//require.NoError(t, err)
tt.want.Equal(t, matches)
})
}

View File

@ -17,30 +17,73 @@ type RulesKey struct {
commitID api.CommitID
}
type AssignedKey struct {
repoID api.RepoID
}
type RulesCache struct {
rules map[RulesKey]*codeowners.Ruleset
assigned map[AssignedKey]own.AssignedOwners
ownService own.Service
mu sync.RWMutex
rulesMu sync.RWMutex
assignedMu sync.RWMutex
}
func NewRulesCache(gs gitserver.Client, db database.DB) RulesCache {
return RulesCache{
rules: make(map[RulesKey]*codeowners.Ruleset),
assigned: make(map[AssignedKey]own.AssignedOwners),
ownService: own.NewService(gs, db),
}
}
func (c *RulesCache) GetFromCacheOrFetch(ctx context.Context, repoName api.RepoName, repoID api.RepoID, commitID api.CommitID) (*codeowners.Ruleset, error) {
c.mu.RLock()
func (c *RulesCache) GetFromCacheOrFetch(ctx context.Context, repoName api.RepoName, repoID api.RepoID, commitID api.CommitID) (repoOwnershipData, error) {
assigned, err := c.AssignedOwners(ctx, repoID, commitID)
if err != nil {
return repoOwnershipData{}, err
}
codeowners, err := c.Codeowners(ctx, repoName, repoID, commitID)
if err != nil {
return repoOwnershipData{}, err
}
return repoOwnershipData{
assigned: assigned,
codeowners: codeowners,
}, nil
}
func (c *RulesCache) AssignedOwners(ctx context.Context, repoID api.RepoID, commitID api.CommitID) (own.AssignedOwners, error) {
c.assignedMu.RLock()
key := AssignedKey{repoID}
if v, ok := c.assigned[key]; ok {
defer c.assignedMu.RUnlock()
return v, nil
}
c.assignedMu.RUnlock()
c.assignedMu.Lock()
defer c.assignedMu.Unlock()
if _, ok := c.assigned[key]; !ok {
assigned, err := c.ownService.AssignedOwnership(ctx, repoID, commitID)
if err != nil {
// TODO: Consider error condition
return nil, err
}
c.assigned[key] = assigned
}
return c.assigned[key], nil
}
func (c *RulesCache) Codeowners(ctx context.Context, repoName api.RepoName, repoID api.RepoID, commitID api.CommitID) (*codeowners.Ruleset, error) {
c.rulesMu.RLock()
key := RulesKey{repoName, commitID}
if _, ok := c.rules[key]; ok {
defer c.mu.RUnlock()
defer c.rulesMu.RUnlock()
return c.rules[key], nil
}
c.mu.RUnlock()
c.mu.Lock()
defer c.mu.Unlock()
c.rulesMu.RUnlock()
c.rulesMu.Lock()
defer c.rulesMu.Unlock()
// Recheck condition.
if _, ok := c.rules[key]; !ok {
file, err := c.ownService.RulesetForRepo(ctx, repoName, repoID, commitID)
@ -55,3 +98,51 @@ func (c *RulesCache) GetFromCacheOrFetch(ctx context.Context, repoName api.RepoN
}
return c.rules[key], nil
}
type repoOwnershipData struct {
codeowners *codeowners.Ruleset
assigned own.AssignedOwners
}
func (o repoOwnershipData) Match(path string) fileOwnershipData {
var rule *codeownerspb.Rule
if o.codeowners != nil {
rule = o.codeowners.Match(path)
}
return fileOwnershipData{
rule: rule,
assignedOwners: o.assigned.Match(path),
}
}
type fileOwnershipData struct {
rule *codeownerspb.Rule
assignedOwners []database.AssignedOwnerSummary
}
func (d fileOwnershipData) NonEmpty() bool {
if d.rule != nil && len(d.rule.Owner) > 0 {
return true
}
if len(d.assignedOwners) > 0 {
return true
}
return false
}
func (d fileOwnershipData) Contains(bag own.Bag) bool {
for _, o := range d.rule.GetOwner() {
if bag.Contains(own.Reference{
Handle: o.Handle,
Email: o.Email,
}) {
return true
}
}
for _, o := range d.assignedOwners {
if bag.Contains(own.Reference{UserID: o.OwnerUserID}) {
return true
}
}
return false
}

View File

@ -115,7 +115,7 @@ func getCodeOwnersFromMatches(
if !ok {
continue
}
rs, err := rules.GetFromCacheOrFetch(ctx, mm.Repo.Name, mm.Repo.ID, mm.CommitID)
rs, err := rules.Codeowners(ctx, mm.Repo.Name, mm.Repo.ID, mm.CommitID)
if err != nil {
errs = errors.Append(errs, err)
continue

View File

@ -2811,6 +2811,425 @@ func (c AccessTokenStoreWithTransactFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// MockAssignedOwnersStore is a mock implementation of the
// AssignedOwnersStore interface (from the package
// github.com/sourcegraph/sourcegraph/internal/database) used for unit
// testing.
type MockAssignedOwnersStore struct {
// DeleteOwnerFunc is an instance of a mock function object controlling
// the behavior of the method DeleteOwner.
DeleteOwnerFunc *AssignedOwnersStoreDeleteOwnerFunc
// InsertFunc is an instance of a mock function object controlling the
// behavior of the method Insert.
InsertFunc *AssignedOwnersStoreInsertFunc
// ListAssignedOwnersForRepoFunc is an instance of a mock function
// object controlling the behavior of the method
// ListAssignedOwnersForRepo.
ListAssignedOwnersForRepoFunc *AssignedOwnersStoreListAssignedOwnersForRepoFunc
}
// NewMockAssignedOwnersStore creates a new mock of the AssignedOwnersStore
// interface. All methods return zero values for all results, unless
// overwritten.
func NewMockAssignedOwnersStore() *MockAssignedOwnersStore {
return &MockAssignedOwnersStore{
DeleteOwnerFunc: &AssignedOwnersStoreDeleteOwnerFunc{
defaultHook: func(context.Context, int32, api.RepoID, string) (r0 error) {
return
},
},
InsertFunc: &AssignedOwnersStoreInsertFunc{
defaultHook: func(context.Context, int32, api.RepoID, string, int32) (r0 error) {
return
},
},
ListAssignedOwnersForRepoFunc: &AssignedOwnersStoreListAssignedOwnersForRepoFunc{
defaultHook: func(context.Context, api.RepoID) (r0 []*AssignedOwnerSummary, r1 error) {
return
},
},
}
}
// NewStrictMockAssignedOwnersStore creates a new mock of the
// AssignedOwnersStore interface. All methods panic on invocation, unless
// overwritten.
func NewStrictMockAssignedOwnersStore() *MockAssignedOwnersStore {
return &MockAssignedOwnersStore{
DeleteOwnerFunc: &AssignedOwnersStoreDeleteOwnerFunc{
defaultHook: func(context.Context, int32, api.RepoID, string) error {
panic("unexpected invocation of MockAssignedOwnersStore.DeleteOwner")
},
},
InsertFunc: &AssignedOwnersStoreInsertFunc{
defaultHook: func(context.Context, int32, api.RepoID, string, int32) error {
panic("unexpected invocation of MockAssignedOwnersStore.Insert")
},
},
ListAssignedOwnersForRepoFunc: &AssignedOwnersStoreListAssignedOwnersForRepoFunc{
defaultHook: func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error) {
panic("unexpected invocation of MockAssignedOwnersStore.ListAssignedOwnersForRepo")
},
},
}
}
// NewMockAssignedOwnersStoreFrom creates a new mock of the
// MockAssignedOwnersStore interface. All methods delegate to the given
// implementation, unless overwritten.
func NewMockAssignedOwnersStoreFrom(i AssignedOwnersStore) *MockAssignedOwnersStore {
return &MockAssignedOwnersStore{
DeleteOwnerFunc: &AssignedOwnersStoreDeleteOwnerFunc{
defaultHook: i.DeleteOwner,
},
InsertFunc: &AssignedOwnersStoreInsertFunc{
defaultHook: i.Insert,
},
ListAssignedOwnersForRepoFunc: &AssignedOwnersStoreListAssignedOwnersForRepoFunc{
defaultHook: i.ListAssignedOwnersForRepo,
},
}
}
// AssignedOwnersStoreDeleteOwnerFunc describes the behavior when the
// DeleteOwner method of the parent MockAssignedOwnersStore instance is
// invoked.
type AssignedOwnersStoreDeleteOwnerFunc struct {
defaultHook func(context.Context, int32, api.RepoID, string) error
hooks []func(context.Context, int32, api.RepoID, string) error
history []AssignedOwnersStoreDeleteOwnerFuncCall
mutex sync.Mutex
}
// DeleteOwner delegates to the next hook function in the queue and stores
// the parameter and result values of this invocation.
func (m *MockAssignedOwnersStore) DeleteOwner(v0 context.Context, v1 int32, v2 api.RepoID, v3 string) error {
r0 := m.DeleteOwnerFunc.nextHook()(v0, v1, v2, v3)
m.DeleteOwnerFunc.appendCall(AssignedOwnersStoreDeleteOwnerFuncCall{v0, v1, v2, v3, r0})
return r0
}
// SetDefaultHook sets function that is called when the DeleteOwner method
// of the parent MockAssignedOwnersStore instance is invoked and the hook
// queue is empty.
func (f *AssignedOwnersStoreDeleteOwnerFunc) SetDefaultHook(hook func(context.Context, int32, api.RepoID, string) error) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// DeleteOwner method of the parent MockAssignedOwnersStore instance invokes
// the hook at the front of the queue and discards it. After the queue is
// empty, the default hook function is invoked for any future action.
func (f *AssignedOwnersStoreDeleteOwnerFunc) PushHook(hook func(context.Context, int32, api.RepoID, string) error) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *AssignedOwnersStoreDeleteOwnerFunc) SetDefaultReturn(r0 error) {
f.SetDefaultHook(func(context.Context, int32, api.RepoID, string) error {
return r0
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *AssignedOwnersStoreDeleteOwnerFunc) PushReturn(r0 error) {
f.PushHook(func(context.Context, int32, api.RepoID, string) error {
return r0
})
}
func (f *AssignedOwnersStoreDeleteOwnerFunc) nextHook() func(context.Context, int32, api.RepoID, string) error {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *AssignedOwnersStoreDeleteOwnerFunc) appendCall(r0 AssignedOwnersStoreDeleteOwnerFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of AssignedOwnersStoreDeleteOwnerFuncCall
// objects describing the invocations of this function.
func (f *AssignedOwnersStoreDeleteOwnerFunc) History() []AssignedOwnersStoreDeleteOwnerFuncCall {
f.mutex.Lock()
history := make([]AssignedOwnersStoreDeleteOwnerFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// AssignedOwnersStoreDeleteOwnerFuncCall is an object that describes an
// invocation of method DeleteOwner on an instance of
// MockAssignedOwnersStore.
type AssignedOwnersStoreDeleteOwnerFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 int32
// Arg2 is the value of the 3rd argument passed to this method
// invocation.
Arg2 api.RepoID
// Arg3 is the value of the 4th argument passed to this method
// invocation.
Arg3 string
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c AssignedOwnersStoreDeleteOwnerFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c AssignedOwnersStoreDeleteOwnerFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// AssignedOwnersStoreInsertFunc describes the behavior when the Insert
// method of the parent MockAssignedOwnersStore instance is invoked.
type AssignedOwnersStoreInsertFunc struct {
defaultHook func(context.Context, int32, api.RepoID, string, int32) error
hooks []func(context.Context, int32, api.RepoID, string, int32) error
history []AssignedOwnersStoreInsertFuncCall
mutex sync.Mutex
}
// Insert delegates to the next hook function in the queue and stores the
// parameter and result values of this invocation.
func (m *MockAssignedOwnersStore) Insert(v0 context.Context, v1 int32, v2 api.RepoID, v3 string, v4 int32) error {
r0 := m.InsertFunc.nextHook()(v0, v1, v2, v3, v4)
m.InsertFunc.appendCall(AssignedOwnersStoreInsertFuncCall{v0, v1, v2, v3, v4, r0})
return r0
}
// SetDefaultHook sets function that is called when the Insert method of the
// parent MockAssignedOwnersStore instance is invoked and the hook queue is
// empty.
func (f *AssignedOwnersStoreInsertFunc) SetDefaultHook(hook func(context.Context, int32, api.RepoID, string, int32) error) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// Insert method of the parent MockAssignedOwnersStore instance invokes the
// hook at the front of the queue and discards it. After the queue is empty,
// the default hook function is invoked for any future action.
func (f *AssignedOwnersStoreInsertFunc) PushHook(hook func(context.Context, int32, api.RepoID, string, int32) error) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *AssignedOwnersStoreInsertFunc) SetDefaultReturn(r0 error) {
f.SetDefaultHook(func(context.Context, int32, api.RepoID, string, int32) error {
return r0
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *AssignedOwnersStoreInsertFunc) PushReturn(r0 error) {
f.PushHook(func(context.Context, int32, api.RepoID, string, int32) error {
return r0
})
}
func (f *AssignedOwnersStoreInsertFunc) nextHook() func(context.Context, int32, api.RepoID, string, int32) error {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *AssignedOwnersStoreInsertFunc) appendCall(r0 AssignedOwnersStoreInsertFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of AssignedOwnersStoreInsertFuncCall objects
// describing the invocations of this function.
func (f *AssignedOwnersStoreInsertFunc) History() []AssignedOwnersStoreInsertFuncCall {
f.mutex.Lock()
history := make([]AssignedOwnersStoreInsertFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// AssignedOwnersStoreInsertFuncCall is an object that describes an
// invocation of method Insert on an instance of MockAssignedOwnersStore.
type AssignedOwnersStoreInsertFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 int32
// Arg2 is the value of the 3rd argument passed to this method
// invocation.
Arg2 api.RepoID
// Arg3 is the value of the 4th argument passed to this method
// invocation.
Arg3 string
// Arg4 is the value of the 5th argument passed to this method
// invocation.
Arg4 int32
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c AssignedOwnersStoreInsertFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1, c.Arg2, c.Arg3, c.Arg4}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c AssignedOwnersStoreInsertFuncCall) Results() []interface{} {
return []interface{}{c.Result0}
}
// AssignedOwnersStoreListAssignedOwnersForRepoFunc describes the behavior
// when the ListAssignedOwnersForRepo method of the parent
// MockAssignedOwnersStore instance is invoked.
type AssignedOwnersStoreListAssignedOwnersForRepoFunc struct {
defaultHook func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error)
hooks []func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error)
history []AssignedOwnersStoreListAssignedOwnersForRepoFuncCall
mutex sync.Mutex
}
// ListAssignedOwnersForRepo delegates to the next hook function in the
// queue and stores the parameter and result values of this invocation.
func (m *MockAssignedOwnersStore) ListAssignedOwnersForRepo(v0 context.Context, v1 api.RepoID) ([]*AssignedOwnerSummary, error) {
r0, r1 := m.ListAssignedOwnersForRepoFunc.nextHook()(v0, v1)
m.ListAssignedOwnersForRepoFunc.appendCall(AssignedOwnersStoreListAssignedOwnersForRepoFuncCall{v0, v1, r0, r1})
return r0, r1
}
// SetDefaultHook sets function that is called when the
// ListAssignedOwnersForRepo method of the parent MockAssignedOwnersStore
// instance is invoked and the hook queue is empty.
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) SetDefaultHook(hook func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error)) {
f.defaultHook = hook
}
// PushHook adds a function to the end of hook queue. Each invocation of the
// ListAssignedOwnersForRepo method of the parent MockAssignedOwnersStore
// instance invokes the hook at the front of the queue and discards it.
// After the queue is empty, the default hook function is invoked for any
// future action.
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) PushHook(hook func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error)) {
f.mutex.Lock()
f.hooks = append(f.hooks, hook)
f.mutex.Unlock()
}
// SetDefaultReturn calls SetDefaultHook with a function that returns the
// given values.
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) SetDefaultReturn(r0 []*AssignedOwnerSummary, r1 error) {
f.SetDefaultHook(func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error) {
return r0, r1
})
}
// PushReturn calls PushHook with a function that returns the given values.
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) PushReturn(r0 []*AssignedOwnerSummary, r1 error) {
f.PushHook(func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error) {
return r0, r1
})
}
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) nextHook() func(context.Context, api.RepoID) ([]*AssignedOwnerSummary, error) {
f.mutex.Lock()
defer f.mutex.Unlock()
if len(f.hooks) == 0 {
return f.defaultHook
}
hook := f.hooks[0]
f.hooks = f.hooks[1:]
return hook
}
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) appendCall(r0 AssignedOwnersStoreListAssignedOwnersForRepoFuncCall) {
f.mutex.Lock()
f.history = append(f.history, r0)
f.mutex.Unlock()
}
// History returns a sequence of
// AssignedOwnersStoreListAssignedOwnersForRepoFuncCall objects describing
// the invocations of this function.
func (f *AssignedOwnersStoreListAssignedOwnersForRepoFunc) History() []AssignedOwnersStoreListAssignedOwnersForRepoFuncCall {
f.mutex.Lock()
history := make([]AssignedOwnersStoreListAssignedOwnersForRepoFuncCall, len(f.history))
copy(history, f.history)
f.mutex.Unlock()
return history
}
// AssignedOwnersStoreListAssignedOwnersForRepoFuncCall is an object that
// describes an invocation of method ListAssignedOwnersForRepo on an
// instance of MockAssignedOwnersStore.
type AssignedOwnersStoreListAssignedOwnersForRepoFuncCall struct {
// Arg0 is the value of the 1st argument passed to this method
// invocation.
Arg0 context.Context
// Arg1 is the value of the 2nd argument passed to this method
// invocation.
Arg1 api.RepoID
// Result0 is the value of the 1st result returned from this method
// invocation.
Result0 []*AssignedOwnerSummary
// Result1 is the value of the 2nd result returned from this method
// invocation.
Result1 error
}
// Args returns an interface slice containing the arguments of this
// invocation.
func (c AssignedOwnersStoreListAssignedOwnersForRepoFuncCall) Args() []interface{} {
return []interface{}{c.Arg0, c.Arg1}
}
// Results returns an interface slice containing the results of this
// invocation.
func (c AssignedOwnersStoreListAssignedOwnersForRepoFuncCall) Results() []interface{} {
return []interface{}{c.Result0, c.Result1}
}
// MockAuthzStore is a mock implementation of the AuthzStore interface (from
// the package github.com/sourcegraph/sourcegraph/internal/database) used
// for unit testing.

View File

@ -38,6 +38,7 @@
interfaces:
- AccessRequestStore
- AccessTokenStore
- AssignedOwnersStore
- AuthzStore
- BitbucketProjectPermissionsStore
- ConfStore