package api
import (
"errors"
"strings"
"testing"
"craftdesign.group/quarry/internal/oltp"
"craftdesign.group/quarry/internal/ops"
)
// A PROMOTED local id names the artifact its files belong on; an absent
// target gets the plain refusal.
//
// A forge-tier ticket now takes attachments and holds them until
// promotion (localattach_test.go), so the one local case that still
// refuses is the id a promoted ticket left behind: it is a redirect,
// and "no ticket" would read as if the ticket never existed. The
// refusal says where it went instead — the same not-found, with the
// artifact named — while an id nothing ever answered to stays the
// generic does-not-exist, so the two are not confused.
func TestAPromotedLocalIdSaysWhereItsFilesBelong(t *testing.T) {
ix, z, _ := attachStore(t)
const id = "46591711a4d778a82c6a63b09be735d0905b4c6c"
const to = "9a7d3a8fd2c8a6b3e4b5c6d7e8f90a1b2c3d4e5f"
d := Deps{Version: "v", Local: &tierLocal{id: id, promotedTo: to},
Repos: NewRepos(map[string]*Repo{"test": {Index: ix, Zit: z}})}
r := ops.NewRegistry()
if err := RegisterAll(r, d); err != nil {
t.Fatal(err)
}
who := as("dan", "bBl")
_, err := r.Invoke(who, "attachment.remove",
attachmentRemoveIn{Repo: "test", Target: "tkt:" + id, Name: "whatever.txt"})
if !errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: a promoted local id did not answer not-found: %v", err)
} else if !strings.Contains(err.Error(), "was promoted to "+to[:12]) || strings.Contains(err.Error(), "wiki page") {
t.Errorf("FAIL: a promoted local id got the generic does-not-exist refusal: %v", err)
}
_, err = r.Invoke(who, "attachment.remove",
attachmentRemoveIn{Repo: "test", Target: "tkt:ffffffffffffffffffffffffffffffffffffffff", Name: "x.txt"})
if !errors.Is(err, ops.ErrNotFound) {
t.Errorf("FAIL: an absent target did not answer not-found: %v", err)
} else if strings.Contains(err.Error(), "promoted") {
t.Errorf("FAIL: an absent target was reported as a promoted ticket: %v", err)
}
}
// tierLocal is a LocalTicketStore holding one ticket, already promoted
// when promotedTo is set: then it answers only as a redirect, as the
// real store does once a ticket's changes are gone.
type tierLocal struct {
noLocalAttachments
id, promotedTo string
}
func (l *tierLocal) LocalTicketNew(_, _, _ string, _ map[string]string) (string, error) {
return l.id, nil
}
func (l *tierLocal) LocalTicketSet(string, string, string, map[string]string) error { return nil }
func (l *tierLocal) LocalTicketByPrefix(_, prefix string) (oltp.LocalTicket, bool, error) {
if l.promotedTo == "" && prefix != "" && strings.HasPrefix(l.id, prefix) {
return oltp.LocalTicket{ID: l.id}, true, nil
}
return oltp.LocalTicket{}, false, nil
}
func (l *tierLocal) LocalTickets(string) ([]oltp.LocalTicket, error) { return nil, nil }
func (l *tierLocal) LocalTicketDiscard(string, string) error { return nil }
func (l *tierLocal) LocalTicketPromoted(string, string, string) error { return nil }
func (l *tierLocal) LocalTicketClaim(string, string) (bool, error) { return true, nil }
func (l *tierLocal) LocalTicketRelease(string, string) error { return nil }
func (l *tierLocal) LocalTicketRedirect(_, prefix string) (string, bool) {
if l.promotedTo != "" && prefix != "" && strings.HasPrefix(l.id, prefix) {
return l.promotedTo, true
}
return "", false
}