~bigbes/sr-ht-ecore

34cf83d43d19071055e7e7593d0646519771e625 — Eugene Blikh 9 days ago 43ad928
follow compare.sr.ht's rename to diff.sr.ht

The service renamed itself; what reaches this repository is the section name
in ecoretest's synthetic config.ini, which every service's switcher test is
built from, and the examples in the chrome docs and README that spelled the
old section. The fixture's custom services now read bench, cover, diff, dolt,
spec, tokens — alphabetical, which is the order BuildNav puts them in.
M README.md => README.md +5 -5
@@ 1,7 1,7 @@
# sr-ht-ecore

Extended core for the custom services of a self-hosted SourceHut instance
(compare, spec, dolt, cover, bench, ...). Everything these services share that
(diff, spec, dolt, cover, bench, ...). Everything these services share that
is *ours* — not upstream's — lives here, so the sr-ht-core fork can stay a
clean mirror of upstream core-go, and so the services stop carrying drifting
copies of the same code.


@@ 111,7 111,7 @@ copies of the same code.
## Usage: chrome

```go
svc := chrome.NewService(conf, "compare.sr.ht")
svc := chrome.NewService(conf, "diff.sr.ht")
svc.StyleHref = cssHref // after discovering the hashed stylesheet
svc.Assets = map[string]string{"bundle.js": bundleHref} // any further artefacts



@@ 180,7 180,7 @@ var webFS embed.FS
cssHref, err := assets.Resolve(webFS, "static/main.min.*.css", assets.DefaultPrefix)
// err is a malformed glob, not a missing stylesheet: absent resolves to "".

svc := chrome.NewService(conf, "compare.sr.ht")
svc := chrome.NewService(conf, "diff.sr.ht")
svc.StyleHref = cssHref

set, err := pages.Load(webFS, pages.Options{Funcs: myHelpers})


@@ 210,8 210,8 @@ seeded crypto:
```go
func TestMain(m *testing.M) { ecoretest.InitCrypto(); os.Exit(m.Run()) }

