package api
import (
"context"
"testing"
)
// mentionAccounts is the forge's account list and nothing else: the
// question here is whether a NAME renders as a mention, which never
// needed an index.
type mentionAccounts struct{ names []string }
func (m mentionAccounts) Accounts() ([]Account, error) {
out := make([]Account, 0, len(m.names))
for _, n := range m.names {
out = append(out, Account{Name: n})
}
return out, nil
}
func (m mentionAccounts) AddAccount(string, string, bool) error { return nil }
func (m mentionAccounts) SetPassword(string, string) error { return nil }
func (m mentionAccounts) SetOperator(string, string) error { return nil }
func (m mentionAccounts) DeleteAccount(string) error { return nil }
func (m mentionAccounts) Grant(string, string, string, string) (string, error) { return "", nil }
func (m mentionAccounts) Revoke(string, string, string) (string, error) { return "", nil }
func (m mentionAccounts) GrantsFor(string) (map[string]string, error) {
return map[string]string{}, nil
}
// An @name rendered as plain text while the SAME @name sends its owner
// a notification is one mention answering two different questions.
//
// dmestas found it in a forum thread: @dmestas told him and rendered as
// prose, because the notify side asks mentionKnown (an account with
// some capability here) and rendering asked the contributor fold (have
// you committed code to this repository). Rendering asks both now.
func TestSomebodyIsMentionableWithoutHavingCommitted(t *testing.T) {
d := Deps{
Accounts: mentionAccounts{names: []string{"dmestas", "jva", "stranger"}},
CapsFor: func(account, repo string) CapSet {
// Everybody but stranger can see this repository; nobody
// here has ever committed to it.
if account == "stranger" {
return letterCaps("")
}
return letterCaps("r")
},
}
// ix is nil on purpose: with no contributor fold to consult, the
// ONLY thing that can resolve these names is the new predicate.
r := resolver{repo: "sandbox", scope: &refScope{ctx: context.Background(), d: d}}
for _, who := range []string{"dmestas", "jva"} {
url, _, ok := r.ResolveUser(who)
if !ok {
t.Errorf("FAIL: @%s renders as plain text but would be notified", who)
} else {
t.Logf("ok: @%s resolves to %s", who, url)
}
}
// The gate still holds: a name with no way into this repository is
// not a mention here, and an unknown name never becomes a dead link.
for _, who := range []string{"stranger", "nobody"} {
if _, _, ok := r.ResolveUser(who); ok {
t.Errorf("FAIL: @%s resolved, and cannot even read this repository", who)
} else {
t.Logf("ok: @%s stays plain text", who)
}
}
}
// The two rules are ONE rule. A name the notify side would tell is a
// name the renderer must draw, or the reader sees prose where somebody
// was addressed.
func TestRenderingAndNotifyingAgreeOnWhoIsMentionable(t *testing.T) {
d := Deps{
Accounts: mentionAccounts{names: []string{"dmestas", "stranger"}},
CapsFor: func(account, repo string) CapSet {
if account == "stranger" {
return letterCaps("")
}
return letterCaps("r")
},
}
r := resolver{repo: "sandbox", scope: &refScope{ctx: context.Background(), d: d}}
text := "cc @dmestas and @stranger in a forum reply"
told := map[string]bool{}
for _, who := range mentionsIn(text, d.mentionKnown("sandbox")) {
told[who] = true
}
for _, who := range []string{"dmestas", "stranger"} {
_, _, drawn := r.ResolveUser(who)
if drawn != told[who] {
t.Errorf("FAIL: @%s drawn=%v told=%v — one mention, two answers", who, drawn, told[who])
} else {
t.Logf("ok: @%s drawn=%v told=%v", who, drawn, told[who])
}
}
}