package web
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"testing"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/ops"
)
// The Rich composer's pickers ask ref.suggest what a sigil could name,
// and insert what comes back as typed: a numbered ticket as #N, one
// without a number by its hash, a landing as !N, a person as @handle,
// a wiki page as [[Title]]. What the reader typed after the sigil
// narrows the list by the reference or its title.
func TestRefSuggestOffersReferencesAsTyped(t *testing.T) {
type listIn struct {
Repo string `json:"repo"`
State string `json:"state"`
Limit int `json:"limit"`
Q string `json:"q"`
}
type ticket struct {
ID string `json:"id"`
Title string `json:"title"`
Number int `json:"number,omitempty"`
}
type tickets struct {
Tickets []ticket `json:"tickets"`
}
type landing struct {
Title string `json:"title"`
Number int `json:"number,omitempty"`
}
type landings struct {
Landings []landing `json:"landings"`
}
type handle struct {
Handle string `json:"handle"`
}
type handles struct {
Handles []handle `json:"handles"`
}
type page struct {
Title string `json:"title"`
}
type pages struct {
Pages []page `json:"pages"`
}
r := ops.NewRegistry()
must := func(err error) {
if err != nil {
t.Fatal(err)
}
}
must(ops.Register(r, ops.Op[listIn, tickets]{Name: "ticket.list", Doc: "stub", Kind: ops.Query, Auth: ops.Anonymous,
Route: "GET /api/v1/r/{repo}/tickets",
Run: func(context.Context, listIn) (tickets, error) {
return tickets{[]ticket{{ID: "aaaa1111bbbb2222", Title: "Accept is slow", Number: 263},
{ID: "cccc3333dddd4444", Title: "Accent colour in dark mode"},
{ID: "eeee5555ffff6666", Title: "Send the file view whole", Number: 350}}}, nil
}}))
must(ops.Register(r, ops.Op[listIn, landings]{Name: "landing.list", Doc: "stub", Kind: ops.Query, Auth: ops.Anonymous,
Route: "GET /api/v1/r/{repo}/landings",
Run: func(context.Context, listIn) (landings, error) {
return landings{[]landing{{Title: "Rich editor", Number: 1051}, {Title: "never numbered"}}}, nil
}}))
must(ops.Register(r, ops.Op[listIn, handles]{Name: "people.mention", Doc: "stub", Kind: ops.Query, Auth: ops.Anonymous,
Route: "GET /api/v1/r/{repo}/mention",
Run: func(_ context.Context, in listIn) (handles, error) {
return handles{[]handle{{Handle: "zeitgeist"}}}, nil
}}))
must(ops.Register(r, ops.Op[listIn, pages]{Name: "wiki.list", Doc: "stub", Kind: ops.Query, Auth: ops.Anonymous,
Route: "GET /api/v1/r/{repo}/wiki",
Run: func(context.Context, listIn) (pages, error) {
return pages{[]page{{Title: "checkin/0123456789abcdef"}, {Title: "Deploy ritual"}, {Title: "Home"}}}, nil
}}))
verify := Verify(func(repo, user, pass string, need ops.Auth) api.Decision { return api.Allowed })
srv := httptest.NewServer(HandlerWith(r, nil, nil, []Option{WithSessions(fakeSessions{user: "dan", roles: map[string]ops.Auth{"alpha": ops.Token}})}, verify))
t.Cleanup(srv.Close)
ask := func(q string) []string {
req, _ := http.NewRequest("GET", srv.URL+"/r/alpha/ref.suggest?"+q, nil)
req.AddCookie(&http.Cookie{Name: sessionCookie, Value: "good"})
resp, err := http.DefaultClient.Do(req)
if err != nil {
t.Fatal(err)
}
defer resp.Body.Close()
var out struct {
Picks []refPick `json:"picks"`
}
if err := json.NewDecoder(resp.Body).Decode(&out); err != nil {
t.Fatalf("FAIL: %s: %d, not a list: %v", q, resp.StatusCode, err)
}
var toks []string
for _, p := range out.Picks {
toks = append(toks, p.Tok)
}
return toks
}
cases := []struct {
q string
want []string
}{
{"k=ticket", []string{"#263", "[cccc3333dddd]", "#350"}},
{"k=ticket&q=acc", []string{"#263", "[cccc3333dddd]"}},
{"k=ticket&q=35", []string{"#350"}},
{"k=landing", []string{"!1051"}},
{"k=person&q=zeit", []string{"@zeitgeist"}},
{"k=wiki&q=deploy", []string{"[[Deploy ritual]]"}},
{"k=wiki", []string{"[[Deploy ritual]]", "[[Home]]"}},
}
for _, c := range cases {
got := ask(c.q)
if len(got) != len(c.want) {
t.Errorf("FAIL: %s offered %q, want %q", c.q, got, c.want)
continue
}
for i := range got {
if got[i] != c.want[i] {
t.Errorf("FAIL: %s offered %q, want %q", c.q, got, c.want)
break
}
}
}
t.Log("ok: each picker offers references as they are typed")
}