Skip to content
quarry Artifact [65d8517748]
Login

Artifact [65d8517748]

Artifact 65d8517748a6bb76e68f5c5de114095081ff3fe621af41f0aaf408d47159ffa2:


package web

import (
	"net/http/httptest"
	"sync"
	"testing"

	"craftdesign.group/quarry/internal/api"
	"craftdesign.group/quarry/internal/ops"
)

// Building a handler must not write anything a handler already serving
// reads. The parallel tests build handlers while others serve, and a
// forge that ever builds a second one (a reload, a test harness) would
// otherwise hand a request half of one configuration and half of the
// other.
//
// The later handlers place orchard somewhere else, so a handler whose
// routing is overwritten by one built after it fails the final
// assertion even without the race detector, which CI does not run.
// Under -race the concurrent building is caught as well.
func TestBuildingAHandlerDoesNotRaceOneServing(t *testing.T) {
	r := ops.NewRegistry()
	if err := api.RegisterAll(r, api.Deps{Version: "v", Repos: api.NewRepos(map[string]*api.Repo{})}); err != nil {
		t.Fatal(err)
	}
	opts := func(ns string) []Option {
		return []Option{
			WithNamespaces(place(map[string]string{"orchard": ns})),
			WithNamespaceHistory(func(string, string) bool { return false }),
			WithMarks(func(string, string) (string, bool) { return "", false }),
			WithMarksChanged(func() {}),
			WithAgents(func(string) string { return "" }),
		}
	}
	serving := HandlerWith(r, nil, nil, opts("grove"))
	var wg sync.WaitGroup
	for i := 0; i < 4; i++ {
		wg.Add(2)
		go func() {
			defer wg.Done()
			for _, path := range []string{"/r/orchard", "/r/grove/orchard", "/"} {
				serving.ServeHTTP(httptest.NewRecorder(), httptest.NewRequest("GET", path, nil))
			}
		}()
		go func() {
			defer wg.Done()
			HandlerWith(r, nil, nil, opts("elsewhere"))
		}()
	}
	wg.Wait()
	// The first handler still answers by its own configuration.
	rec := httptest.NewRecorder()
	serving.ServeHTTP(rec, httptest.NewRequest("GET", "/r/orchard", nil))
	if rec.Code != 302 || rec.Header().Get("Location") != "/r/grove/orchard" {
		t.Errorf("FAIL: a flat path answered %d %q, want 302 to /r/grove/orchard", rec.Code, rec.Header().Get("Location"))
	}
}

// drawWith has the page templates draw with h until the test ends.
func drawWith(t *testing.T, h handlerHooks) {
	t.Helper()
	was := drawn.Load()
	t.Cleanup(func() { drawn.Store(was) })
	drawn.Store(&h)
}