conf := ecoretest.Config("compare.sr.ht")
noHub := ecoretest.Config("compare.sr.ht", ecoretest.Delete("hub.sr.ht"))
conf := ecoretest.Config("diff.sr.ht")
noHub := ecoretest.Config("diff.sr.ht", ecoretest.Delete("hub.sr.ht"))
```

## Policy: chrome

M chrome/chrome.go => chrome/chrome.go +3 -3
@@ 1,5 1,5 @@
// Package chrome is the shared page chrome for the custom services of a
// self-hosted SourceHut instance (compare, spec, dolt, cover, bench, ...).
// self-hosted SourceHut instance (diff, spec, dolt, cover, bench, ...).
//
// Every one of those services renders the same top strip: the brand (circle
// icon + site name + red service label), the service switcher derived from the


@@ 10,7 10,7 @@
//
// Usage:
//
//	svc := chrome.NewService(conf, "compare.sr.ht")
//	svc := chrome.NewService(conf, "diff.sr.ht")
//	svc.StyleHref = cssHref            // after discovering the hashed stylesheet
//	page := svc.Page(r, "My title", username)
//


@@ 208,7 208,7 @@ type RepoList struct {
// exported fields may be adjusted between NewService and the first Page call
// (they are read, never written, by Page).
type Service struct {
	// Section is the literal config section, e.g. "compare.sr.ht".
	// Section is the literal config section, e.g. "diff.sr.ht".
	Section string
	// StyleHref is the href of the built stylesheet (the hashed
	// main.min.<sha>.css); the zero value renders a bare page rather than

M chrome/chrome_test.go => chrome/chrome_test.go +33 -33
@@ 20,22 20,22 @@ func testConf() ini.File {
			"site-name":   "srht.example",
			"environment": "production",
		},
		"meta.sr.ht":    {"origin": "https://meta.example"},
		"git.sr.ht":     {"origin": "https://git.example"},
		"todo.sr.ht":    {"origin": "https://todo.example"},
		"builds.sr.ht":  {"origin": "https://builds.example"},
		"hub.sr.ht":     {"origin": "https://hub.example"},
		"paste.sr.ht":   {"origin": "https://paste.example"},
		"pages.sr.ht":   {"origin": "https://pages.example"},
		"compare.sr.ht": {"origin": "https://compare.example"},
		"dolt.sr.ht":    {"origin": "https://dolt.example"},
		"ghost.sr.ht":   {}, // no origin -> must not appear
		"webhooks":      {"private-key": "x"},
		"meta.sr.ht":   {"origin": "https://meta.example"},
		"git.sr.ht":    {"origin": "https://git.example"},
		"todo.sr.ht":   {"origin": "https://todo.example"},
		"builds.sr.ht": {"origin": "https://builds.example"},
		"hub.sr.ht":    {"origin": "https://hub.example"},
		"paste.sr.ht":  {"origin": "https://paste.example"},
		"pages.sr.ht":  {"origin": "https://pages.example"},
		"diff.sr.ht":   {"origin": "https://diff.example"},
		"dolt.sr.ht":   {"origin": "https://dolt.example"},
		"ghost.sr.ht":  {}, // no origin -> must not appear
		"webhooks":     {"private-key": "x"},
	}
}

func TestBuildNavOrderExclusionsActive(t *testing.T) {
	nav := BuildNav(testConf(), "compare.sr.ht")
	nav := BuildNav(testConf(), "diff.sr.ht")

	var names []string
	for _, it := range nav {


@@ 43,24 43,24 @@ func TestBuildNavOrderExclusionsActive(t *testing.T) {
	}
	// Canonical services first in canonical order, customs alphabetical after;
	// hub/paste/pages and the origin-less section excluded.
	assert.Equal(t, []string{"git", "todo", "builds", "meta", "compare", "dolt"}, names)
	assert.Equal(t, []string{"git", "todo", "builds", "meta", "diff", "dolt"}, names)

	for _, it := range nav {
		assert.Equal(t, it.Name == "compare", it.Active, "active flag for %s", it.Name)
		assert.Equal(t, it.Name == "diff", it.Active, "active flag for %s", it.Name)
	}
}

func TestPageURLsAndIdentity(t *testing.T) {
	svc := NewService(testConf(), "compare.sr.ht")
	svc := NewService(testConf(), "diff.sr.ht")
	r := httptest.NewRequest("GET", "/~alice/demo?a=1", nil)

	p := svc.Page(r, "t", "alice")
	assert.Equal(t, "srht.example", p.SiteName)
	assert.Equal(t, "compare", p.SiteLabel)
	assert.Equal(t, "diff", p.SiteLabel)
	assert.Equal(t, "https://meta.example/login?return_to="+
		"https%3A%2F%2Fcompare.example%2F~alice%2Fdemo%3Fa%3D1", p.LoginURL)
		"https%3A%2F%2Fdiff.example%2F~alice%2Fdemo%3Fa%3D1", p.LoginURL)
	assert.Equal(t, "https://meta.example/logout?return_to="+
		"https%3A%2F%2Fcompare.example", p.LogoutURL)
		"https%3A%2F%2Fdiff.example", p.LogoutURL)
	// Hub is configured, so the profile link prefers hub's ~username page.
	assert.Equal(t, "https://hub.example/~alice", p.ProfileURL)
	assert.Equal(t, "container", p.ContainerClass)


@@ 70,19 70,19 @@ func TestPageURLsAndIdentity(t *testing.T) {
func TestPageProfileFallsBackToMeta(t *testing.T) {
	conf := testConf()
	delete(conf, "hub.sr.ht")
	svc := NewService(conf, "compare.sr.ht")
	svc := NewService(conf, "diff.sr.ht")
	r := httptest.NewRequest("GET", "/", nil)

	assert.Equal(t, "https://meta.example/profile", svc.Page(r, "t", "alice").ProfileURL)
	// Anonymous viewers get the meta profile link regardless of hub.
	assert.Equal(t, "https://meta.example/profile",
		NewService(testConf(), "compare.sr.ht").Page(r, "t", "").ProfileURL)
		NewService(testConf(), "diff.sr.ht").Page(r, "t", "").ProfileURL)
}

func TestPageEnvBanner(t *testing.T) {
	conf := testConf()
	conf["sr.ht"]["environment"] = "staging"
	svc := NewService(conf, "compare.sr.ht")
	svc := NewService(conf, "diff.sr.ht")
	p := svc.Page(httptest.NewRequest("GET", "/", nil), "t", "")

	assert.True(t, p.ShowBanner)


@@ 102,7 102,7 @@ func render(t *testing.T, v any) string {
}

func TestNavTemplateLoggedIn(t *testing.T) {
	svc := NewService(testConf(), "compare.sr.ht")
	svc := NewService(testConf(), "diff.sr.ht")
	svc.ExtraNav = []NavItem{{Name: "tokens", Origin: "/tokens"}}
	p := svc.Page(httptest.NewRequest("GET", "/", nil), "t", "alice")



@@ 110,7 110,7 @@ func TestNavTemplateLoggedIn(t *testing.T) {
	assert.Contains(t, out, "icon icon-circle")
	// The brand is two links: the site name to hub, the red label to us.
	assert.Contains(t, out, `<a href="https://hub.example">srht.example</a>`)
	assert.Contains(t, out, `<a href="/"><span class="text-danger">compare</span></a>`)
	assert.Contains(t, out, `<a href="/"><span class="text-danger">diff</span></a>`)
	assert.Contains(t, out, `href="https://git.example"`)
	assert.Contains(t, out, `href="/tokens"`, "extra nav entries must render")
	assert.Contains(t, out, "Logged in as")


@@ 118,7 118,7 @@ func TestNavTemplateLoggedIn(t *testing.T) {
}

func TestNavTemplateAnonymous(t *testing.T) {
	svc := NewService(testConf(), "compare.sr.ht")
	svc := NewService(testConf(), "diff.sr.ht")
	p := svc.Page(httptest.NewRequest("GET", "/", nil), "t", "")

	out := render(t, p)


@@ 134,11 134,11 @@ func TestNavTemplateAnonymous(t *testing.T) {
func TestNavBrandWithoutHub(t *testing.T) {
	conf := testConf()
	delete(conf, "hub.sr.ht")
	svc := NewService(conf, "compare.sr.ht")
	svc := NewService(conf, "diff.sr.ht")

	out := render(t, svc.Page(httptest.NewRequest("GET", "/", nil), "t", "alice"))
	assert.Contains(t, out, `<a href="/">srht.example</a>`)
	assert.Contains(t, out, `<a href="/"><span class="text-danger">compare</span></a>`)
	assert.Contains(t, out, `<a href="/"><span class="text-danger">diff</span></a>`)
}

// TestNavTemplateEmbeddedPage guards the documented consumption pattern: a


@@ 245,12 245,12 @@ func TestRepoListEmptyState(t *testing.T) {
// handler redirecting to login must land the viewer exactly where the nav's
// "Log in" would have.
func TestLoginURLForMatchesTheNav(t *testing.T) {
	svc := NewService(testConf(), "compare.sr.ht")
	svc := NewService(testConf(), "diff.sr.ht")
	r := httptest.NewRequest("GET", "/~alice/demo?a=1", nil)

	assert.Equal(t, svc.Page(r, "t", "").LoginURL, svc.LoginURLFor(r))
	assert.Equal(t, "https://meta.example/login?return_to="+
		"https%3A%2F%2Fcompare.example%2F~alice%2Fdemo%3Fa%3D1", svc.LoginURLFor(r))
		"https%3A%2F%2Fdiff.example%2F~alice%2Fdemo%3Fa%3D1", svc.LoginURLFor(r))
}

// TestHeadLinksAreGuardedAndTheFaviconSurvivesEscaping covers the two ways the


@@ 267,7 267,7 @@ func TestHeadLinksAreGuardedAndTheFaviconSurvivesEscaping(t *testing.T) {
		return b.String()
	}

	svc := NewService(testConf(), "compare.sr.ht")
	svc := NewService(testConf(), "diff.sr.ht")
	svc.StyleHref = "/static/main.min.0badc0de.css"
	out := renderHead(t, svc.Page(httptest.NewRequest("GET", "/", nil), "t", ""))



@@ 281,7 281,7 @@ func TestHeadLinksAreGuardedAndTheFaviconSurvivesEscaping(t *testing.T) {

	// A binary built without a stylesheet, and a service that switched the
	// icon off, render neither link rather than an empty one.
	bare := NewService(testConf(), "compare.sr.ht")
	bare := NewService(testConf(), "diff.sr.ht")
	bare.FaviconHref = ""
	out = renderHead(t, bare.Page(httptest.NewRequest("GET", "/", nil), "t", ""))
	assert.NotContains(t, out, "<link")


@@ 305,8 305,8 @@ func TestPageCarriesTheServiceAssets(t *testing.T) {
}

func TestServiceAccessors(t *testing.T) {
	svc := NewService(testConf(), "compare.sr.ht")
	assert.Equal(t, "https://compare.example", svc.SelfOrigin())
	svc := NewService(testConf(), "diff.sr.ht")
	assert.Equal(t, "https://diff.example", svc.SelfOrigin())
	assert.Equal(t, "https://meta.example", svc.MetaOrigin())
	assert.Equal(t, "https://hub.example", svc.HubOrigin())
	assert.Equal(t, "srht.example", svc.SiteName())


@@ 314,7 314,7 @@ func TestServiceAccessors(t *testing.T) {

	conf := testConf()
	delete(conf, "hub.sr.ht")
	assert.Empty(t, NewService(conf, "compare.sr.ht").HubOrigin())
	assert.Empty(t, NewService(conf, "diff.sr.ht").HubOrigin())
}

func TestRelTimeFacesBothDirections(t *testing.T) {

M ecoretest/ecoretest.go => ecoretest/ecoretest.go +2 -2
@@ 1,5 1,5 @@
// Package ecoretest is the test bootstrap shared by the custom services of a
// self-hosted SourceHut instance (compare, spec, dolt, cover, bench, tokens).
// self-hosted SourceHut instance (diff, spec, dolt, cover, bench, tokens).
//
// Every one of those services opens its web tests with the same two things: a
// hand-built ini.File standing in for the instance's config.ini, and a TestMain


@@ 111,7 111,7 @@ var upstreamSections = []string{
// customSections are this instance's own services — the ones that share this
// package.
var customSections = []string{
	"compare.sr.ht",
	"diff.sr.ht",
	"spec.sr.ht",
	"dolt.sr.ht",
	"bench.sr.ht",

M ecoretest/ecoretest_test.go => ecoretest/ecoretest_test.go +1 -1
@@ 27,7 27,7 @@ func TestConfigHasTheSectionsTheNavRulesNeed(t *testing.T) {
	}
	assert.Equal(t, []string{
		"git", "lists", "todo", "builds", "man", "meta",
		"bench", "compare", "cover", "dolt", "spec", "tokens",
		"bench", "cover", "diff", "dolt", "spec", "tokens",
	}, names)

	for _, item := range nav {

M instconf/instconf_test.go => instconf/instconf_test.go +6 -6
@@ 214,13 214,13 @@ private-key=def
[git.sr.ht]
internal-origin=http://git.internal:5001

[compare.sr.ht]
origin=https://compare.example.org
[diff.sr.ht]
origin=https://diff.example.org
`)
	err := instconf.Require(conf,
		instconf.Need("sr.ht", "network-key"),
		instconf.Need("webhooks", "private-key"),
		instconf.Need("compare.sr.ht", "origin"),
		instconf.Need("diff.sr.ht", "origin"),
		instconf.NeedAny("git.sr.ht", instconf.APIOriginKeys()...),
	)
	assert.NoError(t, err)


@@ 242,7 242,7 @@ repos=/var/lib/git
		instconf.Need("sr.ht", "network-key"),                      // present
		instconf.Need("webhooks", "private-key"),                   // section absent
		instconf.Need("meta.sr.ht", "origin"),                      // present but blank
		instconf.Need("compare.sr.ht", "origin"),                   // key absent
		instconf.Need("diff.sr.ht", "origin"),                      // key absent
		instconf.NeedAny("git.sr.ht", instconf.APIOriginKeys()...), // ladder empty
	)
	require.Error(t, err)


@@ 253,13 253,13 @@ repos=/var/lib/git
	assert.Equal(t, []string{
		"[webhooks] private-key",
		"[meta.sr.ht] origin",
		"[compare.sr.ht] origin",
		"[diff.sr.ht] origin",
		"[git.sr.ht] one of api-internal-origin, internal-origin, api-origin, origin",
	}, missing.Strings(), "every gap in one pass, in the order asked")

	msg := err.Error()
	assert.Contains(t, msg, "incomplete configuration")
	assert.Contains(t, msg, "[compare.sr.ht] origin")
	assert.Contains(t, msg, "[diff.sr.ht] origin")
	assert.NotContains(t, msg, "network-key", "a satisfied key must not be reported")
}