detached 0642256

test(host): check the API path for base URLs with a prefix and a trailing slash

commit
0642256cb931bab276c1752e388bd57b8ebf27b5
author
brice · · 2026-10-12 22:23 UTC
parent
2785ef1
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
2 files · +22 -1
M README.md +1 -1
@@ -91,7 +91,7 @@ the host process; the sandbox receives the workflow and its contexts only.
9191
9292 | Variable | Meaning |
9393 | --- | --- |
94-| `ERMITE_RUNNER_URL` | base URL of ermite |
94+| `ERMITE_RUNNER_URL` | base URL of ermite, as in a browser (the runner adds `/-/api` itself) |
9595 | `ERMITE_RUNNER_NAME` | runner name (default: the host name) |
9696 | `ERMITE_RUNNER_LABELS` | extra labels, comma separated (`ubuntu-latest,ubuntu-24.04`) |
9797 | `ERMITE_RUNNER_REGISTER_TOKEN` | token for `register` (see above) |
@@ -91,7 +91,7 @@ the host process; the sandbox receives the workflow and its contexts only.
91 91
92 | Variable | Meaning |92 | Variable | Meaning |
93 | --- | --- |93 | --- | --- |
94-| `ERMITE_RUNNER_URL` | base URL of ermite |94+| `ERMITE_RUNNER_URL` | base URL of ermite, as in a browser (the runner adds `/-/api` itself) |
95 | `ERMITE_RUNNER_NAME` | runner name (default: the host name) |95 | `ERMITE_RUNNER_NAME` | runner name (default: the host name) |
96 | `ERMITE_RUNNER_LABELS` | extra labels, comma separated (`ubuntu-latest,ubuntu-24.04`) |96 | `ERMITE_RUNNER_LABELS` | extra labels, comma separated (`ubuntu-latest,ubuntu-24.04`) |
97 | `ERMITE_RUNNER_REGISTER_TOKEN` | token for `register` (see above) |97 | `ERMITE_RUNNER_REGISTER_TOKEN` | token for `register` (see above) |
M internal/host/rpc_test.go +21 -0
@@ -93,6 +93,27 @@ func TestClientRefusesToSendTheTokenInClear(t *testing.T) {
9393 }
9494 }
9595
96+// The API lives under /-/api of the base URL, which may carry a path prefix
97+// and a trailing slash.
98+func TestClientCallsTheAPIPath(t *testing.T) {
99+ for base, want := range map[string]string{
100+ "https://h": "/-/api/ermite.RunnerService/FetchJob",
101+ "https://h/": "/-/api/ermite.RunnerService/FetchJob",
102+ "https://h/ermite": "/ermite/-/api/ermite.RunnerService/FetchJob",
103+ "https://h/ermite/": "/ermite/-/api/ermite.RunnerService/FetchJob",
104+ } {
105+ var got string
106+ rt := roundTripFunc(func(r *http.Request) (*http.Response, error) {
107+ got = r.URL.Path
108+ return nil, context.Canceled
109+ })
110+ _, _ = NewClient(base, "ermiterun_secret", rt).FetchJob(context.Background(), connect.NewRequest(&ermite.FetchJobRequest{}))
111+ if got != want {
112+ t.Errorf("%s: called %q, want %q", base, got, want)
113+ }
114+ }
115+}
116+
96117 type roundTripFunc func(*http.Request) (*http.Response, error)
97118
98119 func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }
@@ -93,6 +93,27 @@ func TestClientRefusesToSendTheTokenInClear(t *testing.T) {
93 }93 }
94 }94 }
95 95
96+// The API lives under /-/api of the base URL, which may carry a path prefix
97+// and a trailing slash.
98+func TestClientCallsTheAPIPath(t *testing.T) {
99+ for base, want := range map[string]string{
100+ "https://h": "/-/api/ermite.RunnerService/FetchJob",
101+ "https://h/": "/-/api/ermite.RunnerService/FetchJob",
102+ "https://h/ermite": "/ermite/-/api/ermite.RunnerService/FetchJob",
103+ "https://h/ermite/": "/ermite/-/api/ermite.RunnerService/FetchJob",
104+ } {
105+ var got string
106+ rt := roundTripFunc(func(r *http.Request) (*http.Response, error) {
107+ got = r.URL.Path
108+ return nil, context.Canceled
109+ })
110+ _, _ = NewClient(base, "ermiterun_secret", rt).FetchJob(context.Background(), connect.NewRequest(&ermite.FetchJobRequest{}))
111+ if got != want {
112+ t.Errorf("%s: called %q, want %q", base, got, want)
113+ }
114+ }
115+}
116+
96 type roundTripFunc func(*http.Request) (*http.Response, error)117 type roundTripFunc func(*http.Request) (*http.Response, error)
97 118
98 func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }119 func (f roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { return f(r) }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 4

Tags 11

↑↓ moveenter openesc close