package api
import (
"context"
"errors"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// Where a new ticket lands follows the REPOSITORY's setting, not the
// caller's privilege. Filing is an ordinary act; permanence is a
// repository policy.
//
// ticket.new took the admin letter for a day, which stopped the API
// bypassing the local tier and also blocked every API caller who had no
// other door — jva could file only through the web form because of it.
// dmestas's call, and the better shape.
func TestANewTicketFollowsTheRepositorysTier(t *testing.T) {
rig := func(tier, caps string) (*ops.Registry, *countingLocal) {
local := &countingLocal{}
r := ops.NewRegistry()
if err := RegisterAll(r, Deps{Version: "v", Repos: testRepos(wikiIndex(t)),
Local: local,
TicketTier: func(string) string { return tier },
CapsFor: func(string, string) CapSet { return letterCaps(caps) },
}); err != nil {
t.Fatal(err)
}
return r, local
}
ctx := ops.WithUser(context.Background(), "jva")
// The default tier: an ordinary filer reaches the forge's own tier
// rather than a 403.
r, local := rig("", "n")
if _, err := r.Invoke(ctx, "ticket.new", ticketNewIn{Repo: "test", Title: "from the API"}); err != nil {
t.Fatalf("FAIL: an ordinary account could not file through the API: %v", err)
}
if local.made != 1 {
t.Errorf("FAIL: %d local tickets, want 1 — it did not follow the tier", local.made)
} else {
t.Log("ok: a forge-tier repository files through intake, from the API")
}
// Overriding into the artifact log without the letter is refused,
// and refused BY NAME rather than with a bare 403.
r2, local2 := rig("", "n")
_, err := r2.Invoke(ctx, "ticket.new", ticketNewIn{Repo: "test", Title: "sneak", Fossil: true})
if !errors.Is(err, ops.ErrForbidden) {
t.Errorf("FAIL: an override without the admin letter was allowed: %v", err)
} else if local2.made != 0 {
t.Error("FAIL: the refused override still wrote something")
} else {
t.Logf("ok: refused — %v", err)
}
}
// A forge with NO local tier keeps the old door — there is nowhere
// else for the ticket to go, and refusing would close the tracker on a
// deployment that never opted into the tier. That case is guarded by
// TestTicketSetAndWikiSaveAttach and the web's TestBoardWorksWithout-
// Script, both of which file through ticket.new against Deps with no
// Local and both of which FAILED when this routing first landed without
// the d.Local == nil fallback. A third test here would need a Zit stub
// to reach the artifact write and would guard nothing those two do not.
type countingLocal struct {
noLocalAttachments
made int
}
func (c *countingLocal) LocalTicketNew(_, _, _ string, _ map[string]string) (string, error) {
c.made++
return "abc123", nil
}
func (c *countingLocal) LocalTicketSet(string, string, string, map[string]string) error { return nil }
func (c *countingLocal) LocalTicketByPrefix(string, string) (oltp.LocalTicket, bool, error) {
return oltp.LocalTicket{}, false, nil
}
func (c *countingLocal) LocalTickets(string) ([]oltp.LocalTicket, error) { return nil, nil }
func (c *countingLocal) LocalTicketDiscard(string, string) error { return nil }
func (c *countingLocal) LocalTicketPromoted(string, string, string, string) error { return nil }
func (c *countingLocal) LocalTicketClaim(string, string) (string, bool, error) { return "t", true, nil }
func (c *countingLocal) LocalTicketRelease(string, string, string) error { return nil }
func (c *countingLocal) LocalTicketRedirect(string, string) (string, bool) { return "", false }