Files
warmbly/internal/models/sequence_thread_test.go

117 lines
4.0 KiB
Go

package models
import "testing"
// StepSubject answers "what subject does this step actually send", which is
// not the column once a step replies in the contact's thread. Preflight, the
// content score and the test send all read it, and getting it wrong reports
// every follow-up as having no subject line (issue #472).
func TestStepSubject(t *testing.T) {
steps := []Sequence{
{Subject: "Quick question", Kind: "email", ThreadReply: true},
{Subject: "", Kind: "email", ThreadReply: true},
{Subject: "", Kind: "action", ThreadReply: true},
{Subject: "", Kind: "email", ThreadReply: true},
{Subject: "New angle", Kind: "email", ThreadReply: false},
{Subject: "", Kind: "email", ThreadReply: true},
{Subject: "Own subject", Kind: "email", ThreadReply: false},
}
for _, tc := range []struct {
name string
i int
want string
}{
{"the first step writes its own", 0, "Quick question"},
{"a follow-up inherits the conversation's", 1, "Quick question"},
{"an action node between steps is skipped", 3, "Quick question"},
{"a step that starts a new thread keeps its own", 4, "New angle"},
{"the steps after it inherit that one", 5, "New angle"},
{"a step opting out writes its own", 6, "Own subject"},
} {
t.Run(tc.name, func(t *testing.T) {
if got := StepSubject(steps, tc.i); got != tc.want {
t.Errorf("StepSubject(%d) = %q, want %q", tc.i, got, tc.want)
}
})
}
}
// A threading step with no earlier email to inherit from falls back to its
// own subject rather than sending a blank one. That is the branch-reached and
// the deleted-predecessor case, and it is also what the send path does when it
// finds no parent.
func TestStepSubjectFallsBackWhenThereIsNothingToInherit(t *testing.T) {
steps := []Sequence{
{Subject: "", Kind: "action", ThreadReply: true},
{Subject: "Only subject", Kind: "email", ThreadReply: true},
}
if got := StepSubject(steps, 1); got != "Only subject" {
t.Errorf("StepSubject = %q, want the step's own subject", got)
}
}
// An index nobody has is not a panic.
func TestStepSubjectOutOfRange(t *testing.T) {
if got := StepSubject(nil, 0); got != "" {
t.Errorf("StepSubject(nil, 0) = %q, want empty", got)
}
if got := StepSubject([]Sequence{{Subject: "x"}}, 3); got != "" {
t.Errorf("StepSubject(_, 3) = %q, want empty", got)
}
}
// ThreadReplyDefaults is what keeps `POST /campaigns` with `steps` behaving the
// way it always has for a caller that does not know the field exists: a step
// carrying a subject that is not the conversation's opens a new one, a blank or
// repeated subject continues it (issue #472).
func TestThreadReplyDefaults(t *testing.T) {
for _, tc := range []struct {
name string
subjects []string
want []bool
}{
{
"a blank follow-up continues the conversation, and so does the step after it",
[]string{"Quick question", "", "Quick question"},
[]bool{true, true, true},
},
{
"a subject of its own opens a new conversation, which the next step then continues",
[]string{"Quick question", "New angle", ""},
[]bool{true, false, true},
},
{
"a step repeating an older subject, not the current conversation's, opens its own",
[]string{"Quick question", "", "New angle", "", "Quick question"},
[]bool{true, true, false, true, false},
},
{
"whitespace is not a different subject",
[]string{"Quick question", " Quick question "},
[]bool{true, true},
},
{
"nothing to differ from yet",
[]string{"", "Quick question"},
[]bool{true, true},
},
{"no steps", nil, []bool{}},
} {
t.Run(tc.name, func(t *testing.T) {
steps := make([]CreateSequenceInput, 0, len(tc.subjects))
for _, sub := range tc.subjects {
steps = append(steps, CreateSequenceInput{Subject: sub})
}
got := ThreadReplyDefaults(steps)
if len(got) != len(tc.want) {
t.Fatalf("got %d answers, want %d", len(got), len(tc.want))
}
for i := range got {
if got[i] != tc.want[i] {
t.Errorf("step %d = %v, want %v (subjects %q)", i, got[i], tc.want[i], tc.subjects)
}
}
})
}
}