package api
import (
"context"
"errors"
"path/filepath"
"strings"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// me.signin-link mints for a token with web scope and refuses a token
// without it, and a caller that is not a token at all (RFC 0021, Web
// sign-in).
func TestSigninLinkOpMintsForWebScopeOnly(t *testing.T) {
db, err := oltp.Open(filepath.Join(t.TempDir(), "o.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { db.Close() })
if err := db.AddAccount("jan", "pw-jan-12345", false); err != nil {
t.Fatal(err)
}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(wikiIndex(t)), SigninLinks: db, Origin: "https://forge.test"}); err != nil {
t.Fatal(err)
}
mint := func(scopes ...string) ops.TokenAuth {
_, tok, err := db.MintToken("jan", oltp.TokenSpec{Name: "t", Scopes: scopes})
if err != nil {
t.Fatal(err)
}
return ops.TokenAuth{ID: tok.ID, Caps: func(string) ops.Caps { return oltp.CapSet("o") }}
}
as := func(tok *ops.TokenAuth) context.Context {
ctx := ops.WithUser(context.Background(), "jan")
if tok != nil {
ctx = ops.WithToken(ctx, *tok)
}
return ctx
}
web, apiOnly := mint(oltp.ScopeWeb, oltp.ScopeAPI), mint(oltp.ScopeAPI)
out, err := r.Invoke(as(&web), "me.signin-link", meSigninLinkIn{Next: "/r/quarry"})
if err != nil {
t.Fatalf("FAIL: a web-scoped token was refused: %v", err)
}
got := out.(meSigninLinkOut)
link, ok := strings.CutPrefix(got.URL, "https://forge.test/signin/")
if !ok || got.Expires == "" {
t.Fatalf("FAIL: out = %+v; want an absolute /signin/<link> url and an expiry", got)
}
if _, _, next, ok := db.RedeemSigninLink(link, "", ""); !ok || next != "/r/quarry" {
t.Errorf("FAIL: the minted link redeems ok=%v next=%q; want /r/quarry", ok, next)
}
if _, err := r.Invoke(as(&apiOnly), "me.signin-link", meSigninLinkIn{}); !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: a token without web scope = %v; want forbidden", err)
}
if _, err := r.Invoke(as(nil), "me.signin-link", meSigninLinkIn{}); !errors.Is(err, ops.ErrInvalid) {
t.Errorf("FAIL: a caller that is no token = %v; want invalid", err)
}
}