prevent cross-contamination on filter reduce (#898)

## Description

Over-restrictive scope correlation caused the
reduce processor to only include filters which
matched the data type of the scope.  Ironically,
this allowed a superset of information to match,
by evading the _all-match_ expectations of
filter scopes.

Also replaces the data category consts inside /details
with the path category types, since those are acting as
better canonical owners of data type identification
throughout the app.

## Type of change

- [x] 🐛 Bugfix

## Issue(s)

* #890

## Test Plan

- [x] 💪 Manual
- [x]  Unit test
This commit is contained in:
Keepers 2022-09-19 18:34:51 -06:00 committed by GitHub
parent b2d3330db7
commit 41bb3ee6f9
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 297 additions and 131 deletions

View File

@ -39,7 +39,7 @@ func (dm DetailsModel) PrintEntries(ctx context.Context) {
}
func printTable(ctx context.Context, dm DetailsModel) {
perType := map[itemType][]print.Printable{}
perType := map[ItemType][]print.Printable{}
for _, de := range dm.Entries {
it := de.infoType()
@ -226,21 +226,21 @@ func (de DetailsEntry) Values() []string {
return vs
}
type itemType int
type ItemType int
const (
UnknownType itemType = iota
UnknownType ItemType = iota
// separate each service by a factor of 100 for padding
ExchangeContact
ExchangeEvent
ExchangeMail
SharepointItem itemType = iota + 100
SharepointItem ItemType = iota + 100
OneDriveItem itemType = iota + 200
OneDriveItem ItemType = iota + 200
FolderItem itemType = iota + 300
FolderItem ItemType = iota + 300
)
// ItemInfo is a oneOf that contains service specific
@ -258,7 +258,7 @@ type ItemInfo struct {
// infoType provides internal categorization for collecting like-typed ItemInfos.
// It should return the most granular value type (ex: "event" for an exchange
// calendar event).
func (i ItemInfo) infoType() itemType {
func (i ItemInfo) infoType() ItemType {
switch {
case i.Folder != nil:
return i.Folder.ItemType
@ -277,7 +277,7 @@ func (i ItemInfo) infoType() itemType {
}
type FolderInfo struct {
ItemType itemType
ItemType ItemType `json:"itemType,omitempty"`
DisplayName string `json:"displayName"`
}
@ -291,7 +291,7 @@ func (i FolderInfo) Values() []string {
// ExchangeInfo describes an exchange item
type ExchangeInfo struct {
ItemType itemType
ItemType ItemType `json:"itemType,omitempty"`
Sender string `json:"sender,omitempty"`
Subject string `json:"subject,omitempty"`
Received time.Time `json:"received,omitempty"`
@ -344,7 +344,7 @@ func (i ExchangeInfo) Values() []string {
// TODO: Implement this. This is currently here
// just to illustrate usage
type SharepointInfo struct {
ItemType itemType
ItemType ItemType `json:"itemType,omitempty"`
}
// Headers returns the human-readable names of properties in a SharepointInfo
@ -361,7 +361,7 @@ func (i SharepointInfo) Values() []string {
// OneDriveInfo describes a oneDrive item
type OneDriveInfo struct {
ItemType itemType
ItemType ItemType `json:"itemType,omitempty"`
ParentPath string `json:"parentPath"`
ItemName string `json:"itemName"`
}

View File

@ -333,8 +333,8 @@ func (sr *ExchangeRestore) EventRecurs(recurs string) []ExchangeScope {
func (sr *ExchangeRestore) EventStartsAfter(timeStrings string) []ExchangeScope {
return []ExchangeScope{
makeFilterScope[ExchangeScope](
ExchangeMail,
ExchangeFilterMailReceivedAfter,
ExchangeEvent,
ExchangeFilterEventStartsAfter,
[]string{timeStrings},
wrapFilter(filters.Less)),
}
@ -347,8 +347,8 @@ func (sr *ExchangeRestore) EventStartsAfter(timeStrings string) []ExchangeScope
func (sr *ExchangeRestore) EventStartsBefore(timeStrings string) []ExchangeScope {
return []ExchangeScope{
makeFilterScope[ExchangeScope](
ExchangeMail,
ExchangeFilterMailReceivedBefore,
ExchangeEvent,
ExchangeFilterEventStartsBefore,
[]string{timeStrings},
wrapFilter(filters.Greater)),
}
@ -690,25 +690,21 @@ func (s exchange) Reduce(ctx context.Context, deets *details.Details) *details.D
)
}
// matchesEntry returns true if either the path or the info in the exchangeEntry matches the scope details.
func (s ExchangeScope) matchesEntry(
cat categorizer,
pathValues map[categorizer]string,
entry details.DetailsEntry,
) bool {
// matchesPathValues can be handled generically, thanks to SCIENCE.
return matchesPathValues(s, cat.(exchangeCategory), pathValues, entry.ShortRef) || s.matchesInfo(entry.Exchange)
}
// matchesInfo handles the standard behavior when comparing a scope and an ExchangeFilter
// returns true if the scope and info match for the provided category.
func (s ExchangeScope) matchesInfo(info *details.ExchangeInfo) bool {
// we need values to match against
func (s ExchangeScope) matchesInfo(dii details.ItemInfo) bool {
info := dii.Exchange
if info == nil {
return false
}
filterCat := s.FilterCategory()
cfpc := categoryFromItemType(info.ItemType)
if !typeAndCategoryMatches(filterCat, cfpc) {
return false
}
i := ""
switch filterCat {
@ -732,3 +728,19 @@ func (s ExchangeScope) matchesInfo(info *details.ExchangeInfo) bool {
return s.Matches(filterCat, i)
}
// categoryFromItemType interprets the category represented by the ExchangeInfo
// struct. Since every ExchangeInfo can hold all exchange data info, the exact
// type that the struct represents must be compared using its ItemType prop.
func categoryFromItemType(pct details.ItemType) exchangeCategory {
switch pct {
case details.ExchangeContact:
return ExchangeContact
case details.ExchangeMail:
return ExchangeMail
case details.ExchangeEvent:
return ExchangeEvent
}
return ExchangeCategoryUnknown
}

View File

@ -710,7 +710,12 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesInfo() {
epoch = time.Time{}
now = time.Now()
future = now.Add(1 * time.Minute)
info = &details.ExchangeInfo{
)
infoWith := func(itype details.ItemType) details.ItemInfo {
return details.ItemInfo{
Exchange: &details.ExchangeInfo{
ItemType: itype,
ContactName: name,
EventRecurs: true,
EventStart: now,
@ -718,54 +723,86 @@ func (suite *ExchangeSelectorSuite) TestExchangeScope_MatchesInfo() {
Sender: sender,
Subject: subject,
Received: now,
},
}
}
)
table := []struct {
name string
itype details.ItemType
scope []ExchangeScope
expect assert.BoolAssertionFunc
}{
{"any mail with a sender", es.MailSender(AnyTgt), assert.True},
{"no mail, regardless of sender", es.MailSender(NoneTgt), assert.False},
{"mail from a different sender", es.MailSender("magoo@ma.goo"), assert.False},
{"mail from the matching sender", es.MailSender(sender), assert.True},
{"mail with any subject", es.MailSubject(AnyTgt), assert.True},
{"mail with none subject", es.MailSubject(NoneTgt), assert.False},
{"mail with a different subject", es.MailSubject("fancy"), assert.False},
{"mail with the matching subject", es.MailSubject(subject), assert.True},
{"mail with a substring subject match", es.MailSubject(subject[5:9]), assert.True},
{"mail received after the epoch", es.MailReceivedAfter(common.FormatTime(epoch)), assert.True},
{"mail received after now", es.MailReceivedAfter(common.FormatTime(now)), assert.False},
{"mail received after sometime later", es.MailReceivedAfter(common.FormatTime(future)), assert.False},
{"mail received before the epoch", es.MailReceivedBefore(common.FormatTime(epoch)), assert.False},
{"mail received before now", es.MailReceivedBefore(common.FormatTime(now)), assert.False},
{"mail received before sometime later", es.MailReceivedBefore(common.FormatTime(future)), assert.True},
{"event with any organizer", es.EventOrganizer(AnyTgt), assert.True},
{"event with none organizer", es.EventOrganizer(NoneTgt), assert.False},
{"event with a different organizer", es.EventOrganizer("fancy"), assert.False},
{"event with the matching organizer", es.EventOrganizer(organizer), assert.True},
{"event that recurs", es.EventRecurs("true"), assert.True},
{"event that does not recur", es.EventRecurs("false"), assert.False},
{"event starting after the epoch", es.EventStartsAfter(common.FormatTime(epoch)), assert.True},
{"event starting after now", es.EventStartsAfter(common.FormatTime(now)), assert.False},
{"event starting after sometime later", es.EventStartsAfter(common.FormatTime(future)), assert.False},
{"event starting before the epoch", es.EventStartsBefore(common.FormatTime(epoch)), assert.False},
{"event starting before now", es.EventStartsBefore(common.FormatTime(now)), assert.False},
{"event starting before sometime later", es.EventStartsBefore(common.FormatTime(future)), assert.True},
{"event with any subject", es.EventSubject(AnyTgt), assert.True},
{"event with none subject", es.EventSubject(NoneTgt), assert.False},
{"event with a different subject", es.EventSubject("fancy"), assert.False},
{"event with the matching subject", es.EventSubject(subject), assert.True},
{"contact with a different name", es.ContactName("blarps"), assert.False},
{"contact with the same name", es.ContactName(name), assert.True},
{"contact with a subname search", es.ContactName(name[2:5]), assert.True},
{"any mail with a sender", details.ExchangeMail, es.MailSender(AnyTgt), assert.True},
{"no mail, regardless of sender", details.ExchangeMail, es.MailSender(NoneTgt), assert.False},
{"mail from a different sender", details.ExchangeMail, es.MailSender("magoo@ma.goo"), assert.False},
{"mail from the matching sender", details.ExchangeMail, es.MailSender(sender), assert.True},
{"mail with any subject", details.ExchangeMail, es.MailSubject(AnyTgt), assert.True},
{"mail with none subject", details.ExchangeMail, es.MailSubject(NoneTgt), assert.False},
{"mail with a different subject", details.ExchangeMail, es.MailSubject("fancy"), assert.False},
{"mail with the matching subject", details.ExchangeMail, es.MailSubject(subject), assert.True},
{"mail with a substring subject match", details.ExchangeMail, es.MailSubject(subject[5:9]), assert.True},
{"mail received after the epoch", details.ExchangeMail, es.MailReceivedAfter(common.FormatTime(epoch)), assert.True},
{"mail received after now", details.ExchangeMail, es.MailReceivedAfter(common.FormatTime(now)), assert.False},
{
"mail received after sometime later",
details.ExchangeMail,
es.MailReceivedAfter(common.FormatTime(future)),
assert.False,
},
{
"mail received before the epoch",
details.ExchangeMail,
es.MailReceivedBefore(common.FormatTime(epoch)),
assert.False,
},
{"mail received before now", details.ExchangeMail, es.MailReceivedBefore(common.FormatTime(now)), assert.False},
{
"mail received before sometime later",
details.ExchangeMail,
es.MailReceivedBefore(common.FormatTime(future)),
assert.True,
},
{"event with any organizer", details.ExchangeEvent, es.EventOrganizer(AnyTgt), assert.True},
{"event with none organizer", details.ExchangeEvent, es.EventOrganizer(NoneTgt), assert.False},
{"event with a different organizer", details.ExchangeEvent, es.EventOrganizer("fancy"), assert.False},
{"event with the matching organizer", details.ExchangeEvent, es.EventOrganizer(organizer), assert.True},
{"event that recurs", details.ExchangeEvent, es.EventRecurs("true"), assert.True},
{"event that does not recur", details.ExchangeEvent, es.EventRecurs("false"), assert.False},
{"event starting after the epoch", details.ExchangeEvent, es.EventStartsAfter(common.FormatTime(epoch)), assert.True},
{"event starting after now", details.ExchangeEvent, es.EventStartsAfter(common.FormatTime(now)), assert.False},
{
"event starting after sometime later",
details.ExchangeEvent,
es.EventStartsAfter(common.FormatTime(future)),
assert.False,
},
{
"event starting before the epoch",
details.ExchangeEvent,
es.EventStartsBefore(common.FormatTime(epoch)),
assert.False,
},
{"event starting before now", details.ExchangeEvent, es.EventStartsBefore(common.FormatTime(now)), assert.False},
{
"event starting before sometime later",
details.ExchangeEvent,
es.EventStartsBefore(common.FormatTime(future)),
assert.True,
},
{"event with any subject", details.ExchangeEvent, es.EventSubject(AnyTgt), assert.True},
{"event with none subject", details.ExchangeEvent, es.EventSubject(NoneTgt), assert.False},
{"event with a different subject", details.ExchangeEvent, es.EventSubject("fancy"), assert.False},
{"event with the matching subject", details.ExchangeEvent, es.EventSubject(subject), assert.True},
{"contact with a different name", details.ExchangeContact, es.ContactName("blarps"), assert.False},
{"contact with the same name", details.ExchangeContact, es.ContactName(name), assert.True},
{"contact with a subname search", details.ExchangeContact, es.ContactName(name[2:5]), assert.True},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
scopes := setScopesToDefault(test.scope)
for _, scope := range scopes {
test.expect(t, scope.matchesInfo(info))
test.expect(t, scope.matchesInfo(infoWith(test.itype)))
}
})
}
@ -864,6 +901,12 @@ func (suite *ExchangeSelectorSuite) TestIdPath() {
}
func (suite *ExchangeSelectorSuite) TestExchangeRestore_Reduce() {
var (
contact = stubRepoRef(path.ExchangeService, path.ContactsCategory, "uid", "cfld", "cid")
event = stubRepoRef(path.ExchangeService, path.EventsCategory, "uid", "ecld", "eid")
mail = stubRepoRef(path.ExchangeService, path.EmailCategory, "uid", "mfld", "mid")
)
makeDeets := func(refs ...string) *details.Details {
deets := &details.Details{
DetailsModel: details.DetailsModel{
@ -872,20 +915,30 @@ func (suite *ExchangeSelectorSuite) TestExchangeRestore_Reduce() {
}
for _, r := range refs {
itype := details.UnknownType
switch r {
case contact:
itype = details.ExchangeContact
case event:
itype = details.ExchangeEvent
case mail:
itype = details.ExchangeMail
}
deets.Entries = append(deets.Entries, details.DetailsEntry{
RepoRef: r,
ItemInfo: details.ItemInfo{
Exchange: &details.ExchangeInfo{
ItemType: itype,
},
},
})
}
return deets
}
var (
contact = stubRepoRef(path.ExchangeService, path.ContactsCategory, "uid", "cfld", "cid")
event = stubRepoRef(path.ExchangeService, path.EventsCategory, "uid", "ecld", "eid")
mail = stubRepoRef(path.ExchangeService, path.EmailCategory, "uid", "mfld", "mid")
)
arr := func(s ...string) []string {
return s
}
@ -1009,6 +1062,44 @@ func (suite *ExchangeSelectorSuite) TestExchangeRestore_Reduce() {
},
arr(contact, event),
},
{
"filter on mail subject",
func() *details.Details {
ds := makeDeets(mail)
for i := range ds.Entries {
ds.Entries[i].Exchange.Subject = "has a subject"
}
return ds
}(),
func() *ExchangeRestore {
er := NewExchangeRestore()
er.Include(er.Users(Any()))
er.Filter(er.MailSubject("subj"))
return er
},
arr(mail),
},
{
"filter on mail subject multiple input categories",
func() *details.Details {
mds := makeDeets(mail)
for i := range mds.Entries {
mds.Entries[i].Exchange.Subject = "has a subject"
}
ds := makeDeets(contact, event)
ds.Entries = append(ds.Entries, mds.Entries...)
return ds
}(),
func() *ExchangeRestore {
er := NewExchangeRestore()
er.Include(er.Users(Any()))
er.Filter(er.MailSubject("subj"))
return er
},
arr(mail),
},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
@ -1067,7 +1158,7 @@ func (suite *ExchangeSelectorSuite) TestScopesByCategory() {
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
result := scopesByCategory[ExchangeScope](test.scopes, cats)
result := scopesByCategory[ExchangeScope](test.scopes, cats, false)
assert.Len(t, result[ExchangeContact], test.expect.contact)
assert.Len(t, result[ExchangeEvent], test.expect.event)
assert.Len(t, result[ExchangeMail], test.expect.mail)
@ -1288,3 +1379,38 @@ func (suite *ExchangeSelectorSuite) TestExchangeCategory_PathKeys() {
})
}
}
func (suite *ExchangeSelectorSuite) TestCategoryFromItemType() {
table := []struct {
name string
input details.ItemType
expect exchangeCategory
}{
{
name: "contact",
input: details.ExchangeContact,
expect: ExchangeContact,
},
{
name: "event",
input: details.ExchangeEvent,
expect: ExchangeEvent,
},
{
name: "mail",
input: details.ExchangeMail,
expect: ExchangeMail,
},
{
name: "unknown",
input: details.UnknownType,
expect: ExchangeCategoryUnknown,
},
}
for _, test := range table {
suite.T().Run(test.name, func(t *testing.T) {
result := categoryFromItemType(test.input)
assert.Equal(t, test.expect, result)
})
}
}

View File

@ -52,7 +52,10 @@ func (mc mockCategorizer) isLeaf() bool {
}
func (mc mockCategorizer) pathValues(pth path.Path) map[categorizer]string {
return map[categorizer]string{rootCatStub: "stub"}
return map[categorizer]string{
rootCatStub: "root",
leafCatStub: "leaf",
}
}
func (mc mockCategorizer) pathKeys() []categorizer {
@ -86,11 +89,7 @@ func (ms mockScope) categorizer() categorizer {
return unknownCatStub
}
func (ms mockScope) matchesEntry(
cat categorizer,
pathValues map[categorizer]string,
entry details.DetailsEntry,
) bool {
func (ms mockScope) matchesInfo(dii details.ItemInfo) bool {
return ms[shouldMatch].Target == "true"
}
@ -107,14 +106,27 @@ func stubScope(match string) mockScope {
sm = match
}
filt := passAny
if match == "none" {
filt = failAny
}
return mockScope{
rootCatStub.String(): passAny,
rootCatStub.String(): filt,
leafCatStub.String(): filt,
scopeKeyCategory: filters.Identity(rootCatStub.String()),
scopeKeyDataType: filters.Identity(rootCatStub.String()),
shouldMatch: filters.Identity(sm),
}
}
func stubInfoScope(match string) mockScope {
sc := stubScope(match)
sc[scopeKeyInfoFilter] = filters.Identity("true")
return sc
}
// ---------------------------------------------------------------------------
// selectors
// ---------------------------------------------------------------------------

View File

@ -299,23 +299,14 @@ func (s OneDriveScope) setDefaults() {
// no-op while no child scope types below user are identified
}
// matchesEntry returns true if either the path or the info in the oneDriveEntry matches the scope details.
func (s OneDriveScope) matchesEntry(
cat categorizer,
pathValues map[categorizer]string,
entry details.DetailsEntry,
) bool {
// matchesPathValues can be handled generically, thanks to SCIENCE.
return matchesPathValues(s, cat.(oneDriveCategory), pathValues, entry.ShortRef) || s.matchesInfo(entry.OneDrive)
}
// matchesInfo handles the standard behavior when comparing a scope and an oneDriveInfo
// returns true if the scope and info match for the provided category.
func (s OneDriveScope) matchesInfo(info *details.OneDriveInfo) bool {
// we need values to match against
func (s OneDriveScope) matchesInfo(dii details.ItemInfo) bool {
info := dii.OneDrive
if info == nil {
return false
}
// the scope must define targets to match on
filterCat := s.FilterCategory()
targets := s.Get(filterCat)

View File

@ -93,19 +93,14 @@ type (
// internal to scopes.go can utilize the scope's category without the service context.
categorizer() categorizer
// matchesEntry is used to determine if the scope values match with either the pathValues,
// or the DetailsEntry for the given category.
// The path comparison (using cat and pathValues) can be handled generically within
// scopes.go. However, the entry comparison requires service-specific context in order
// for the scope to extract the correct serviceInfo in the entry.
// matchesInfo is used to determine if the scope values match a specific DetailsEntry
// ItemInfo filter. Unlike path filtering, the entry comparison requires service-specific
// context in order for the scope to extract the correct serviceInfo in the entry.
//
// Params:
// cat - the category type expressed in the Path. Not the category of the Scope. If the
// scope does not align with this parameter, the result is automatically false.
// pathValues - the result of categorizer.pathValues() for the Path being checked.
// entry - the details entry containing extended service info for the item that a filter may
// compare. Identification of the correct entry Info service is left up to the scope.
matchesEntry(cat categorizer, pathValues map[categorizer]string, entry details.DetailsEntry) bool
// info - the details entry itemInfo containing extended service info that a filter may
// compare. Identification of the correct entry Info service is left up to the fulfiller.
matchesInfo(info details.ItemInfo) bool
// setDefaults populates default values for certain scope categories.
// Primarily to ensure that root- or mid-tier scopes (such as folders)
@ -220,9 +215,9 @@ func reduce[T scopeT, C categoryT](
}
// aggregate each scope type by category for easier isolation in future processing.
excls := scopesByCategory[T](s.Excludes, dataCategories)
filts := scopesByCategory[T](s.Filters, dataCategories)
incls := scopesByCategory[T](s.Includes, dataCategories)
excls := scopesByCategory[T](s.Excludes, dataCategories, false)
filts := scopesByCategory[T](s.Filters, dataCategories, true)
incls := scopesByCategory[T](s.Includes, dataCategories, false)
ents := []details.DetailsEntry{}
@ -262,9 +257,12 @@ func reduce[T scopeT, C categoryT](
// ex: a slice containing the scopes [mail1, mail2, event1]
// would produce a map like { mail: [1, 2], event: [1] }
// so long as "mail" and "event" are contained in cats.
// For ALL-mach requirements, scopes used as filters should force inclusion using
// includeAll=true, independent of the category.
func scopesByCategory[T scopeT, C categoryT](
scopes []scope,
cats map[path.CategoryType]C,
includeAll bool,
) map[C][]T {
m := map[C][]T{}
for _, cat := range cats {
@ -274,7 +272,8 @@ func scopesByCategory[T scopeT, C categoryT](
for _, sc := range scopes {
for _, cat := range cats {
t := T(sc)
if typeAndCategoryMatches(cat, t.categorizer()) {
// include a scope if the data category matches, or the caller forces inclusion.
if includeAll || typeAndCategoryMatches(cat, t.categorizer()) {
m[cat] = append(m[cat], t)
}
}
@ -285,8 +284,8 @@ func scopesByCategory[T scopeT, C categoryT](
// passes compares each path to the included and excluded exchange scopes. Returns true
// if the path is included, passes filters, and not excluded.
func passes[T scopeT](
cat categorizer,
func passes[T scopeT, C categoryT](
cat C,
pathValues map[categorizer]string,
entry details.DetailsEntry,
excs, filts, incs []T,
@ -303,7 +302,7 @@ func passes[T scopeT](
var included bool
for _, inc := range incs {
if inc.matchesEntry(cat, pathValues, entry) {
if matchesEntry(inc, cat, pathValues, entry) {
included = true
break
}
@ -316,14 +315,14 @@ func passes[T scopeT](
// all filters must pass
for _, filt := range filts {
if !filt.matchesEntry(cat, pathValues, entry) {
if !matchesEntry(filt, cat, pathValues, entry) {
return false
}
}
// any matching exclusion means failure
for _, exc := range excs {
if exc.matchesEntry(cat, pathValues, entry) {
if matchesEntry(exc, cat, pathValues, entry) {
return false
}
}
@ -331,6 +330,22 @@ func passes[T scopeT](
return true
}
// matchesEntry determines whether the category and scope require a path
// comparison or an entry info comparison.
func matchesEntry[T scopeT, C categoryT](
sc T,
cat C,
pathValues map[categorizer]string,
entry details.DetailsEntry,
) bool {
// filterCategory requires matching against service-specific info values
if len(getFilterCategory(sc)) > 0 {
return sc.matchesInfo(entry.ItemInfo)
}
return matchesPathValues(sc, cat, pathValues, entry.ShortRef)
}
// matchesPathValues will check whether the pathValues have matching entries
// in the scope. The keys of the values to match against are identified by
// the categorizer.
@ -342,27 +357,25 @@ func matchesPathValues[T scopeT, C categoryT](
pathValues map[categorizer]string,
shortRef string,
) bool {
// if scope specifies a filter category,
// path checking is automatically skipped.
if len(getFilterCategory(sc)) > 0 {
return false
}
for _, c := range cat.pathKeys() {
scopeVals := getCatValue(sc, c)
// the scope must define the targets to match on
if len(scopeVals) == 0 {
return false
}
// None() fails all matches
if scopeVals[0] == NoneTgt {
return false
}
// the path must contain a value to match against
// the pathValues must have an entry for the given categorizer
pathVal, ok := pathValues[c]
if !ok {
return false
}
// all parts of the scope must match
cc := c.(C)
if !isAnyTarget(sc, cc) {

View File

@ -103,19 +103,29 @@ func (suite *SelectorScopesSuite) TestContains() {
func (suite *SelectorScopesSuite) TestGetCatValue() {
t := suite.T()
stub := stubScope("")
stub[rootCatStub.String()] = filterize(rootCatStub.String())
assert.Equal(t,
[]string{rootCatStub.String()},
getCatValue(stub, rootCatStub))
assert.Equal(t, None(), getCatValue(stub, leafCatStub))
assert.Equal(t,
None(),
getCatValue(stub, mockCategorizer("foo")))
}
func (suite *SelectorScopesSuite) TestIsAnyTarget() {
t := suite.T()
stub := stubScope("")
assert.True(t, isAnyTarget(stub, rootCatStub))
assert.True(t, isAnyTarget(stub, leafCatStub))
assert.False(t, isAnyTarget(stub, mockCategorizer("smarf")))
stub = stubScope("none")
assert.False(t, isAnyTarget(stub, rootCatStub))
assert.False(t, isAnyTarget(stub, leafCatStub))
assert.False(t, isAnyTarget(stub, mockCategorizer("smarf")))
}
var reduceTestTable = []struct {
@ -161,7 +171,7 @@ var reduceTestTable = []struct {
name: "include all filter none",
sel: func() mockSel {
sel := stubSelector()
sel.Filters[0] = scope(stubScope("none"))
sel.Filters[0] = scope(stubInfoScope("none"))
sel.Excludes = nil
return sel
},
@ -257,7 +267,8 @@ func (suite *SelectorScopesSuite) TestScopesByCategory() {
[]scope{scope(s1), scope(s2)},
map[path.CategoryType]mockCategorizer{
path.UnknownCategory: rootCatStub,
})
},
false)
assert.Len(t, result, 1)
assert.Len(t, result[rootCatStub], 1)
assert.Empty(t, result[leafCatStub])
@ -265,7 +276,8 @@ func (suite *SelectorScopesSuite) TestScopesByCategory() {
func (suite *SelectorScopesSuite) TestPasses() {
cat := rootCatStub
pathVals := map[categorizer]string{}
pth := stubPath(suite.T(), "uid", []string{"fld"}, path.EventsCategory)
pathVals := cat.pathValues(pth)
entry := details.DetailsEntry{}
for _, test := range reduceTestTable {