[test-improver] Improve tests for mcptest harness#2631
Merged
lpcox merged 1 commit intoMar 27, 2026
Conversation
- Replace if-block manual checks with require.NoError, require.Len,
assert.Equal, assert.False, assert.ElementsMatch
- Remove redundant 'if len > 0' guards (now safe after require.Len)
- Remove t.Logf status log messages (not assertion output)
- Add missing assertion on add tool return value ("8")
- Use assert.ElementsMatch for unordered tool name comparison
- Add assert import alongside existing require import
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This was referenced Mar 27, 2026
lpcox
added a commit
that referenced
this pull request
Mar 27, 2026
…ctionPool.Get (#2634) 🤖 *This is an automated pull request from [Repo Assist](https://github.com/github/gh-aw-mcpg/actions/runs/23636580547), an AI assistant.* ## Summary `SessionConnectionPool.Get` used an unsafe read-lock upgrade pattern that introduced a data race: ``` RLock → read metadata → RUnlock → Lock → write metadata → Unlock → RLock ``` Between the manual `RUnlock` and subsequent `Lock`, another goroutine could call `Delete` on the same key. If that happened, `Get` would update and return a connection that had already been removed from the pool (and potentially closed), violating the caller's assumption that the returned connection is live. The Go race detector (`go test -race`) would flag this as a data race on `metadata.LastUsedAt`, `metadata.RequestCount`, and `metadata.State`. ## Root cause The original code tried to optimise by using a read lock for the initial map lookup, upgrading to a write lock only for the three metadata field writes. This optimisation is **not safe** for `sync.RWMutex` because Go's `RWMutex` does not support lock upgrading — you must release the read lock before acquiring the write lock, creating an unavoidable race window. ## Fix Replace the entire operation with a single write lock (`p.mu.Lock()` / `defer p.mu.Unlock()`). This is the standard, correct pattern for read-then-update operations. Since every successful `Get` also performs three writes to the metadata struct, the read-lock optimisation provided minimal benefit and the lock contention difference is negligible. ## Changes - `internal/launcher/connection_pool.go`: `Get` now uses `p.mu.Lock()` / `defer p.mu.Unlock()` throughout, removing the manual lock upgrade. - `internal/launcher/connection_pool_test.go`: Added `TestConnectionPoolConcurrencyWithDeletes` which runs concurrent `Get`, `Set`, and `Delete` operations to exercise the previously racy path. Run with `go test -race` to confirm correctness. ## Trade-offs The write lock means concurrent reads from different sessions are serialised during `Get`. In practice this is unlikely to be a bottleneck — the lock is held only for a map lookup and three field writes (nanoseconds), and the actual MCP backend communication happens outside the lock. ## Test Status >⚠️ Infrastructure note: the workflow environment cannot download the Go 1.25.0 toolchain (network access to `proxy.golang.org` is blocked). Tests could not be executed locally. The code change is mechanically correct (removing the manual lock dance, keeping identical logic), and the existing test suite is expected to pass in CI where the toolchain is available. Closes N/A (proactive bug fix — no linked issue) > [!WARNING] > <details> > <summary><strong>⚠️ Firewall blocked 1 domain</strong></summary> > > The following domain was blocked by the firewall during workflow execution: > > - `proxy.golang.org` > > To allow these domains, add them to the `network.allowed` list in your workflow frontmatter: > > ```yaml > network: > allowed: > - defaults > - "proxy.golang.org" > ``` > > See [Network Configuration](https://github.github.com/gh-aw/reference/network/) for more information. > > </details> > [!NOTE] > <details> > <summary><b>🔒 Integrity filter blocked 14 items</b></summary> > > The following items were blocked because they don't meet the GitHub integrity level. > > - #1711 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2568 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2626 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2628 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2629 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2630 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2632 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - #2633 `list_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - [#2631](#2631) `list_pull_requests`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - [#2577](#2577) `list_pull_requests`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - [#2567](#2567) `list_pull_requests`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - [#1711](#1711) `search_issues`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - [#2278](#2278) `issue_read`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > - search_pull_requests `search_pull_requests`: has lower integrity than agent requires. The agent cannot read data with integrity below "merged". > > To allow these resources, lower `min-integrity` in your GitHub frontmatter: > > ```yaml > tools: > github: > min-integrity: approved # merged | approved | unapproved | none > ``` > > </details> > Generated by [Repo Assist](https://github.com/github/gh-aw-mcpg/actions/runs/23636580547) · [◷](https://github.com/search?q=repo%3Agithub%2Fgh-aw-mcpg+%22gh-aw-workflow-id%3A+repo-assist%22&type=pullrequests) > > To install this [agentic workflow](https://github.com/githubnext/agentics/tree/851905c06e905bf362a9f6cc54f912e3df747d55/workflows/repo-assist.md), run > ``` > gh aw add githubnext/agentics@851905c > ``` <!-- gh-aw-agentic-workflow: Repo Assist, engine: copilot, model: auto, id: 23636580547, workflow_id: repo-assist, run: https://github.com/github/gh-aw-mcpg/actions/runs/23636580547 --> <!-- gh-aw-workflow-id: repo-assist -->
Contributor
There was a problem hiding this comment.
Pull request overview
This PR refactors the internal/testutil/mcptest harness tests to reduce boilerplate and make failures clearer by standardizing on testify assertions.
Changes:
- Replaced manual
if/t.Errorf/t.Fatalfchecks withrequire/asserthelpers. - Added additional result validation (notably for the
addtool) and safer length checks before indexing. - Simplified test flow by removing redundant guards and logging noise.
Comments suppressed due to low confidence (1)
internal/testutil/mcptest/harness_test.go:120
- Same as above: if the tool returns
IsError=true, prefer arequireassertion so later content/type assertions don’t run and obscure the primary failure.
require.NoError(t, err, "Failed to call add tool")
assert.False(t, result.IsError)
require.Len(t, result.Content, 1, "Expected exactly 1 content item")
textContent, ok := result.Content[0].(*sdk.TextContent)
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| t.Logf("✓ Tool executed correctly: %s", textContent.Text) | ||
| } | ||
| } | ||
| assert.False(t, result.IsError) |
There was a problem hiding this comment.
result.IsError indicates the tool reported an error; using assert.False allows the test to continue and can produce additional, less relevant failures. Use a require assertion here so the test stops immediately when the tool call reports an error.
This issue also appears on line 117 of the same file.
Suggested change
| assert.False(t, result.IsError) | |
| require.False(t, result.IsError) |
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.
Test Improvements:
harness_test.goFile Analyzed
internal/testutil/mcptest/harness_test.gointernal/testutil/mcptestImprovements Made
1. Better Testify Assertions
Replaced all manual
if-block error checking with idiomatic testify calls:require.NoError(t, err)instead ofif err := ...; err != nil { t.Fatalf(...) }require.Len(t, tools, 1)instead ofif len(tools) != 1 { t.Errorf(...) }assert.Equal(t, "test_echo", tools[0].Name)instead of nestedif tools[0].Name != ...assert.False(t, result.IsError)instead ofif result.IsError { t.Error(...) }require.True(t, ok, "Expected *sdk.TextContent")instead ofif !ok { t.Error(...) }assert.ElementsMatch(t, []string{"echo1", "echo2", "add"}, toolNames)instead of manual map-lookup loopassertimport alongside existingrequire2. Increased Coverage
addtool result:assert.Equal(t, "8", textContent.Text)— the original test called the add tool but never verified the return valuerequire.Lenonresult.Contentbefore accessingresult.Content[0], preventing a potential index panic3. Cleaner & More Stable Tests
if len(x) > 0 { ... }guards that were only needed becauset.Errorfdoesn't stop execution (nowrequire.Lenstops on failure)t.Logf("✓ ...")status messages that added noise without testing anythingerr :=declaration pattern across all test functionsWhy These Changes?
harness_test.goalready importedrequirefrom testify but performed most assertions via manualif-blocks witht.Errorf/t.Fatalf. This pattern lets tests continue running after a failure, leading to confusing cascaded errors (e.g., an index-out-of-bounds panic after a length mismatch). Converting to testify stops tests at the right boundary, gives clearer failure messages, and removes substantial boilerplate.The missing assertion on the
addtool result was a genuine coverage gap — the test confirmed the tool call succeeded but never checked the computed value.Generated by Test Improver Workflow
Focuses on better patterns, increased coverage, and more stable tests