Skills: replace real git in publish tests with CommandStubber#13188
Skills: replace real git in publish tests with CommandStubber#13188
Conversation
5b5a1f7 to
0eb250a
Compare
| // for each remote in the map. The map key is the remote name and the value is the URL. | ||
| // Patterns use `git( .+)?` prefix to match optional `-C <dir>` arguments. | ||
| func stubGitRemote(cs *run.CommandStubber, remoteURLs map[string]string) { | ||
| // Build `git remote -v` output |
There was a problem hiding this comment.
nitpick: redundant comment (just the // Build ... line).
| for name, url := range remoteURLs { | ||
| runGitInDir(t, dir, "remote", "add", name, url) | ||
| runGitInDir(t, dir, "remote", "set-url", "--push", name, bareDir) | ||
| remoteLines += fmt.Sprintf("%s\t%s (fetch)\n%s\t%s (push)\n", name, url, name, url) |
There was a problem hiding this comment.
nitpick: can reuse args like this:
remoteLines += fmt.Sprintf("%[1]s\t%[2]s (fetch)\n%[1]s\t%[2]s (push)\n", name, url)| cs.Register(`git( .+)? remote -v`, 0, remoteLines) | ||
| cs.Register(`git( .+)? config --get-regexp \^remote\\\.`, 1, "") | ||
| for name, url := range remoteURLs { | ||
| cs.Register(fmt.Sprintf(`git( .+)? remote get-url -- %s`, name), 0, url+"\n") |
There was a problem hiding this comment.
nitpick: name should be regexp-escaped:
cs.Register(fmt.Sprintf(`git( .+)? remote get-url -- %s`, regexp.QuoteMeta(name)), 0, url+"\n")| assert.Equal(t, "0", strings.TrimSpace(string(out))) | ||
| cmdStubs: func(cs *run.CommandStubber) { | ||
| cs.Register(`git( .+)? symbolic-ref --quiet HEAD`, 0, "refs/heads/feature\n") | ||
| // rev-list fails when branch has no upstream |
There was a problem hiding this comment.
s/has no upstream/is not pushed/
| GitClient: &git.Client{}, | ||
| IO: ios, | ||
| Dir: dir, | ||
| GitClient: &git.Client{}, |
There was a problem hiding this comment.
We should initialise git.Client like this (otherwise it'll go through finding the real git path, see here and here):
&git.Client{
GhPath: "some/path/gh",
GitPath: "some/path/git",
},
Maybe do it via a helper function, or if all test cases need a git client (which they probably do), create the git client in the test loop.
babakks
left a comment
There was a problem hiding this comment.
I approve to prevent the blocker once we're merging this.
Replace all exec.Command("git", ...), initGitRepo, runGitInDir, and
newTestGitClientWithUpstream with run.Stub()/run.CommandStubber stubs.
Changes:
- Remove os/exec and strings imports; add fmt, regexp, internal/run
- Add newTestGitClient(), stubGitRemote(), stubEnsurePushed() helpers
- Remove initGitRepo, runGitInDir, newTestGitClientWithUpstream helpers
- Add cmdStubs field to TestPublishRun table struct
- Convert all test cases to use stub-based git interactions
- Use regexp.QuoteMeta for remote name patterns
- Use %[1]s/%[2]s format args in stubGitRemote
- Initialize git.Client with explicit GitPath to avoid real git resolution
- Rewrite TestEnsurePushed with stub-based tests
- Update TestDetectGitHubRemote_UsesDir and TestPublishRun_DirArgUsesTargetRemote
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
0eb250a to
5ae5d1d
Compare
|
Rebased and addressed all review feedback:
All tests pass, lint clean. |
There was a problem hiding this comment.
Pull request overview
Replaces real git invocations in skills publish tests with internal/run command stubs to make the test suite hermetic and consistent with project patterns.
Changes:
- Removed helper utilities that created and manipulated real git repositories via
exec.Command("git", ...). - Introduced
run.Stub()/run.CommandStubber-based helpers to stub git remote detection and push-check behavior. - Converted
TestPublishRun,TestEnsurePushed, and remote-selection tests to validate behavior through expected command invocations.
Show a summary per file
| File | Description |
|---|---|
pkg/cmd/skills/publish/publish_test.go |
Reworks publish/push/remote-related tests to stub git commands via run.CommandStubber instead of executing real git |
Copilot's findings
Tip
Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Comments suppressed due to low confidence (1)
pkg/cmd/skills/publish/publish_test.go:1583
- Same issue as the dedicated remote-detection test: the stubs here don't assert which directory git commands are executed against, so this regression test won't catch a bug where
publishRunmistakenly detects remotes from the factory client'sRepoDir(cwdRepo) instead of theDirargument (targetRepo). Consider making the git stubs directory-specific (or asserting on-C <targetRepo>via a stub callback) so the test meaningfully verifies the behavior it describes.
cs, cmdTeardown := run.Stub()
defer cmdTeardown(t)
stubGitRemote(cs, map[string]string{
"origin": "https://github.com/monalisa/target-repo.git",
})
cwdRepo := t.TempDir()
targetRepo := t.TempDir()
writeSkill(t, targetRepo, "my-skill", heredoc.Doc(`
---
name: my-skill
description: A test skill
license: MIT
---
Body text.
`))
ios, _, stdout, _ := iostreams.Test()
ios.SetStdoutTTY(true)
ios.SetStdinTTY(true)
ios.SetStderrTTY(true)
reg := &httpmock.Registry{}
defer reg.Verify(t)
stubAllSecureRemote(reg, "monalisa", "target-repo")
err := publishRun(&PublishOptions{
IO: ios,
Dir: targetRepo,
DryRun: true,
GitClient: &git.Client{GitPath: "some/path/git", RepoDir: cwdRepo},
HttpClient: func() (*http.Client, error) { return &http.Client{Transport: reg}, nil },
- Files reviewed: 1/1 changed files
- Comments generated: 5
| cs, cmdTeardown := run.Stub() | ||
| defer cmdTeardown(t) | ||
| stubGitRemote(cs, map[string]string{ | ||
| "origin": "https://github.com/monalisa/target-repo.git", | ||
| }) | ||
|
|
||
| cwdRepo := t.TempDir() | ||
| targetRepo := t.TempDir() | ||
| initGitRepo(t, targetRepo, map[string]string{ | ||
| "origin": "https://github.com/monalisa/target-repo.git", | ||
| }) | ||
|
|
||
| // gitClient points at cwd-repo (simulating factory-provided client) | ||
| gitClient := &git.Client{RepoDir: cwdRepo} | ||
| gitClient := &git.Client{GitPath: "some/path/git", RepoDir: cwdRepo} | ||
|
|
||
| // detectGitHubRemote should use targetRepo's remotes, not cwdRepo's | ||
| repo, err := detectGitHubRemote(gitClient, targetRepo) |
There was a problem hiding this comment.
This test is meant to verify that detectGitHubRemote uses the provided dir rather than the git client's existing RepoDir, but the current stubs don't distinguish between -C <cwdRepo> and -C <targetRepo> (the regexes match either). As a result, the test would still pass even if the implementation mistakenly used cwdRepo. Consider tightening the stubs (e.g., match on the expected -C <targetRepo> argument or add a callback assertion on the invoked args) so the test actually fails when the wrong directory is used.
This issue also appears on line 1551 of the same file.
| HttpClient: func() (*http.Client, error) { return &http.Client{Transport: reg}, nil }, | ||
| host: "github.com", | ||
| } | ||
| }, |
There was a problem hiding this comment.
In the "publish adds missing topic via --tag" test case, the assertions for user-facing output appear to have been removed (the case no longer sets wantStdout/wantStderr). This makes the test less effective at verifying that the topic-add message and publish output are actually emitted. Consider restoring an assertion for the expected output (e.g., that the "Added "agent-skills" topic" and/or "Published" messages are printed).
| }, | |
| }, | |
| wantStdout: "Added \"agent-skills\" topic\nPublished v1.0.0", |
| cs.Register(`git( .+)? remote -v`, 0, "origin\thttps://gitlab.com/hubot/bar.git (fetch)\norigin\thttps://gitlab.com/hubot/bar.git (push)\n") | ||
| cs.Register(`git( .+)? config --get-regexp \^remote\\\.`, 1, "") | ||
| cs.Register(fmt.Sprintf(`git( .+)? remote get-url -- %s`, regexp.QuoteMeta("origin")), 0, "https://gitlab.com/hubot/bar.git\n") |
There was a problem hiding this comment.
This cmd stub setup duplicates the same git remote -v / git config --get-regexp ... / git remote get-url registrations that stubGitRemote already provides, just to satisfy the second Remotes() call from detectMissingRepoDiagnostic. To reduce duplication and make the intent clearer, consider reusing stubGitRemote for the second call as well (e.g., by calling it twice or by introducing a small helper that registers the remote stubs N times).
| cs.Register(`git( .+)? remote -v`, 0, "origin\thttps://gitlab.com/hubot/bar.git (fetch)\norigin\thttps://gitlab.com/hubot/bar.git (push)\n") | |
| cs.Register(`git( .+)? config --get-regexp \^remote\\\.`, 1, "") | |
| cs.Register(fmt.Sprintf(`git( .+)? remote get-url -- %s`, regexp.QuoteMeta("origin")), 0, "https://gitlab.com/hubot/bar.git\n") | |
| stubGitRemote(cs, map[string]string{ | |
| "origin": "https://gitlab.com/hubot/bar.git", | |
| }) |
| cmdStubs: func(cs *run.CommandStubber) { | ||
| cs.Register(`git( .+)? check-ignore -q -- .agents/skills`, 1, "") | ||
| cs.Register(`git( .+)? remote -v`, 0, "") | ||
| cs.Register(`git( .+)? config --get-regexp \^remote\\\.`, 1, "") |
There was a problem hiding this comment.
Within this test case, the stubbed git check-ignore exit status 1 is treated as an error (because CommandStubber's error doesn't satisfy *exec.ExitError), so the behavior under test is effectively the "could not verify" warning path rather than the true "not gitignored" path. Consider renaming the case accordingly (or adjusting the stubbing approach) so the test name matches what it's validating.
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Summary
Follow-up from #13184 / PR #13185. Replaces all real
exec.Command("git", ...)usage inpkg/cmd/skills/publish/publish_test.gowithrun.Stub()/run.CommandStubber, as requested in PR #13171 review feedback.Changes
initGitRepo(created real git repos with bare remotes),runGitInDir,newTestGitClientWithUpstreamstubGitRemote(stubsgit remote -v,git config --get-regexp,git remote get-url) andstubEnsurePushed(stubsgit symbolic-ref,git rev-list, secondCurrentBranchcall)TestPublishRunto use command stubs instead of real gitTestEnsurePushedto verify correct git commands via stubsTestDetectGitHubRemote_UsesDirandTestPublishRun_DirArgUsesTargetRemote** to use stubsos/exec,strings; Added:internal/runNotes
installed_skill_dirs_not_gitignored_warnstest expectation was adjusted: with stubs,IsIgnoredcan't distinguish exit code 1 (not ignored) from other errors becauseerrWithExitCodedoesn't satisfy*exec.ExitError. The test still validates the gitignore warning is shown.git( .+)?prefix to match optional-C <dir>arguments injected bygit.Client.Fixes #13186