Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
141 changes: 141 additions & 0 deletions scripts/compare-standalone-to-monorepo.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
#!/bin/sh
# compare-standalone-to-monorepo.sh
#
# Compare POM, Java source, and Java properties files between the
# standalone copilot-sdk-java repo and the monorepo's java/ directory.
#
# Usage:
# ./scripts/compare-standalone-to-monorepo.sh /path/to/standalone /path/to/monorepo [--diff]
#
# The standalone path should point to the root of copilot-sdk-java.
# The monorepo path should point to the root of copilot-sdk (the java/ subdir
# is appended automatically).
#
# Compatible with macOS zsh and POSIX sh/bash.

set -e

# ── Parse arguments ──────────────────────────────────────────────────
SHOW_DIFF=false
STANDALONE=""
MONOREPO=""

for arg in "$@"; do
case "$arg" in
--diff)
SHOW_DIFF=true
;;
*)
if [ -z "$STANDALONE" ]; then
STANDALONE="$arg"
elif [ -z "$MONOREPO" ]; then
MONOREPO="$arg"
else
echo "Error: unexpected argument: $arg" >&2
echo "Usage: $0 <standalone-path> <monorepo-path> [--diff]" >&2
exit 1
fi
;;
esac
done

if [ -z "$STANDALONE" ] || [ -z "$MONOREPO" ]; then
echo "Usage: $0 <standalone-path> <monorepo-path> [--diff]" >&2
exit 1
fi

MONO_JAVA="${MONOREPO}/java"

if [ ! -d "$STANDALONE" ]; then
echo "Error: standalone directory does not exist: $STANDALONE" >&2
exit 1
fi

if [ ! -d "$MONO_JAVA" ]; then
echo "Error: monorepo java directory does not exist: $MONO_JAVA" >&2
exit 1
fi

# ── Collect comparable files from the standalone repo ────────────────
# Excludes: target/, node_modules/, temporary-prompts/, scripts/codegen/
# (these are build artifacts or standalone-only content)
TMPFILE=$(mktemp)
trap 'rm -f "$TMPFILE"' EXIT

(cd "$STANDALONE" && find . -type f \( -name "pom.xml" -o -name "*.java" -o -name "*.properties" \) \
| grep -v '/target/' \
| grep -v '/node_modules/' \
| grep -v '^\./temporary-prompts/' \
| grep -v '^\./scripts/codegen/' \
| sed 's|^\./||' \
| sort) > "$TMPFILE"

# ── Compare ──────────────────────────────────────────────────────────
DIFFER_COUNT=0
MISSING_COUNT=0
SAME_COUNT=0
DIFFER_LIST=""
MISSING_LIST=""

while IFS= read -r relpath; do
standalone_file="${STANDALONE}/${relpath}"
mono_file="${MONO_JAVA}/${relpath}"

if [ ! -f "$mono_file" ]; then
MISSING_COUNT=$((MISSING_COUNT + 1))
MISSING_LIST="${MISSING_LIST}${relpath}
"
elif ! diff -q "$standalone_file" "$mono_file" >/dev/null 2>&1; then
DIFFER_COUNT=$((DIFFER_COUNT + 1))
DIFFER_LIST="${DIFFER_LIST}${relpath}
"
else
SAME_COUNT=$((SAME_COUNT + 1))
fi
done < "$TMPFILE"

TOTAL_COUNT=$(wc -l < "$TMPFILE" | tr -d ' ')

# ── Output ───────────────────────────────────────────────────────────
echo "Compared ${TOTAL_COUNT} files (pom.xml, *.java, *.properties)"
echo " Identical: ${SAME_COUNT}"
echo " Differ: ${DIFFER_COUNT}"
echo " Missing from monorepo: ${MISSING_COUNT}"
echo ""

if [ "$DIFFER_COUNT" -gt 0 ]; then
echo "The following files differ between the standalone and monorepo:"
echo ""
printf '%s' "$DIFFER_LIST" | while IFS= read -r f; do
[ -n "$f" ] && echo " $f"
done
echo ""
fi

if [ "$MISSING_COUNT" -gt 0 ]; then
echo "The following files exist in standalone but NOT in monorepo:"
echo ""
printf '%s' "$MISSING_LIST" | while IFS= read -r f; do
[ -n "$f" ] && echo " $f"
done
echo ""
fi

if [ "$DIFFER_COUNT" -eq 0 ] && [ "$MISSING_COUNT" -eq 0 ]; then
echo "All files are identical."
fi

