asks: the proof sees what the phone shows at a tap, and the question's outcome as its title (hq issue 372)
mesh/merge-gate error: the check could not run: the base branch proofs/asks-answered-on-the-phone cannot be read, and the check it holds judges the change:…
mesh/repo-check error: the check could not run: the base branch proofs/asks-answered-on-the-phone cannot be read, and the check it holds judges the change:…
mesh/delivery rejected: the gate failed or could not run, or the repository's own check failed
mesh/merge-gate error: the check could not run: the base branch proofs/asks-answered-on-the-phone cannot be read, and the check it holds judges the change:…
mesh/repo-check error: the check could not run: the base branch proofs/asks-answered-on-the-phone cannot be read, and the check it holds judges the change:…
mesh/delivery rejected: the gate failed or could not run, or the repository's own check failed
The operator approved on Telegram and saw nothing: the tap was acknowledged with no words and the question kept its title. The proof now checks the toast says the approval was recorded, no earlier than the warrant; that the edited question opens with how it ended; and that the same answer tapped again is said as already recorded, not refused.
This commit is contained in:
@@ -25,10 +25,17 @@ type fakeTelegram struct {
|
||||
arrived chan struct{}
|
||||
sent []tgMessage
|
||||
msgID int64
|
||||
taps int // answerCallbackQuery calls
|
||||
taps int // answerCallbackQuery calls
|
||||
toasts []toast // what each acknowledgement showed on the phone (issue 372)
|
||||
deleted int
|
||||
}
|
||||
|
||||
// toast is a tap's acknowledgement: the short line the phone shows over the chat, and when.
|
||||
type toast struct {
|
||||
Text string
|
||||
At time.Time
|
||||
}
|
||||
|
||||
// tgMessage is one message the bot sent or edited: its chat, its id, its words and its buttons (label →
|
||||
// callback data).
|
||||
type tgMessage struct {
|
||||
@@ -108,9 +115,12 @@ func (f *fakeTelegram) serve(w http.ResponseWriter, r *http.Request) {
|
||||
f.t.Logf("phone: %s to %d (#%d): %q %v", method, chat, m.ID, firstLine(text), m.Buttons)
|
||||
ok(map[string]any{"message_id": m.ID, "chat": map[string]any{"id": chat, "type": "private"}, "text": text})
|
||||
case "answerCallbackQuery":
|
||||
text, _ := body["text"].(string)
|
||||
f.mu.Lock()
|
||||
f.taps++
|
||||
f.toasts = append(f.toasts, toast{Text: text, At: time.Now()})
|
||||
f.mu.Unlock()
|
||||
f.t.Logf("phone: a tap acknowledged with %q", text)
|
||||
ok(true)
|
||||
case "deleteMessage":
|
||||
f.mu.Lock()
|
||||
@@ -280,3 +290,29 @@ func firstLine(s string) string {
|
||||
}
|
||||
return s
|
||||
}
|
||||
|
||||
// waitForToast waits for a tap's acknowledgement since a time that shows what match wants, and fails saying
|
||||
// what the phone showed instead.
|
||||
func (f *fakeTelegram) waitForToast(what string, since time.Time, within time.Duration, match func(string) bool) toast {
|
||||
f.t.Helper()
|
||||
deadline := time.Now().Add(within)
|
||||
for {
|
||||
f.mu.Lock()
|
||||
var seen []string
|
||||
for _, t := range f.toasts {
|
||||
if t.At.Before(since) {
|
||||
continue
|
||||
}
|
||||
if match(t.Text) {
|
||||
f.mu.Unlock()
|
||||
return t
|
||||
}
|
||||
seen = append(seen, fmt.Sprintf("%q", t.Text))
|
||||
}
|
||||
f.mu.Unlock()
|
||||
if time.Now().After(deadline) {
|
||||
f.t.Fatalf("the phone never showed %s; it showed: %s", what, strings.Join(seen, ", "))
|
||||
}
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
+17
-4
@@ -161,9 +161,19 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
|
||||
if got := a.performed(); len(got) != 1 || got[0]["arg.switch"] != "approve" {
|
||||
t.Fatalf("performed %v, want the one bound act once", got)
|
||||
}
|
||||
// Every copy says how it ended, its buttons gone.
|
||||
l.tg.waitFor("the question edited to its outcome", operatorAccount, tapped, 90*time.Second,
|
||||
// At the tap, the phone says it was recorded, and never before the warrant was (issue 372).
|
||||
ack := l.tg.waitForToast("the approval confirmed", tapped, 30*time.Second, func(s string) bool {
|
||||
return strings.HasPrefix(s, "You chose Approve: recorded")
|
||||
})
|
||||
if ack.At.Before(w.At) {
|
||||
t.Errorf("the phone said recorded at %s, before the warrant at %s", ack.At, w.At)
|
||||
}
|
||||
// Every copy says how it ended — as its title, read at a glance — its buttons gone.
|
||||
edited := l.tg.waitFor("the question edited to its outcome", operatorAccount, tapped, 90*time.Second,
|
||||
func(m tgMessage) bool { return m.Edited && m.ID == firstMessage.ID && len(m.Buttons) == 0 })
|
||||
if !strings.HasPrefix(edited.Text, "You chose Approve at ") || !strings.Contains(firstLine(edited.Text), " on Telegram · ") {
|
||||
t.Errorf("the question does not open with how it ended: %q", firstLine(edited.Text))
|
||||
}
|
||||
// And the router's record holds who approved what.
|
||||
e, _ := l.routerRecord("messenger_asks", "lab-asker.a1")
|
||||
var rec struct {
|
||||
@@ -181,8 +191,11 @@ func TestTheOperatorsAnswerEndToEnd(t *testing.T) {
|
||||
t.Run("a replayed tap and a redelivered warrant do nothing more", func(t *testing.T) {
|
||||
tapped := time.Now()
|
||||
l.tg.tapped(operatorAccount, operatorAccount, "private", firstMessage.ID, button(firstMessage, "Approve"))
|
||||
l.tg.waitFor("the refusal of a replayed tap", operatorAccount, tapped, 90*time.Second,
|
||||
func(m tgMessage) bool { return strings.Contains(m.Text, "That answer was not taken") })
|
||||
// The same answer tapped again is what was recorded: the phone says so, and nothing more is done (issue 372:
|
||||
// a refusal here read as the approval failing).
|
||||
l.tg.waitForToast("the replayed tap said as already recorded", tapped, 30*time.Second, func(s string) bool {
|
||||
return strings.HasPrefix(s, "You already chose Approve at ") && strings.HasSuffix(s, ": recorded.")
|
||||
})
|
||||
time.Sleep(2 * time.Second)
|
||||
if n := a.wordsOn("a1"); n != 1 {
|
||||
t.Errorf("the router said %d words on one ask", n)
|
||||
|
||||
Reference in New Issue
Block a user