Documentation
¶
Overview ¶
Package webtest is what the tests of internal/webserver and of the packages that leave it share: the harness that serves the API handler under a session and sends it requests, the canned seams and fixture constants, the event stream's reader, a seam call held on its context, the configuration's edits, the Taskwarrior fake and lefthook's fixtures. Each test still drives its code black-box: the server through webserver.Handler, or a package that left through its exported API. Only tests import it.
Index ¶
- Constants
- Variables
- func Asking(ctx context.Context, handler http.Handler, method, target string) <-chan struct{}
- func Decode[T any](t *testing.T, recorder *httptest.ResponseRecorder) T
- func DoneTask() taskwarrior.Task
- func FilledDeps() webserver.Deps
- func FirstSnapshot(t *testing.T, body string) api.Snapshot
- func Get(t *testing.T, handler http.Handler, target string) *httptest.ResponseRecorder
- func Hearing(deps webserver.Deps) (webserver.Deps, *[]error)
- func HookDeps(found []hooks.GitHook, configured bool, written *[]hooks.Generated) webserver.Deps
- func Marshal(t *testing.T, cfg config.Config) string
- func OldHooks() []hooks.GitHook
- func PaceStart() time.Time
- func PlainTask() taskwarrior.Task
- func Presenting(t *testing.T, session edge.Session, handler http.Handler) http.Handler
- func PutConfig(t *testing.T, handler http.Handler, body string) *httptest.ResponseRecorder
- func PutConfigOver(t *testing.T, handler http.Handler, body, etag string) *httptest.ResponseRecorder
- func Rewrite(t *testing.T, path, contents string)
- func Send(t *testing.T, handler http.Handler, method, target, body string) *httptest.ResponseRecorder
- func Serve(t *testing.T, deps webserver.Deps, cfg config.Config) http.Handler
- func ServeForALifetime(t *testing.T, deps webserver.Deps, cfg config.Config) (http.Handler, context.Context, context.CancelFunc)
- func ServeUntilStopped(t *testing.T, deps webserver.Deps) (http.Handler, context.CancelFunc)
- func ServeWith(t *testing.T, deps webserver.Deps, cfg config.Config, info webserver.Info) http.Handler
- func ServeWorld(t *testing.T, world webserver.World, assets fs.FS) http.Handler
- func SessionToken(t *testing.T, session edge.Session) string
- func Snapshots(t *testing.T, body string) []api.Snapshot
- func StartedTask() taskwarrior.Task
- func StreamOnce(t *testing.T, handler http.Handler, target string) *httptest.ResponseRecorder
- func StreamPaced(t *testing.T, deps webserver.Deps, cfg config.Config, step time.Duration) string
- func TaskDay() time.Time
- type FailingFlushWriter
- type HeldCall
- type Message
- type OpenedStream
- type TaskFake
- type UnflushableWriter
Constants ¶
const ( TestKey = "PROJ-412" TestSummary = "Fix token redaction" TestProject = "PROJ" TestReporter = "Ana Lopez" TestBranchName = "fix/PROJ-412" TestAuthor = "octocat" TestVersion = "1.2.3" TestBugJQL = "type = Bug" TestBugView = "Bugs" TestBase = "origin/main" TestChannel = "#dev-workflow" // TestCommitSubject and TestCommitHash are the branch's one commit, shared by // the write-action tests that need a branch with a commit on it. TestCommitSubject = "feat: redact" TestCommitHash = "abc1234" // NoBranchSeam is the shared name for the "no branch seam" case the write // handlers' unavailability tables each exercise. NoBranchSeam = "no branch seam" // NoForgeSeam is its twin for the reads and writes that ask the forge. NoForgeSeam = "no forge seam" // WaitAndTryAgain is what an answer to a service limiting requests says to // do, whichever service it is. WaitAndTryAgain = "wait and try again" // TryAgain is what the internal problem's detail says to do, whichever // request met it. TryAgain = "try again" // RepoPath is where the repository sits on disk, which a git error can // carry and no answer may repeat. RepoPath = "/home/dev/src/acme" // LoopbackHost is the Host the shared request helpers send, so requests pass // the loopback guard the same way a browser on 127.0.0.1 does. A test that // exercises the guard sets its own Host instead. LoopbackHost = "127.0.0.1:13579" )
Fixtures the tests share.
const ( PRTitle = "fix: redact tokens" PRBase = "main" PRURL = "https://x/7" )
The pull request the tests open: its title, the base it targets and its address.
const ( CheckoutPath = "/api/checkout" NoIssueKey = `{"issue_key":""}` )
The writes the contract's tests ask for, and the body that names no issue.
const ( HookSetupAt = "/api/hooks/setup" HooksAt = "/api/hooks" )
HookSetupAt is where the lefthook offer is read and taken, and HooksAt where the hooks lefthook configures are listed.
const ( PreCommitHook = "pre-commit" PrePushHook = "pre-push" CommitMsgHook = "commit-msg" CommitMsgReason = "git hands it the file holding the commit message" )
The hooks the tests' lefthook configures: pre-commit and pre-push, which a run by hand can run, and commit-msg, which only git runs, and why.
const ( SnapshotMessage = "snapshot" SeedMessage = "seed" )
SnapshotMessage and SeedMessage name the stream's two kinds of message.
const ( TasksPath = "/api/tasks" // StartedUUID is the started task's, linked to TestKey; PlainUUID an // unlinked task's; DoneUUID a completed task's linked to another issue; // AddedUUID the task an add creates. StartedUUID = "5f3c9a1e-8b2d-4c6f-9e7a-1d2b3c4d5e6f" PlainUUID = "0a1b2c3d-4e5f-4a6b-8c7d-9e0f1a2b3c4d" DoneUUID = "9d8c7b6a-5f4e-4d3c-8b2a-1f0e9d8c7b6a" AddedUUID = "c0ffee00-1234-4abc-9def-0123456789ab" // OddUUID is the task whose status the spec does not know. OddUUID = "0dd0dd00-aaaa-4bbb-8ccc-ddddeeeeffff" // DataPath is where Taskwarrior keeps its data, and HomePath your home, // which its errors can carry and no answer may repeat. DataPath = "/Users/x/.local/share/task" HomePath = "/Users/x" // TaskContext is the context the fake has active. TaskContext = "work" )
The tasks the Taskwarrior fake holds, where it keeps them, and the path the list is read at.
const EndsWithin = 2 * time.Second
EndsWithin is how long a context the server ends is given to be seen ended: one it never ends is never seen, so the bound only has to outlast a busy machine.
const FilledHead = "abc123"
FilledHead is the head commit of FilledDeps' branch, its one commit.
const HeldPatience = 2 * EndsWithin
HeldPatience is how long a call held until its context ends waits before giving up, so a context that never ends fails its test rather than holding it open: longer than EndsWithin, which the test waits for it.
const ProblemJSON = "application/problem+json"
ProblemJSON is the media type every refusal is written in.
const StreamedFrames = 3
StreamedFrames is how many snapshots the stream tests let a stream push: enough for a read made once to show against one made on every frame.
const TaskwarriorProgram = "/opt/homebrew/bin/task"
TaskwarriorProgram is a taskwarrior.program a user might set.
Variables ¶
var ErrSeam = errors.New("the seam failed")
ErrSeam is what a failing seam returns: an error in none of fault's classes, so the server answers it with the opaque internal 500, whose detail carries none of the error's own text.
Functions ¶
func Asking ¶
Asking sends a request under ctx on its own goroutine, as the HTTP server does, and closes the channel it returns once it has been answered.
func Decode ¶
func Decode[T any](t *testing.T, recorder *httptest.ResponseRecorder) T
Decode unmarshals the recorder's JSON body into T, failing the test on error.
func DoneTask ¶
func DoneTask() taskwarrior.Task
DoneTask is a completed task linked to another issue.
func FilledDeps ¶
FilledDeps is a Deps with every seam populated with canned answers. A test nils a seam to exercise the not-configured path, or replaces one to fail.
func FirstSnapshot ¶
FirstSnapshot is the first snapshot an event-stream body carries.
func HookDeps ¶
HookDeps is FilledDeps with the hooks directory holding found, configured or not, and the configuration written kept in written.
func OldHooks ¶
OldHooks is a repository's own pre-commit hook, which lefthook does not manage: two plain commands.
func PlainTask ¶
func PlainTask() taskwarrior.Task
PlainTask is a pending task linked to no issue, not started, with no dates but the two every task has.
func Presenting ¶
Presenting hands handler each request with session presented as the page presents it, unless the request presents one itself, as a test of the session's check does.
func PutConfig ¶
PutConfig saves body as Settings does: it reads the configuration first, and saves over the revision that read returned.
func PutConfigOver ¶
func PutConfigOver(t *testing.T, handler http.Handler, body, etag string) *httptest.ResponseRecorder
PutConfigOver saves body over the revision etag names, sent as If-Match; an empty etag sends none.
func Rewrite ¶
Rewrite leaves the file at path holding contents, as an edit made outside the browser does.
func Send ¶
func Send(t *testing.T, handler http.Handler, method, target, body string) *httptest.ResponseRecorder
Send sends a request with an optional body and returns the recorder.
func Serve ¶
Serve builds the API handler over deps and cfg, not in dry-run, so the write endpoints are reachable — the common case — with deps' Taskwarrior bound with cfg's settings, as at start. A test that exercises dry-run's read-only guard passes its own Info. Handler fails only when the embedded spec cannot load, which is a build defect, so the test fails there.
func ServeForALifetime ¶
func ServeForALifetime( t *testing.T, deps webserver.Deps, cfg config.Config, ) (http.Handler, context.Context, context.CancelFunc)
ServeForALifetime is Serve over deps and cfg for a lifetime of its own, which webserver.Serve would derive each request's context from, and the function that stops the server, ending it.
func ServeUntilStopped ¶
ServeUntilStopped is Serve, and the function that stops the server it serves from, ending its lifetime.
func ServeWith ¶
func ServeWith(t *testing.T, deps webserver.Deps, cfg config.Config, info webserver.Info) http.Handler
ServeWith is Serve with the caller's Info, for the tests that need a specific stream interval.
func ServeWorld ¶
ServeWorld builds the handler for world with assets mounted, under a session of its own that it presents on each request as the page does. Handler fails only on a spec that cannot load, which is a build defect.
func SessionToken ¶
SessionToken is the session the address the server prints carries, read from its fragment as the page reads it.
func Snapshots ¶
Snapshots decodes every snapshot event's data from an event-stream body, in the order the stream pushed them.
func StartedTask ¶
func StartedTask() taskwarrior.Task
StartedTask is a pending task for TestKey, started, with a due date and a note.
func StreamOnce ¶
StreamOnce runs the events handler with a context that is canceled at once, so it pushes a single snapshot and returns. It returns the recorder.
func StreamPaced ¶
StreamPaced streams StreamedFrames snapshots over cfg, on a clock that starts at PaceStart and that step moves on between two frames, as that much time passing would.
Types ¶
type FailingFlushWriter ¶
type FailingFlushWriter struct {
// contains filtered or unexported fields
}
FailingFlushWriter can flush but fails every write, standing in for a client that has hung up mid-stream.
func (*FailingFlushWriter) Flush ¶
func (f *FailingFlushWriter) Flush()
Flush does nothing; that it exists is what lets the stream start.
func (*FailingFlushWriter) Header ¶
func (f *FailingFlushWriter) Header() http.Header
Header is the response's header, made on first use.
func (*FailingFlushWriter) Write ¶
func (f *FailingFlushWriter) Write([]byte) (int, error)
Write fails with ErrSeam, as a write to a client that hung up does.
func (*FailingFlushWriter) WriteHeader ¶
func (f *FailingFlushWriter) WriteHeader(int)
WriteHeader does nothing.
type HeldCall ¶
type HeldCall struct {
Entered chan struct{}
Release chan struct{}
// contains filtered or unexported fields
}
HeldCall is a seam call held until its context ends, or until it is let go: it says when it is entered, and how its context stood when it returned.
func NewHeld ¶
func NewHeld() *HeldCall
NewHeld is a call held until it is let go, whatever its context does.
func NewHeldUntilEnded ¶
func NewHeldUntilEnded() *HeldCall
NewHeldUntilEnded is a call held until its context ends, or until patience runs out.
func (*HeldCall) EndedWith ¶
EndedWith is how the call's context stood when it returned, or a failure once patience runs out.
type OpenedStream ¶
type OpenedStream struct {
Cancel context.CancelFunc
Done chan struct{}
// contains filtered or unexported fields
}
OpenedStream is the events handler serving a page's connection on a goroutine of its own, as the HTTP server runs it.
func OpenStream ¶
OpenStream connects to target on handler, and disconnects when the test ends if Close has not already.
func OpenStreamOf ¶
OpenStreamOf is OpenStream for request, disconnected by ending a context derived from its own: one derived from the server's lifetime, as webserver.Serve derives each, ends when the server stops too.
func (*OpenedStream) Close ¶
func (s *OpenedStream) Close() string
Close disconnects, waits for the handler to return, and answers what the stream wrote.
type TaskFake ¶
type TaskFake struct {
Install taskwarrior.Install
Pending taskwarrior.List
Linked []taskwarrior.Task
Said string
Fail map[string]error
// contains filtered or unexported fields
}
TaskFake is Taskwarrior as the server sees it: the install, the pending list and the linked tasks it answers, what undo and sync say, and each call made of it, in order. A call whose verb Fail names answers that error instead. The stream calls it from the connection's goroutine, so it is locked.
func FakeTaskwarrior ¶
func FakeTaskwarrior() *TaskFake
FakeTaskwarrior is a Taskwarrior with a sync backend and a context, whose pending list holds a started task for TestKey and an unlinked one, and whose linked tasks are the started one and a completed one.
type UnflushableWriter ¶
type UnflushableWriter struct {
Code int
// contains filtered or unexported fields
}
UnflushableWriter is an http.ResponseWriter that does not support flushing, so the events handler must refuse it.
func (*UnflushableWriter) Header ¶
func (u *UnflushableWriter) Header() http.Header
Header is the response's header, made on first use.
func (*UnflushableWriter) Write ¶
func (u *UnflushableWriter) Write(b []byte) (int, error)
Write accepts b and discards it.
func (*UnflushableWriter) WriteHeader ¶
func (u *UnflushableWriter) WriteHeader(code int)
WriteHeader keeps the status code in Code.