test: move the end-to-end here, and fix the race that made it flaky
The e2e harness and its mock endpoint were living in the private repo, justified
by "they name the endpoint host". That was true when written and false two
commits later, once the hardcoded URL came out of the wrapper so the route would
live only in Bitwarden. Rechecked: all three name the endpoint zero times -- the
URL, username and password are supplied at runtime -- and the only host they
mention is this repo. They belong next to the dotup they exercise and the unit
suite that covers the rest of it.
The move surfaced a worse problem than the misplacement. The harness was flaky,
and an earlier PASS was partly luck.
chezmoi writes "git user.email?" with the tty still in cooked mode and only then
switches to raw mode -- with TCSAFLUSH, which discards whatever is already
buffered. Answering on the prompt TEXT races that switch. One run answered all
seven prompts; the next lost the Enter after user.email, leaving the field
unsubmitted. Every later expect then waited out its own timeout and the whole
thing surfaced at the 600s ceiling as an unattributed "a prompt went
unanswered".
Two fixes:
- each answer now waits for \033[?2004h, bracketed-paste-on, which the TUI
emits only AFTER raw mode is established. "Probably ready" becomes
"demonstrably ready", and each prompt emits its own, so it is per-answer.
- a bare `expect -re {pat} {...}` treats timeout as "carry on", which is what
turned one lost keystroke into a ten-minute mystery. Prompts now fail
immediately naming which one was missed, and distinguish EOF (dotup exited
early) from timeout.
Also moved red(), the redactor, above the run. It was defined below the new
early-abort path that calls it, so the one branch that most needs redaction
would have hit an undefined function.
Verified by running it twice end to end against a mock endpoint, both passing
identically, including the three assertions the argv fix exists for.
README: the suite is 101 assertions, not 81, and the end-to-end is documented.
This commit is contained in:
@@ -0,0 +1,48 @@
|
||||
# A stand-in for the Caddy bootstrap endpoint, for testing ops/run-e2e.sh
|
||||
# without the operator's password.
|
||||
#
|
||||
# It reproduces the three behaviours the client depends on:
|
||||
# correct credentials -> 200 with the blob
|
||||
# wrong credentials -> 401
|
||||
# any other path -> connection closed with no response (Caddy's `abort`)
|
||||
#
|
||||
# The blob is composed IN MEMORY from credentials already present on this
|
||||
# machine. Nothing is written to disk and nothing is ever logged.
|
||||
import base64, os, http.server
|
||||
|
||||
USER = os.environ['MU']
|
||||
PW = os.environ['MP']
|
||||
ROUTE = os.environ['MR']
|
||||
|
||||
with open(os.path.expanduser('~/.config/bitwarden/bws-token')) as f:
|
||||
bws = f.read().strip()
|
||||
BLOB = ('PRIVATE_REPO_URL=%s\nBWS_ACCESS_TOKEN=%s\n' % (os.environ['MREPO'], bws)).encode()
|
||||
WANT = 'Basic ' + base64.b64encode(('%s:%s' % (USER, PW)).encode()).decode()
|
||||
|
||||
class H(http.server.BaseHTTPRequestHandler):
|
||||
protocol_version = 'HTTP/1.0'
|
||||
|
||||
def do_GET(self):
|
||||
if self.path != '/%s/bootstrap.env' % ROUTE:
|
||||
# Caddy's catch-all is `handle { abort }` -- no response at all, so
|
||||
# curl reports 000. Closing without writing reproduces that.
|
||||
self.close_connection = True
|
||||
return
|
||||
if self.headers.get('Authorization', '') != WANT:
|
||||
self.send_response(401)
|
||||
self.send_header('WWW-Authenticate', 'Basic realm="bootstrap"')
|
||||
self.send_header('Content-Length', '0')
|
||||
self.end_headers()
|
||||
return
|
||||
self.send_response(200)
|
||||
self.send_header('Content-Type', 'text/plain')
|
||||
self.send_header('Content-Length', str(len(BLOB)))
|
||||
self.end_headers()
|
||||
self.wfile.write(BLOB)
|
||||
|
||||
# the default handler logs every request line to stderr; the path contains
|
||||
# the route, so it stays quiet
|
||||
def log_message(self, *a):
|
||||
pass
|
||||
|
||||
http.server.HTTPServer(('0.0.0.0', 8099), H).serve_forever()
|
||||
Reference in New Issue
Block a user