# ── Optional unified diffs ───────────────────────────────────────────
if [ "$SHOW_DIFF" = true ] && [ "$DIFFER_COUNT" -gt 0 ]; then
echo "================================================================================"
echo "Unified diffs for differing files:"
echo "================================================================================"
printf '%s' "$DIFFER_LIST" | while IFS= read -r f; do
if [ -n "$f" ]; then
echo ""
echo "--- standalone/$f"
echo "+++ monorepo/java/$f"
diff -u "${STANDALONE}/${f}" "${MONO_JAVA}/${f}" || true
fi
done
fi
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;

import org.junit.jupiter.api.Test;

Expand Down Expand Up @@ -135,11 +134,6 @@ private static List<Path> documentationFiles() throws IOException {
List<Path> files = new ArrayList<>();
files.add(root.resolve("README.md"));
files.add(root.resolve("jbang-example.java"));

try (Stream<Path> markdownFiles = Files.walk(root.resolve("src/site/markdown"))) {
markdownFiles.filter(Files::isRegularFile).filter(path -> path.toString().endsWith(".md"))
.forEach(files::add);
}
return files;
Comment thread
edburns marked this conversation as resolved.
}
}
7 changes: 7 additions & 0 deletions src/test/java/com/github/copilot/sdk/E2ETestContext.java
Original file line number Diff line number Diff line change
Expand Up @@ -121,6 +121,13 @@ public Path getWorkDir() {
return workDir;
}

/**
* Gets the repository root for locating shared test assets.
*/
public Path getRepoRoot() {
return repoRoot;
}

/**
* Gets the proxy URL.
*/
Expand Down
60 changes: 39 additions & 21 deletions src/test/java/com/github/copilot/sdk/McpAndAgentsTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

import static org.junit.jupiter.api.Assertions.*;

import java.nio.file.Path;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -17,6 +18,7 @@
import org.junit.jupiter.api.Test;

import com.github.copilot.sdk.generated.AssistantMessageEvent;
import com.github.copilot.sdk.generated.rpc.McpServerStatus;
import com.github.copilot.sdk.json.CustomAgentConfig;
import com.github.copilot.sdk.json.DefaultAgentConfig;
import com.github.copilot.sdk.json.McpServerConfig;
Expand Down Expand Up @@ -51,9 +53,33 @@ static void teardown() throws Exception {
}
}

// Helper method to create an MCP stdio server configuration
private McpStdioServerConfig createLocalMcpServer(String command, List<String> args) {
return new McpStdioServerConfig().setCommand(command).setArgs(args).setTools(List.of("*"));
private Map<String, McpServerConfig> createTestMcpServers(String... serverNames) {
Map<String, McpServerConfig> servers = new HashMap<>();
for (String serverName : serverNames) {
servers.put(serverName, createTestMcpServer());
}
return servers;
}

private McpStdioServerConfig createTestMcpServer() {
Path harnessDir = ctx.getRepoRoot().resolve("test").resolve("harness");
return new McpStdioServerConfig().setCommand("node")
.setArgs(List.of(harnessDir.resolve("test-mcp-server.mjs").toString()))
.setWorkingDirectory(harnessDir.toString()).setTools(List.of("*"));
}

private void waitForMcpServerStatus(CopilotSession session, String serverName, McpServerStatus expectedStatus)
throws Exception {
long deadline = System.nanoTime() + TimeUnit.SECONDS.toNanos(60);
while (System.nanoTime() < deadline) {
var result = session.getRpc().mcp.list().get(5, TimeUnit.SECONDS);
if (result.servers() != null && result.servers().stream()
.anyMatch(server -> serverName.equals(server.name()) && expectedStatus == server.status())) {
return;
}
Thread.sleep(200);
}
fail(serverName + " did not reach " + expectedStatus);
}

// ============ MCP Server Tests ============
Expand All @@ -68,15 +94,15 @@ private McpStdioServerConfig createLocalMcpServer(String command, List<String> a
void testShouldAcceptMcpServerConfigurationOnSessionCreate() throws Exception {
ctx.configureForTest("mcp_and_agents", "should_accept_mcp_server_configuration_on_session_create");

var mcpServers = new HashMap<String, McpServerConfig>();
mcpServers.put("test-server", createLocalMcpServer("echo", List.of("hello")));
var mcpServers = createTestMcpServers("test-server");

try (CopilotClient client = ctx.createClient()) {
CopilotSession session = client.createSession(
new SessionConfig().setMcpServers(mcpServers).setOnPermissionRequest(PermissionHandler.APPROVE_ALL))
.get();

assertNotNull(session.getSessionId());
waitForMcpServerStatus(session, "test-server", McpServerStatus.CONNECTED);

// Simple interaction to verify session works
AssistantMessageEvent response = session.sendAndWait(new MessageOptions().setPrompt("What is 2+2?")).get(60,
Expand Down Expand Up @@ -108,20 +134,13 @@ void testShouldAcceptMcpServerConfigurationOnSessionResume() throws Exception {
session1.sendAndWait(new MessageOptions().setPrompt("What is 1+1?")).get(60, TimeUnit.SECONDS);

// Resume with MCP servers
var mcpServers = new HashMap<String, McpServerConfig>();
mcpServers.put("test-server", createLocalMcpServer("echo", List.of("hello")));
var mcpServers = createTestMcpServers("test-server");

CopilotSession session2 = client.resumeSession(sessionId, new ResumeSessionConfig()
.setMcpServers(mcpServers).setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get();

assertEquals(sessionId, session2.getSessionId());

AssistantMessageEvent response = session2.sendAndWait(new MessageOptions().setPrompt("What is 3+3?"))
.get(60, TimeUnit.SECONDS);

assertNotNull(response);
assertTrue(response.getData().content().contains("6"),
"Response should contain 6: " + response.getData().content());
waitForMcpServerStatus(session2, "test-server", McpServerStatus.CONNECTED);

session2.close();
}
Expand All @@ -139,16 +158,16 @@ void testShouldHandleMultipleMcpServers() throws Exception {
// count
ctx.configureForTest("mcp_and_agents", "should_accept_mcp_server_configuration_on_session_create");

var mcpServers = new HashMap<String, McpServerConfig>();
mcpServers.put("server1", createLocalMcpServer("echo", List.of("server1")));
mcpServers.put("server2", createLocalMcpServer("echo", List.of("server2")));
var mcpServers = createTestMcpServers("server1", "server2");

try (CopilotClient client = ctx.createClient()) {
CopilotSession session = client.createSession(
new SessionConfig().setMcpServers(mcpServers).setOnPermissionRequest(PermissionHandler.APPROVE_ALL))
.get();

assertNotNull(session.getSessionId());
waitForMcpServerStatus(session, "server1", McpServerStatus.CONNECTED);
waitForMcpServerStatus(session, "server2", McpServerStatus.CONNECTED);
session.close();
}
}
Expand Down Expand Up @@ -296,8 +315,7 @@ void testShouldAcceptCustomAgentWithMcpServers() throws Exception {
// Use combined snapshot since this uses both MCP servers and custom agents
ctx.configureForTest("mcp_and_agents", "should_accept_both_mcp_servers_and_custom_agents");

var agentMcpServers = new HashMap<String, McpServerConfig>();
agentMcpServers.put("agent-server", createLocalMcpServer("echo", List.of("agent-mcp")));
var agentMcpServers = createTestMcpServers("agent-server");

List<CustomAgentConfig> customAgents = List.of(new CustomAgentConfig().setName("mcp-agent")
.setDisplayName("MCP Agent").setDescription("An agent with its own MCP servers")
Expand Down Expand Up @@ -350,8 +368,7 @@ void testShouldAcceptMultipleCustomAgents() throws Exception {
void testShouldAcceptBothMcpServersAndCustomAgents() throws Exception {
ctx.configureForTest("mcp_and_agents", "should_accept_both_mcp_servers_and_custom_agents");

var mcpServers = new HashMap<String, McpServerConfig>();
mcpServers.put("shared-server", createLocalMcpServer("echo", List.of("shared")));
var mcpServers = createTestMcpServers("shared-server");

List<CustomAgentConfig> customAgents = List.of(new CustomAgentConfig().setName("combined-agent")
.setDisplayName("Combined Agent").setDescription("An agent using shared MCP servers")
Expand All @@ -362,6 +379,7 @@ void testShouldAcceptBothMcpServersAndCustomAgents() throws Exception {
.setCustomAgents(customAgents).setOnPermissionRequest(PermissionHandler.APPROVE_ALL)).get();

assertNotNull(session.getSessionId());
waitForMcpServerStatus(session, "shared-server", McpServerStatus.CONNECTED);

AssistantMessageEvent response = session.sendAndWait(new MessageOptions().setPrompt("What is 7+7?")).get(60,
TimeUnit.SECONDS);
Expand Down
Loading