Read the walkthrough guard's endpoint from test/.env
The reachability guard looked for the FedWiki farm's and the forum's URLs as fields on the integration settings page. Both keys are deployment-only, so the page shows static text and no field, and the guard returned early: neither the name check nor the reachability skip ever ran. On a network whose resolver drops loopback answers for localtest.me, the sites walkthrough again timed out with its site left Pending instead of naming the host (#236). The guard now reads the URL from test/.env, under the variable the console reads for that key, and no longer visits the settings page. A table test pins the mapping from key to variable for both keys.
This commit is contained in:
@@ -43,11 +43,9 @@ const walkthroughGroup = "walkthrough-members"
|
||||
//
|
||||
// The probe itself is the shared one in helpers_test.go, which FedWiki's
|
||||
// guard also binds; only the key and the operator-facing remedy differ.
|
||||
// Leaves the page on the settings surface; callers must navigate onward
|
||||
// themselves.
|
||||
func skipUnlessForumReachable(t *testing.T, page *rod.Page, base string) {
|
||||
func skipUnlessForumReachable(t *testing.T) {
|
||||
t.Helper()
|
||||
skipUnlessIntegrationEndpointReachable(t, page, base, "discourse", "discourse-base-url",
|
||||
skipUnlessIntegrationEndpointReachable(t, "discourse", "discourse-base-url",
|
||||
"start the discourse compose profile or the fakeserver (see this file's header)")
|
||||
}
|
||||
|
||||
@@ -95,16 +93,13 @@ func submitMappingForm(page *rod.Page, groupName string) {
|
||||
|
||||
func TestDiscourseMappingWalkthrough(t *testing.T) {
|
||||
defer browsertest.Recover(t)
|
||||
page, base := browsertest.OpenOperator(t, "/operator/integrations/discourse")
|
||||
page, _ := browsertest.OpenOperator(t, "/operator/integrations/discourse")
|
||||
|
||||
if strings.Contains(page.MustElement(`#operator-body`).MustHTML(), "Discourse is not configured") {
|
||||
t.Skip("app booted without Discourse config; see this file's header for the fakeserver + flags bootstrap")
|
||||
}
|
||||
|
||||
skipUnlessForumReachable(t, page, base)
|
||||
// The probe leaves the page on the settings surface; return to the
|
||||
// mapping surface before driving the form phases.
|
||||
page.Timeout(15 * time.Second).MustNavigate(base + "/operator/integrations/discourse")
|
||||
skipUnlessForumReachable(t)
|
||||
page.Timeout(10 * time.Second).MustElement(`form[hx-post="/partials/operator/discourse/mappings"]`)
|
||||
|
||||
t.Logf("phase 1a: empty group name → 422 + field error")
|
||||
|
||||
@@ -127,7 +127,7 @@ type sitesWalk struct {
|
||||
func TestFedWikiSitesWalkthrough(t *testing.T) {
|
||||
defer browsertest.Recover(t)
|
||||
page, base := browsertest.OpenOperator(t, "/operator/integrations")
|
||||
skipUnlessFarmReachable(t, page, base)
|
||||
skipUnlessFarmReachable(t)
|
||||
env := browsertest.EnvFromTestDir(t)
|
||||
farmURL, farmToken := env["MC_FEDWIKI_FARM_API_URL"], env["MC_FEDWIKI_ADMIN_TOKEN"]
|
||||
if farmURL == "" || farmToken == "" {
|
||||
|
||||
@@ -65,15 +65,19 @@ func foundIn(html string, banned ...string) []string {
|
||||
}
|
||||
|
||||
// skipUnlessIntegrationEndpointReachable probes the endpoint an integration is
|
||||
// actually configured with — the effective value of key on that integration's
|
||||
// settings page — and skips when it does not answer. It is the generic half of
|
||||
// configured with and skips when it does not answer. It is the generic half of
|
||||
// the pattern skipUnlessForumReachable applies to Discourse: a walkthrough may
|
||||
// be pointed at a compose profile (or an external service) that simply is not
|
||||
// composed, and every service-touching phase would then fail on connection
|
||||
// errors instead of skipping.
|
||||
//
|
||||
// Three outcomes, deliberately distinguished:
|
||||
// - no effective value rendered → return, so a walkthrough's config-plumbing
|
||||
// The endpoint comes from test/.env, the file the console's environment is
|
||||
// built from (endpointEnvVar names the variable). The settings page cannot
|
||||
// supply it: these keys are deployment-only, so the page shows static text
|
||||
// and no field holding the URL.
|
||||
//
|
||||
// Four outcomes, deliberately distinguished:
|
||||
// - no http value in test/.env → return, so a walkthrough's config-plumbing
|
||||
// phases still run against an integration nobody configured;
|
||||
// - configured but refusing connections → skip;
|
||||
// - configured but answering 5xx → skip (container up, service still booting
|
||||
@@ -82,27 +86,10 @@ func foundIn(html string, banned ...string) []string {
|
||||
// point at this machine that resolves elsewhere → fail, naming the
|
||||
// /etc/hosts lines that fix it (requireResolvesHere).
|
||||
//
|
||||
// Reading the URL off the settings page rather than off test/.env keeps the
|
||||
// probe truthful for every bootstrap — compose profile, fake server, or a
|
||||
// service hosted outside compose entirely. Leaves the page on the settings
|
||||
// surface; callers must navigate onward themselves.
|
||||
// hint names, in the caller's own words, what would make the endpoint answer.
|
||||
func skipUnlessIntegrationEndpointReachable(t *testing.T, page *rod.Page, base, integrationKey, key, hint string) {
|
||||
func skipUnlessIntegrationEndpointReachable(t *testing.T, integrationKey, key, hint string) {
|
||||
t.Helper()
|
||||
page.Timeout(15 * time.Second).MustNavigate(base + "/operator/integrations/" + integrationKey + "/settings")
|
||||
// The settings page is one declared form, one field per non-secret key
|
||||
// (operator.integration.settings, forms-library lane D): the endpoint
|
||||
// URL is a plain text field named for its key, not a table row, so its
|
||||
// configured value is the field's own value. Wait for the form, then
|
||||
// read the field; an absent or non-http value means the endpoint is not
|
||||
// configured and the guard proceeds (the settings surface never dials
|
||||
// the endpoint, so an unconfigured one still exercises the phases).
|
||||
page.Timeout(10 * time.Second).MustElement(`form[data-form="operator.integration.settings"]`)
|
||||
has, field, err := page.Has(`[name="` + key + `"]`)
|
||||
if err != nil || !has {
|
||||
return
|
||||
}
|
||||
endpoint := strings.TrimSpace(field.MustProperty("value").String())
|
||||
endpoint := strings.TrimSpace(browsertest.EnvFromTestDir(t)[endpointEnvVar(key)])
|
||||
if !strings.HasPrefix(endpoint, "http") {
|
||||
return
|
||||
}
|
||||
@@ -118,6 +105,13 @@ func skipUnlessIntegrationEndpointReachable(t *testing.T, page *rod.Page, base,
|
||||
}
|
||||
}
|
||||
|
||||
// endpointEnvVar names the environment variable the console reads for a
|
||||
// config key: the MC prefix, the key upper-cased, and "-" replaced by "_"
|
||||
// (viper.SetEnvPrefix, SetEnvKeyReplacer and AutomaticEnv in cmd/root.go).
|
||||
func endpointEnvVar(key string) string {
|
||||
return "MC_" + strings.ToUpper(strings.ReplaceAll(key, "-", "_"))
|
||||
}
|
||||
|
||||
// resolutionProblem says why host's lookup answer does not let a test reach
|
||||
// it, or "" when it does: the lookup failed, it returned no address, or a
|
||||
// name defined to point at this machine (localhost, *.localhost,
|
||||
@@ -176,9 +170,9 @@ func requireResolvesHere(t *testing.T, integrationKey, endpoint string) {
|
||||
// skipUnlessFarmReachable is the FedWiki binding of the guard above. The farm
|
||||
// API URL is the endpoint every FedWiki surface dials, and the `fedwiki`
|
||||
// compose profile is what composes a farm to answer it.
|
||||
func skipUnlessFarmReachable(t *testing.T, page *rod.Page, base string) {
|
||||
func skipUnlessFarmReachable(t *testing.T) {
|
||||
t.Helper()
|
||||
skipUnlessIntegrationEndpointReachable(t, page, base, "fedwiki", "fedwiki-farm-api-url",
|
||||
skipUnlessIntegrationEndpointReachable(t, "fedwiki", "fedwiki-farm-api-url",
|
||||
`add "fedwiki" to COMPOSE_PROFILES in test/.env, bring the stack up, and uncomment the MC_FEDWIKI_* block`)
|
||||
}
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ func TestIntegrationSettingsWalkthrough(t *testing.T) {
|
||||
// has always had. An unconfigured farm is not a skip: the settings surface
|
||||
// is config plumbing and never dials the farm, so the guard returns and
|
||||
// the phases run.
|
||||
skipUnlessFarmReachable(t, page, base)
|
||||
skipUnlessFarmReachable(t)
|
||||
|
||||
settingsSaveOverride(t, page, base)
|
||||
originalInterval := settingsRefuseDuration(t, page)
|
||||
|
||||
@@ -36,3 +36,14 @@ func TestResolutionProblem(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestEndpointEnvVar(t *testing.T) {
|
||||
for key, want := range map[string]string{
|
||||
"fedwiki-farm-api-url": "MC_FEDWIKI_FARM_API_URL",
|
||||
"discourse-base-url": "MC_DISCOURSE_BASE_URL",
|
||||
} {
|
||||
if got := endpointEnvVar(key); got != want {
|
||||
t.Errorf("endpointEnvVar(%q) = %q, want %q", key, got, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user