Repository navigation
test: hold every edit mutate makes without docker - #40
Merged
Merged
Conversation
With Docker unreachable no unit test pinned the summaries, remediations, boundaries and builder actions of the doctor checks, so mutate left 41 edits standing. The new tests assert the exact Result and Action each probe yields through fake runners, temp dirs and HOME. The probe-context setup moves into writeProbeContext so its write failure needs no unobservable branch, keeping both messages. Writes of StatusOK, Result's zero value, are exempt. Co-Authored-By: Claude Opus 5.5 <[email protected]>
No unit test checked the 0755 directory mode, that a header without a mode keeps os.Create's default, or that a regular file naming destDir itself fails, so mutate's edits to those lines survived. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Nothing checked that NODE_EXTRA_CA_CERTS is added to the system roots rather than replacing them, nor that HTTP/2 negotiation survives. Tests now install generated system roots in TestMain and probe the GODEBUG=http2client=0 case, where the transport has no TLS config, in a child process. The TLS config is now cloned and assigned in one place so the nil case leaves no field write only that child can observe. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The reference CLI asks credsStore only when the docker config names no credential helper for the registry (getCredentialFromDockerConfigOr- CredentialHelper in httpOCIRegistry.ts): a registry's own helper that finds nothing goes straight to the auths entry, and an empty helper name counts as no helper. Go asked credsStore after a failed registry helper and ran "docker-credential-" for an empty name. The new tests also hold, without Docker, the platform default helper choice (pass on PATH or secretservice), the configContainsAuth rule that skips it, and what each helper outcome logs: until now only Docker-backed e2e tests refused those mutate edits. Co-Authored-By: Claude Opus 5.5 <[email protected]>
A mutate run with Docker unreachable, as on CI, left edits in the OCI auth policy, ref parsing, the client's response-header timeout and the CLI's policy context that only Docker-backed e2e tests refused. Unit tests now hold them against the reference: diagnostics written to the policy's logger, URL.origin port and Docker Hub rules, authority validation, the 300-399 token redirect range, 403 challenges, relative realms, digest rejections, fetch by digest, and a context holding the policy keeping its cancellation. Edits nobody can observe are simplified away instead: the userinfo/path/ query/fragment checks the Host comparison already covers, the unreachable ::1 host, the resp == nil a conforming transport never returns, and index arithmetic strings.Cut replaces. ParseRef now splits a digest on every colon as TS does, so sha256:a:b is a format error. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…rence TS useNewProjectName and the additional build contexts check compare the parsed Compose version with isEarlierVersion, a missing component counting as 0. The Go gates treated a one-component version as unknown, so "1" allowed new project names and "3" refused additional contexts. Both now use isEarlierVersion, and parseComposeVersion keeps every component like TS parseVersion (the cap at three could not change any comparison). Down's error paths and the ToProjectName drop value are now held or exempted, so mutate refuses every edit in compose.go without Docker. Co-Authored-By: Claude Opus 5.5 <[email protected]>
…ocker With Docker unreachable, as on CI, only the e2e tests refused these edits. Unit tests now hold them through the mock API, the HTTP transport fake, temporary context stores and the BuildKit parser: the endpoint NewEngineClient dials, the seven removal tries (under synctest), image labels without a Config, the pull platform, the continued-FROM and label-insertion paths of EnsureFinalStageName, the escape directive and named-stage labels. Edits nobody can observe are simplified (the Stages preallocation, the Keys loop, the redundant parse-error guard) or exempted (exit codes and offsets returned beside a failure). Co-Authored-By: Claude Opus 5.5 <[email protected]>
Nothing held New's stderr, start-time and info-level defaults, the banner's kernel release, the elapsed and stop durations, or the single trailing newline, so mutate left those edits standing without Docker. The secret-masking guard is dropped: masking with no secrets or on empty text already changes nothing. Co-Authored-By: Claude Opus 5.5 <[email protected]>
parseBytes parsed the count as an int64: a count past its range was 0
and a large unit count wrapped ("10000000000tb" became 856809829107236864),
where TS parseInt and `${n}` give 1.099511627776e+22. It now works in
float64 and writes the number as JS does. safeID wrote one "_" for a
character outside the BMP; the TS regex sees its two UTF-16 code units
and writes "__".
The new tests also hold the parseBytes units and the regex it mirrors,
the forwardPorts that stay strings, and the warning an empty metadata
label does not get, which only Docker-backed tests refused before.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
TS writeLockfile writes JSON.stringify(lockfile, null, 2) with a trailing newline and compares the old lockfile through JSON.parse and JSON.stringify. Go wrote it without the newline and with "&" escaped, and compared raw bytes, so a lockfile TS wrote always failed --frozen-lockfile here and was rewritten otherwise, and a cosmetic difference counted as a change. Co-Authored-By: Claude Opus 5.5 <[email protected]>
TS sanitizeControlManifest keeps each entry whose fields are strings. Go dropped entries with an empty field, so an advisory without a description was never reported, and one mistyped entry made the whole list unparsable and dropped its valid siblings. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Only Docker-backed tests refused these edits. The tests hold the soft-dependency match and the tie-breaks of TS compareTo for every source kind, the unresolved nodes of a hand-built graph, one-character ids and the single-alias diagram label. The compareNodes Set guard and the GitHub-shorthand tag split change nothing a caller sees and are gone, aliasesFor no longer sizes its slice, and boolCompare's magnitude is exempt as only its sign is read. Co-Authored-By: Claude Opus 5.5 <[email protected]>
TS installDotfiles turns a repository into a github.com URL only when it
has no colon and does not match /^\.{0,2}\//, so an absolute path is
cloned as written. The port tested for ./ and ../ alone and turned
/srv/dots into https://github.com//srv/dots.git.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
initializeCommand's stdout went straight to the CLI's stdout, ahead of up's JSON result, and its stderr to the CLI's stderr outside the log. TS runInitializeCommand writes every chunk of the command's output to the log with output.raw at info level; do the same, so stdout holds only the result and --log-format json carries the output as raw events. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without Docker no unit test refused edits to BuildContext, the union
types' MarshalJSON, the worktree and git-root walks, the empty ${} tag,
containerEnv's Windows case folding, templateOption's one-name rule and
the host platform. Test each against the TS reference. Drop the
strings.Builder capacity hint in substituteTags, which changes no
result.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without Docker no unit test refused edits to HookError's message, the shell server's -1 exit code on a failed exec, the hook output logging, the sbin PATH filter, the env cache read and parseEnvOutput's continuation lines. Test each through the package's fake executors. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without Docker no unit test refused raising the one-path limit of stop, down, open, generate-docs and features test, the boundary of the terminal-columns/rows implications, or prepareDockerfileBuild's cleanup. Test them; exempt the written Dockerfile's mode, which only the build this process starts reads, and drop cacheFolder's empty-username check, since TS builds devcontainercli-<username> whatever the name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Co-Authored-By: Claude Opus 5.5 <[email protected]>
An object hook runs its commands in parallel, so the race detector flagged the unsynchronized record in TestRunHooksErrorNamesTheFailedCommand. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Four hand-written CommandExecutor doubles recorded commands in slices and the tests read them back; one of them raced once an object hook ran its commands in parallel. MockCommandExecutor (go.uber.org/mock, generated with `go generate ./internal/lifecycle`) states the commands each test expects instead, in order where the lifecycle runs them in order, with no state shared between goroutines. Tests that counted commands now name them. Co-Authored-By: Claude Opus 5.5 <[email protected]>
The whole-repository mutation run without Docker left one edit standing: `reg == "" || seen[reg]` read as `&&`. A DEVCONTAINERS_OCI_AUTH entry with an empty host matches an empty registry, so without the skip the build's auth config gains a "" entry. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Without BuildKit both CLIs build Features from the fixed tag dev_container_feature_content_temp, so two such cases at once can take each other's Features. On the docker8 fleet the TS side failed that way twice (an up in setup_cmd failing silently, then exec or verify finding no container). A case can now name a lock; cases with the same lock run one at a time and stay parallel with the rest. The 24 cases that build Features without BuildKit hold feature-content-image, and TestParityMatrixLocksTheFeatureContentImage keeps that list exact. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
#39 made every edit
go tool mutatemakes across the repository refused, but it verified with Docker available, so some of those refusals came from Docker-backed e2e tests. CI's mutate job has no reachable Docker. A whole-repository run with Docker unreachable left 227 edits no unit test refused, most in library code no pull request had touched (doctor, the OCI auth policy, the Dockerfile parser, the metadata merge). This PR answers each one, so the repository holds againstmutatewith unit tests alone, as CI checks it.Bugs fixed (each with a regression test that fails before the fix)
&escaped, and compared bytes. A lockfile written by the reference CLI therefore failed--frozen-lockfilehere, and without the flag was rewritten. It is now written asJSON.stringify(lockfile, null, 2) + '\n'and the old one is normalized before comparing; lockfiles Go wrote before still compare equal.up's JSON result: stdout/stderr separation is a public contract. It now goes to the log, as TS'soutput.rawdoes.credsStore, and randocker-credential-for an empty helper name; TS consultscredsStoreonly when the registry has no helper.2,3) was treated as unknown for project names andadditional_contexts; TS counts a missing component as 0.https://github.com//srv/dots.git; TS clones it as written.10000000000tb); they are computed in float64 and printed as JS does._for a character outside the BMP; TS writes two.Test doubles
The race detector caught
TestRunHooksErrorNamesTheFailedCommandwriting to a hand-written executor's slice from the parallel commands of an object hook. The four hand-writtenCommandExecutordoubles ininternal/lifecycleare replaced byMockCommandExecutor(go.uber.org/mock v0.6.0, generated withgo generate ./internal/lifecycle). Tests now state the commands they expect, in order where the lifecycle runs them in order, instead of reading back a shared record; tests that counted commands now name them. gomock serializes its own bookkeeping, but the actions (Do,DoAndReturn) run outside its lock, so tests keep state out of them where commands run in parallel.Left for follow-ups (found, not changed here)
EnsureFinalStageNameis tolerant where TS throws ("Dockerfile contains no FROM instructions").\nwhere TS writes\r\n, andStoplines show at debug where TS shows them at trace.${templateOption:...}stays as written; TS substitutes''.--terminal-columns/--terminal-rowsimplications check the value, not the flag's presence.normalizeHTTPSAuthorityrejects forms WHATWG URL accepts (a/,a?) and keeps:443.pfs.ExtractTarGz's traversal guard never fires (entries are re-rooted inside the destination, which is safe).Validation
go tool mutateper group over whole files, Docker unreachablego test ./cmd/... ./internal/...(hermetic)task linttask parity:contractagainst the reference oraclemutate, Docker unreachableResolveBuildAuth's empty-registry skip, is held byTestResolveBuildAuthSkipsAnEmptyRegistrygo test -race ./...(Docker unreachable)task vuln(new dependency)parity-runtime (1)failure on one runup.compose-image-with-features-omit-config-remote-env-from-metadata-success; the same job passed on rerun🤖 Generated with Claude Code