Skip to content

MCP-57 Expand on integration tests against SQC - #429

Draft
nquinquenel wants to merge 13 commits into
masterfrom
task/nq/MCP-57-SQC-its
Draft

MCP-57 Expand on integration tests against SQC#429
nquinquenel wants to merge 13 commits into
masterfrom
task/nq/MCP-57-SQC-its

Conversation

@nquinquenel

@nquinquenel nquinquenel commented Jun 3, 2026

Copy link
Copy Markdown
Member

Summary by Gitar

  • Integration testing framework:
    • Added sonarCloudIntegrationTest task and SonarCloudStagingHarness for in-process MCP client testing against real SonarQube Cloud staging.
    • Implemented a shared project provisioning mechanism using a sample-java Maven project to ensure consistent analysis state.
  • Coverage and verification:
    • Added comprehensive tool-specific integration tests (*SonarCloudIT) to validate staging parity for every MCP tool.
    • Updated ProxiedServerITest to reflect increased tool count (34 to 40).
  • CI and infrastructure:
    • Added sonarcloud-integration.yml weekly workflow and updated build.yml to run staging tests in parallel via matrix.
    • Configured failure notifications to include the new SonarQube Cloud integration suite.
  • Fixes:
    • Corrected byte handling in BlockingQueueOutputStream (b & 0xFF) to prevent data corruption.

This will update automatically on new commits.

@hashicorp-vault-sonar-prod

hashicorp-vault-sonar-prod Bot commented Jun 3, 2026

Copy link
Copy Markdown

MCP-57

@nquinquenel
nquinquenel force-pushed the task/nq/MCP-57-SQC-its branch from 627c6b7 to 5e33aed Compare June 4, 2026 07:53
Comment thread .github/workflows/sonarcloud-integration.yml
@nquinquenel
nquinquenel force-pushed the task/nq/MCP-57-SQC-its branch from 5e33aed to af18e4e Compare June 4, 2026 07:59
Comment thread its/README.md
@nquinquenel
nquinquenel force-pushed the task/nq/MCP-57-SQC-its branch from 7cbe289 to 7943665 Compare June 15, 2026 07:26
@nquinquenel
nquinquenel marked this pull request as ready for review June 15, 2026 07:26
Comment thread its/build.gradle.kts Outdated
@nquinquenel
nquinquenel force-pushed the task/nq/MCP-57-SQC-its branch from 20eec0d to 0d9aa42 Compare July 3, 2026 08:50

@damien-urruty-sonarsource damien-urruty-sonarsource left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks quite good to me, I left a couple of remarks/questions

Comment thread .github/workflows/build.yml Outdated

- name: Install Maven
if: matrix.sonarcloud == true
run: sudo apt-get update && sudo apt-get install -y maven

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we use Mise instead?

version: 2026.4.20

- name: Install Maven
run: sudo apt-get update && sudo apt-get install -y maven

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same

