package web
import (
"context"
"io"
"net/http"
"net/http/httptest"
"path/filepath"
"regexp"
"strings"
"testing"
"time"
"craftdesign.group/quarry/internal/api"
"craftdesign.group/quarry/internal/index"
"craftdesign.group/quarry/internal/ops"
)
// The front page is public, and so is everything it asks for once it
// has loaded. Datastar follows a redirect by NAVIGATING, so a refresh
// endpoint that answers a stranger with 303 to the login form turns a
// public page into a sign-in wall a moment after it paints — which is
// what gating the listing's refresh as an admin view did to every
// signed-out visitor. The listing is answered as the reader, so a
// private repository stays out of it for a stranger.
func TestTheFrontPageStaysOpenForAStranger(t *testing.T) {
ix, err := index.Open(filepath.Join(t.TempDir(), "index.db"))
if err != nil {
t.Fatal(err)
}
t.Cleanup(func() { ix.Close() })
r := ops.NewRegistry()
if err := api.RegisterAll(r, api.Deps{Version: "v",
Repos: api.NewRepos(map[string]*api.Repo{"alpha": {Index: ix}, "secret": {Index: ix}}),
CanRead: func(ctx context.Context, repo string) bool {
return repo != "secret" || ops.User(ctx) == "dan"
},
}); err != nil {
t.Fatal(err)
}
// As the forge answers: a stranger may do what anyone may, and no more.
verify := Verify(func(_, user, _ string, need ops.Auth) api.Decision {
if need <= ops.Anonymous || user != "" {
return api.Allowed
}
return api.Denied
})
srv := httptest.NewServer(HandlerWith(r, &fakeNotifier{ch: make(chan struct{}, 1)}, nil, []Option{
WithSessions(fakeSessions{user: "dan", roles: map[string]ops.Auth{"alpha": ops.Admin}}),
}, verify))
t.Cleanup(srv.Close)
client := *srv.Client()
client.CheckRedirect = func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }
page, err := client.Get(srv.URL + "/")
if err != nil {
t.Fatal(err)
}
body, _ := io.ReadAll(page.Body)
page.Body.Close()
if page.StatusCode != http.StatusOK {
t.Fatalf("FAIL: the front page answered a stranger %d", page.StatusCode)
}
asks := regexp.MustCompile(`@get\('([^']+)'`).FindAllStringSubmatch(string(body), -1)
if len(asks) == 0 {
t.Fatal("FAIL: the front page asks for nothing live — this test has lost what it guards")
}
seen := map[string]bool{}
for _, a := range asks {
u := a[1]
if seen[u] {
continue
}
seen[u] = true
req, _ := http.NewRequest("GET", srv.URL+u, nil)
req.Header.Set("Datastar-Request", "true")
ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second)
resp, err := client.Do(req.WithContext(ctx))
if err != nil {
cancel()
t.Fatalf("FAIL: %s: %v", u, err)
}
if resp.StatusCode != http.StatusOK {
t.Errorf("FAIL: the front page asks for %s, which answers a stranger %d %s", u,
resp.StatusCode, resp.Header.Get("Location"))
}
if strings.HasSuffix(u, ".frag") {
b, _ := io.ReadAll(resp.Body)
if !strings.Contains(string(b), `id="explore"`) {
t.Errorf("FAIL: %s did not answer with the listing", u)
}
if strings.Contains(string(b), "secret") {
t.Errorf("FAIL: %s named a private repository to a stranger", u)
}
}
resp.Body.Close()
cancel()
}
}