package api
import (
"context"
"encoding/json"
"strings"
"testing"
"craftdesign.group/quarry/internal/ops"
)
var inviteToken = strings.Repeat("cd", 32)
type inviteAccounts struct{ *resetAccounts }
func (f inviteAccounts) InviteAccount(string, string, bool) (string, error) { return inviteToken, nil }
// #319: an invite that went to the invitee's own address is theirs.
// The admin learns that it went, and does not get a copy of the link.
func TestAnEmailedInviteIsNotHandedToTheAdmin(t *testing.T) {
accts := inviteAccounts{&resetAccounts{fakeAccounts: newFakeAccounts()}}
_ = accts.AddAccount("root", "root-password", true)
mail := &textMail{}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Accounts: accts, Mail: mail, Origin: "https://forge.example"}); err != nil {
t.Fatal(err)
}
ctx := ops.WithUser(context.Background(), "root")
out, err := r.Invoke(ctx, "account.invite", accountInviteIn{Name: "bob", Email: "[email protected]"})
if err != nil {
t.Fatal(err)
}
if len(mail.sent) != 1 || !strings.Contains(mail.sent[0].Text, inviteToken) {
t.Fatalf("FAIL: the invitee was not mailed the link: %+v", mail.sent)
}
wire, _ := json.Marshal(out)
var got map[string]any
_ = json.Unmarshal(wire, &got)
if got["emailed"] != true {
t.Errorf("FAIL: emailed = %v", got["emailed"])
}
if strings.Contains(string(wire), inviteToken) {
t.Errorf("FAIL: the emailed invite's token came back to the admin: %s", wire)
}
// With no address there is nobody else to send it to: the admin
// hands it over, and it only works while the account is unclaimed.
out, err = r.Invoke(ctx, "account.invite", accountInviteIn{Name: "carol"})
if err != nil {
t.Fatal(err)
}
wire, _ = json.Marshal(out)
got = nil
_ = json.Unmarshal(wire, &got)
if got["link"] != "https://forge.example/invite/"+inviteToken {
t.Errorf("FAIL: an invite with no address did not return its link: %s", wire)
}
t.Log("ok: emailed invite withholds the link; unaddressed invite returns it")
}