Comment thread .github/workflows/build.yml Outdated
Comment on lines +89 to +91
ARTIFACTORY_URL: ${{ steps.secrets-sc.outputs.vault && fromJSON(steps.secrets-sc.outputs.vault).ARTIFACTORY_URL || '' }}
ARTIFACTORY_PRIVATE_USERNAME: ${{ steps.secrets-sc.outputs.vault && fromJSON(steps.secrets-sc.outputs.vault).ARTIFACTORY_USER || '' }}
ARTIFACTORY_PRIVATE_PASSWORD: ${{ steps.secrets-sc.outputs.vault && fromJSON(steps.secrets-sc.outputs.vault).ARTIFACTORY_ACCESS_TOKEN || '' }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This syntax looks different than what we usually have, why do we need the || '' at the end ? We should always expect the variable to be present

Comment thread .github/workflows/build.yml Outdated
Comment on lines +124 to +126
if [ -z "${SONARCLOUD_IT_TOKEN}" ]; then
echo "::warning::SONARCLOUD_IT_TOKEN not available (e.g. fork PR); skipping SonarQube Cloud staging ITS"
exit 0

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should fail harder than that IMO. It's somehow related to my previous comment, I don't know why we fallback to empty string in some cases

EDIT: I see your point about forks, but I think we shouldn't care too much about that

on:
schedule:
# Weekly on Monday at 06:00 UTC
- cron: '0 6 * * 1'

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I assume this is because there is low activity in the project?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I wanted to run the tests on a weekly basis (or maybe daily?) to check any sudden change on SQC - given that we may not always have activity on this repo

.contains("## Context Augmentation")
.contains("All tools loaded: 34 tools after category filtering");
.contains("Advanced analysis mode enabled")
.contains("All tools loaded: 40 tools after category filtering");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you know why we have a different number of tools loaded? It seems strange to have different expected things for a PR that only adds tests

var result = mcpClient.callTool(ProjectStatusTool.TOOL_NAME, Map.of("projectKey", fixture.projectKey()));

var content = structuredContent(result);
assertThat(content.get("status")).isIn("OK", "WARN", "ERROR", "NONE");

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also not very intentional. I think we should check what we expect


assertResultEquals(result, """
{
"webhooks" : [ ]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we have a least one webhook to check the format?

Comment on lines +34 to +35
assertThat(content).containsKey("enterprises");
assertThat(content.get("enterprises")).isInstanceOf(java.util.List.class);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we check the output more precisely?

Comment thread .github/workflows/build.yml Outdated
echo "::warning::SONARCLOUD_IT_TOKEN not available (e.g. fork PR); skipping SonarQube Cloud staging ITS"
exit 0
fi
./gradlew :its:sonarCloudIntegrationTest --info

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe the check for SONARCLOUD_IT_TOKEN should be part of sonarCloudIntegrationTest? So it also fails early and explicitly locally

Comment thread .github/scripts/run-sonarcloud-integration-tests.sh
gitar-bot[bot]

This comment was marked as resolved.

@gitar-bot
gitar-bot Bot dismissed their stale review July 8, 2026 07:36

✅ All code review findings resolved.

Configure merge blocking

@sonarqubecloud

sonarqubecloud Bot commented Jul 8, 2026

Copy link
Copy Markdown

@gitar-bot

gitar-bot Bot commented Jul 8, 2026

Copy link
Copy Markdown
CI failed: The build is failing due to a compilation error in the integration tests module caused by a missing symbol, alongside intermittent failures in existing integration tests.

Overview

The CI build is failing primarily due to a compilation error in the its module, blocking progress. Additionally, there are runtime failures in ProxiedServerITest that appear to be related to test environment timeouts.

Failures

Compilation Error in :its:compileTestJava (confidence: high)

  • Type: build
  • Affected jobs: 85810289562
  • Related to change: yes
  • Root cause: The SearchIssuesTool class is missing the PROJECTS_PROPERTY symbol, which the test code is attempting to access. This suggests a mismatch between the test code and the main source code, likely introduced by recent refactoring.
  • Suggested fix: Verify if PROJECTS_PROPERTY was renamed or removed in SearchIssuesTool. Update the reference in the integration test to use the current correct field name.

Integration Test Timeout in ProxiedServerITest (confidence: high)

  • Type: test
  • Affected jobs: 85819676732
  • Related to change: yes
  • Root cause: The test should_successfully_connect_to_cag_proxied_server_when_given_enough_time fails to connect within the configured time limit.
  • Suggested fix: Investigate if the recent changes introduced resource contention or performance regressions. If the logic is sound, increase the connection timeout threshold to accommodate CI environment latency.

Summary

  • Change-related failures: 2 (Compilation error and test logic timeout).
  • Infrastructure/flaky failures: 0.
  • Recommended action: Resolve the compilation error in SearchIssuesTool first, as this is a hard blocker. Once fixed, re-run the tests to see if the timeout in ProxiedServerITest persists or was a side effect of build instability.
Code Review ✅ Approved 15 resolved / 15 findings

Expands integration test coverage with a new SonarCloud staging harness, addressing 13 findings including storage leaks, thread safety issues, and insecure token handling. The update validates 40 MCP tools and ensures robust analysis workflows.

✅ 15 resolved
Bug: Temp storage dir leaks: non-recursive Files.delete on populated dir

📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingHarness.java:119-125
close() removes the temp storage directory with Files.delete(tempStoragePath), which only deletes an empty directory. STORAGE_PATH is actively populated at runtime by the server (e.g. a logs/ subdirectory with rolling log files, per BackendService). As a result Files.delete throws DirectoryNotEmptyException, which is silently swallowed by the catch (IOException e) { // ignore } block, so every IT run leaks a sonarqube-mcp-sonarcloud-it-* temp directory tree. Use a recursive delete instead.

Bug: newStagingClient overwrites tempStoragePath, leaking earlier dirs

📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingHarness.java:58-63 📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingHarness.java:119-126
tempStoragePath is a single field that is reassigned on every newStagingClient(...) call. If a test creates more than one staging client from the same harness, the directory from the earlier client is no longer referenced and is never cleaned up in close() (only the last one is). Track created storage directories in a list (mirroring clients/servers) and delete all of them on close.

Bug: SonarCloudAnalyzedProject.getOrInitialize is not thread-safe

📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudAnalyzedProject.java:34-40 📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudAnalyzedProject.java:42-49
getOrInitialize() performs an unsynchronized check-then-act on the static fixture field and registers a shutdown hook, while cleanup() synchronizes on LOCK. The two methods use inconsistent locking. If JUnit ever runs test classes in parallel (it currently does not, but this is a latent hazard), two threads could each create a fixture — provisioning/analyzing duplicate staging projects and registering duplicate shutdown hooks. Guard both the read and write in getOrInitialize() with the same LOCK.

Security: SonarCloud token passed as Maven CLI argument

📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingFixture.java:130-142 📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingFixture.java:169-183
analyzeMavenProject passes the staging token via -Dsonar.token=<token> as a process command-line argument. Command-line arguments are visible to other processes on the host (e.g. via ps) and may appear in CI logs/diagnostics. Since redirectErrorStream(true) captures all output and it is printed on failure, and --show-version/--errors are enabled, there is increased risk the token surfaces in logs. Prefer passing the token via an environment variable (SONAR_TOKEN) on the ProcessBuilder environment rather than a -D argument.

Edge Case: bulk_delete query 'q' may match unrelated projects

📄 its/src/test/java/org/sonarsource/sonarqube/mcp/its/sonarcloud/harness/SonarCloudStagingFixture.java:146-151
cleanup() calls api/projects/bulk_delete with q="-" + randomSuffix. bulk_delete's q is a substring/fuzzy match over project keys/names in the org, not an exact key match. Because the suffix is a plain integer, this could match other concurrently-running ITs' projects in the shared sonarlint-it org whose suffix contains the same digits, deleting projects belonging to other runs. Consider deleting by the exact projects key parameter instead of a fuzzy q, e.g. pass projects=projectKey to bulk_delete.

...and 10 more resolved from earlier reviews

Tip

Comment Gitar fix CI or enable auto-apply: gitar auto-apply:on

Options

Auto-apply is off → Gitar will not commit updates to this branch.
Display: compact → Showing less information.

Comment with these commands to change the behavior for this request:

Auto-apply Compact
gitar auto-apply:on         
gitar display:verbose         

Was this helpful? React with 👍 / 👎 | Gitar

@nquinquenel
nquinquenel marked this pull request as draft July 14, 2026 11:08
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants