package xfer
import (
"strings"
"testing"
)
// A push refusal has to name the half that is missing AND give the
// remedy for THAT half.
//
// It used to name both halves, say one of them was missing, and then
// prescribe minting a credential. jva measured all four cells with only
// the check-in letter changing between them and found that a person who
// HELD a credential and lacked the letter was told to mint another one
// — advice they can follow, that changes nothing, and that points away
// from the person who could actually help (G-128).
//
// A wrong remedy is worse than a missing one, because it is followable.
func TestAPushRefusalGivesTheRemedyForTheHalfThatFailed(t *testing.T) {
for _, c := range []struct {
what string
user string
mayWrite, hasCred bool
wants, mustNotSay []string
}{
{
what: "anonymous", user: "", mayWrite: false, hasCred: false,
wants: []string{"anonymous", "--user"},
},
{
what: "holds a credential, lacks the letter", user: "jan",
mayWrite: false, hasCred: true,
wants: []string{"check-in letter", "admin"},
// The cell jva caught: do not send them to mint a second
// credential when they already have one.
mustNotSay: []string{"mint"},
},
{
what: "holds the letter, no credential", user: "jan",
mayWrite: true, hasCred: false,
wants: []string{"no push credential", "sync-token"},
},
{
what: "both present, wrong secret signed the card", user: "jan",
mayWrite: true, hasCred: true,
wants: []string{"did not verify", "account password"},
// Nothing is missing here, so the old word was wrong too.
mustNotSay: []string{"is missing"},
},
} {
got := refusalFor(c.user, c.mayWrite, c.hasCred)
for _, w := range c.wants {
if !strings.Contains(got, w) {
t.Errorf("FAIL [%s]: refusal never says %q: %s", c.what, w, got)
}
}
for _, n := range c.mustNotSay {
if strings.Contains(got, n) {
t.Errorf("FAIL [%s]: refusal says %q, which is the wrong cure: %s", c.what, n, got)
}
}
t.Logf("ok [%s]: %s", c.what, got)
}
}
// The four states must not collapse: two cells sharing a sentence is
// how the wrong remedy survived, since a reader could not tell which
// case they were in.
func TestEachRefusalStateReadsDifferently(t *testing.T) {
seen := map[string]string{}
for _, c := range []struct {
what string
user string
mayWrite, hasCred bool
}{
{"anonymous", "", false, false},
{"no letter", "jan", false, true},
{"no credential", "jan", true, false},
{"bad secret", "jan", true, true},
} {
got := refusalFor(c.user, c.mayWrite, c.hasCred)
if prev, dup := seen[got]; dup {
t.Errorf("FAIL: %q and %q share one sentence: %s", prev, c.what, got)
continue
}
seen[got] = c.what
}
t.Logf("ok: %d states, %d distinct sentences", 4, len(seen))
}