package markup
import "testing"
func refIDs(refs []RefToken) []string {
var out []string
for _, r := range refs {
out = append(out, r.ID)
}
return out
}
// addresses is the verb for work on a ticket that does not finish it.
// Same grammar, same link, and no effect on the ticket's state.
func TestAddressesParsesLikeClosesAndActsLikeNothing(t *testing.T) {
const msg = "landings: the second half\n\naddresses a488d7fa42 and closes 0eafaa1984."
if got := refIDs(AddressingRefs(msg)); len(got) != 1 || got[0] != "a488d7fa42" {
t.Errorf("FAIL: addresses read %q, want [a488d7fa42]", got)
}
if got := refIDs(ClosingRefs(msg)); len(got) != 1 || got[0] != "0eafaa1984" {
t.Errorf("FAIL: closes read %q, want [0eafaa1984]", got)
}
}
// The two verbs never read each other's references. A landing that
// addresses a ticket must not close it, and that separation is the
// whole reason the second verb exists.
func TestTheTwoVerbsDoNotOverlap(t *testing.T) {
if got := ClosingRefs("addresses a488d7fa42"); len(got) != 0 {
t.Errorf("FAIL: an addresses was read as a close: %q", refIDs(got))
}
if got := AddressingRefs("closes a488d7fa42"); len(got) != 0 {
t.Errorf("FAIL: a close was read as an addresses: %q", refIDs(got))
}
}
// Same distance rule as closes: the reference must follow the verb, or
// prose that happens to contain both words would act.
func TestAddressesNeedsTheReferenceRightAfterTheVerb(t *testing.T) {
if got := AddressingRefs("this addresses the gap that a488d7fa42 describes"); len(got) != 0 {
t.Errorf("FAIL: prose was read as a declaration: %q", refIDs(got))
}
if got := refIDs(AddressingRefs("addresses: [a488d7fa42]")); len(got) != 1 || got[0] != "a488d7fa42" {
t.Errorf("FAIL: the bracketed spelling read %q", got)
}
}
// Adjacency is the safety, not a short verb list, so the tenses people
// actually write all count. A landing message in the past tense is the
// common case: "fixed a488d7fa42" is a reader plainly asking for the
// link, and refusing it bought nothing.
func TestEveryTenseOfTheClosingVerbsCounts(t *testing.T) {
for _, verb := range []string{"close", "closes", "closed", "closing",
"fix", "fixes", "fixed", "fixing", "resolve", "resolves", "resolved", "resolving"} {
got := refIDs(ClosingRefs(verb + " a488d7fa42"))
if len(got) != 1 || got[0] != "a488d7fa42" {
t.Errorf("FAIL: %q did not close: %q", verb, got)
}
if other := AddressingRefs(verb + " a488d7fa42"); len(other) != 0 {
t.Errorf("FAIL: %q also addressed: %q", verb, refIDs(other))
}
}
}
// The addressing set is wider on purpose and safe to be: a false
// positive links a ticket a reader can see is linked, where a false
// positive on a close shuts somebody's ticket.
func TestTheWaysToSayAddressesAllCount(t *testing.T) {
for _, verb := range []string{"address", "addresses", "addressed", "addressing",
"ref", "refs", "reference", "references", "referenced", "referencing",
"part of", "relates to", "relate to", "related to", "relating to",
"works on", "work on", "working on", "toward", "towards",
"progress on", "progresses on", "contributes to", "contribute to"} {
got := refIDs(AddressingRefs(verb + " a488d7fa42"))
if len(got) != 1 || got[0] != "a488d7fa42" {
t.Errorf("FAIL: %q did not address: %q", verb, got)
}
if other := ClosingRefs(verb + " a488d7fa42"); len(other) != 0 {
t.Errorf("FAIL: %q also closed: %q", verb, refIDs(other))
}
}
}
// The words that precede a reference in ordinary prose are NOT
// declarations. The adjacency rule cannot help there, because the
// reference is the next token either way.
func TestProseWordsAreNotDeclarations(t *testing.T) {
for _, prose := range []string{
"see a488d7fa42 for the numbers",
"re a488d7fa42",
"waited for 3600000",
"the fix for a488d7fa42 is elsewhere",
} {
if got := AddressingRefs(prose); len(got) != 0 {
t.Errorf("FAIL: %q read as an addresses: %q", prose, refIDs(got))
}
if got := ClosingRefs(prose); len(got) != 0 {
t.Errorf("FAIL: %q read as a close: %q", prose, refIDs(got))
}
}
}