package console import ( "context" "crypto/ecdsa" "crypto/elliptic" "crypto/rand" "errors" "fmt" "strings" "testing" "time" ) func fedClient(t *testing.T, fk *fakeKeymail) *Keymail { t.Helper() k := NewKeymail(fk.srv.URL, "https://console.example.test") key, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { t.Fatal(err) } k.SetSigningKey(key) return k } func TestSendFederatedUnknownSenderRegistersThenDelivers(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") k := fedClient(t, fk) id, err := k.SendFederated(context.Background(), "bill@keymail.dev", "subj", "the link") if err != nil { t.Fatal(err) } if fk.registrations != 1 { t.Fatalf("registrations = %d, want 1", fk.registrations) } if len(fk.fedSends) != 1 || fk.fedSends[0].ID != id || fk.fedSends[0].Body != "the link" || fk.fedSends[0].From != "console@console.example.test" || fk.fedSends[0].To != "bill@keymail.dev" { t.Fatalf("fedSends = %+v", fk.fedSends) } } func TestSendFederatedKnownSenderPaysNothing(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") k := fedClient(t, fk) ctx := context.Background() if _, err := k.SendFederated(ctx, "bill@keymail.dev", "s", "one"); err != nil { t.Fatal(err) } if _, err := k.SendFederated(ctx, "bill@keymail.dev", "s", "two"); err != nil { t.Fatal(err) } if fk.registrations != 1 { t.Fatalf("registrations = %d after two sends, want 1", fk.registrations) } } // Review Focus 1: the console's key changed since it registered. func TestSendFederatedReRegistersARotatedKey(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") fk.senders["console@console.example.test"] = "an-old-key" k := fedClient(t, fk) if _, err := k.SendFederated(context.Background(), "bill@keymail.dev", "s", "b"); err != nil { t.Fatal(err) } if fk.registrations != 1 || len(fk.fedSends) != 1 { t.Fatalf("registrations=%d fedSends=%d, want 1 and 1", fk.registrations, len(fk.fedSends)) } } func TestSendFederatedRefusesAPriceAboveTheCap(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") fk.powBits = maxFederationPowBits + 1 k := fedClient(t, fk) _, err := k.SendFederated(context.Background(), "bill@keymail.dev", "s", "b") if !errors.Is(err, ErrFederationPriceTooHigh) { t.Fatalf("err = %v, want ErrFederationPriceTooHigh", err) } if fk.registrations != 0 { t.Fatal("registered despite refusing the price") } } // Review Focus 2 and the non-Keymail host. func TestSendFederatedNoMailbox(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") fk.noMailbox["ghost"] = true k := fedClient(t, fk) for _, to := range []string{"ghost@keymail.dev", "bill@gmail.com"} { _, err := k.SendFederated(context.Background(), to, "s", "b") if !errors.Is(err, ErrNoKeymailMailbox) { t.Errorf("SendFederated(%s) = %v, want ErrNoKeymailMailbox", to, err) } // The lookup's own error stays reachable (%w: %w). var ke *keymailError if !errors.As(err, &ke) { t.Errorf("SendFederated(%s) = %v, want the keymailError wrapped too", to, err) } } } func TestSendFederatedBlocked(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") fk.blocked["console@console.example.test"] = true k := fedClient(t, fk) _, err := k.SendFederated(context.Background(), "bill@keymail.dev", "s", "b") if err == nil || !strings.Contains(err.Error(), "blocked") { t.Fatalf("err = %v, want a blocked error", err) } if !IsKeymailBlocked(err) { t.Fatalf("IsKeymailBlocked(%v) = false", err) } } func TestIsKeymailBlocked(t *testing.T) { blocked := &keymailError{Status: 403, Msg: "blocked"} for _, c := range []struct { err error want bool }{ {nil, false}, {blocked, true}, {fmt.Errorf("keymail: deliver: %w", blocked), true}, {&keymailError{Status: 403, Msg: "unknown_sender"}, false}, {&keymailError{Status: 500, Msg: "blocked"}, false}, {ErrFederationPriceTooHigh, false}, } { if got := IsKeymailBlocked(c.err); got != c.want { t.Errorf("IsKeymailBlocked(%v) = %v, want %v", c.err, got, c.want) } } } func TestSendFederatedWithoutKey(t *testing.T) { fk := newFakeKeymail(t, "paul@keymail.dev") k := NewKeymail(fk.srv.URL, "https://console.example.test") if _, err := k.SendFederated(context.Background(), "bill@keymail.dev", "s", "b"); err == nil { t.Fatal("SendFederated without a signing key succeeded") } } // Final review Important #2: proof of work is one core's worth at a time. // A second registration waits for the slot and gives up at its deadline. func TestPowSlotIsOneAtATimeAndHonoursTheDeadline(t *testing.T) { release, err := acquirePowSlot(context.Background()) if err != nil { t.Fatalf("first acquire: %v", err) } released := false releaseOnce := func() { if !released { release() released = true } } defer releaseOnce() ctx, cancel := context.WithTimeout(context.Background(), 50*time.Millisecond) defer cancel() start := time.Now() if _, err := acquirePowSlot(ctx); !errors.Is(err, context.DeadlineExceeded) { t.Fatalf("second acquire while held = %v, want DeadlineExceeded", err) } if d := time.Since(start); d > time.Second { t.Fatalf("waiting acquire took %v", d) } releaseOnce() again, err := acquirePowSlot(context.Background()) if err != nil { t.Fatalf("acquire after release: %v", err) } again() }