package api
import (
"strings"
"testing"
)
// fakeMentionAccounts is the minimum AccountStore mentionKnown needs:
// a fixed roster, nothing else callable.
type fakeMentionAccounts struct{ names []string }
func (f fakeMentionAccounts) Accounts() ([]Account, error) {
out := make([]Account, len(f.names))
for i, n := range f.names {
out[i] = Account{Name: n}
}
return out, nil
}
func (f fakeMentionAccounts) AddAccount(string, string, bool) error { return nil }
func (f fakeMentionAccounts) SetPassword(string, string) error { return nil }
func (f fakeMentionAccounts) DeleteAccount(string) error { return nil }
func (f fakeMentionAccounts) Grant(string, string, string) error { return nil }
func (f fakeMentionAccounts) Revoke(string, string) error { return nil }
func (f fakeMentionAccounts) GrantsFor(string) (map[string]string, error) {
return nil, nil
}
// fakeCapSet is a CapSet whose letters are fixed by the test table. It
// mirrors oltp.CapSet.HasAny's umbrella rule (caps.go ~L130) rather than
// a plain membership test: s grants everything, a grants everything
// except s, x, y (fossil's Admin-vs-Setup rule) — mentionKnown calls
// HasAny(anyCapLetters) to test "any standing at all", and a fake that
// only does plain membership would never exercise that umbrella path.
type fakeCapSet string
func (c fakeCapSet) HasAny(letters string) bool {
for _, l := range letters {
if strings.ContainsRune(string(c), l) {
return true
}
if strings.ContainsRune(string(c), 's') {
return true
}
if strings.ContainsRune(string(c), 'a') && !strings.ContainsRune("sxy", l) {
return true
}
}
return false
}
func TestMentionsInFiltersUnknownHandles(t *testing.T) {
tests := []struct {
name string
text string
accts []string
caps map[string]string // account -> caps on "repo"
want []string
}{
{
name: "unknown handle is dropped",
text: "ping @nobody",
accts: nil,
want: nil,
},
{
name: "known account with no caps on the repo is dropped",
text: "ping @bob",
accts: []string{"bob"},
caps: map[string]string{"bob": ""},
want: nil,
},
{
name: "known account with any cap on the repo survives",
text: "ping @alice",
accts: []string{"alice"},
caps: map[string]string{"alice": "C"},
want: []string{"alice"},
},
{
name: "mixed: only the known, capable handle survives",
text: "hey @alice and @nobody and @bob",
accts: []string{"alice", "bob"},
caps: map[string]string{"alice": "C", "bob": ""},
want: []string{"alice"},
},
{
name: "no accounts plane filters nothing",
text: "ping @anyone",
accts: nil,
want: []string{"anyone"},
},
{
name: "setup umbrella cap survives without the literal letter asked for",
text: "ping @root",
accts: []string{"root"},
caps: map[string]string{"root": "s"},
want: []string{"root"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
d := Deps{}
if tt.name != "no accounts plane filters nothing" {
d.Accounts = fakeMentionAccounts{names: tt.accts}
d.CapsFor = func(account, repo string) CapSet {
return fakeCapSet(tt.caps[account])
}
}
got := mentionsIn(tt.text, d.mentionKnown("repo"))
if len(got) != len(tt.want) {
t.Fatalf("mentionsIn(%q) = %v, want %v", tt.text, got, tt.want)
}
for i := range got {
if got[i] != tt.want[i] {
t.Fatalf("mentionsIn(%q) = %v, want %v", tt.text, got, tt.want)
}
}
})
}
}
func TestMentionsInNilKnownFiltersNothing(t *testing.T) {
got := mentionsIn("ping @alice and @bob", nil)
if len(got) != 2 {
t.Fatalf("mentionsIn with nil known = %v, want both handles", got)
}
}