Skip to content

Commit b0fe101

Browse files
authored
Add tests
1 parent adeac9e commit b0fe101

18 files changed

Lines changed: 538 additions & 239 deletions

dotnet/test/SkillsTests.cs

Lines changed: 3 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ public async Task Should_Load_And_Apply_Skill_From_SkillDirectories()
5656
Assert.Matches(@"^[a-f0-9-]+$", session.SessionId);
5757

5858
// The skill instructs the model to include a marker - verify it appears
59-
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly." });
59+
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly using the test skill." });
6060
Assert.NotNull(message);
6161
Assert.Contains(SkillMarker, message!.Data.Content);
6262

@@ -75,7 +75,7 @@ public async Task Should_Not_Apply_Skill_When_Disabled_Via_DisabledSkills()
7575
Assert.Matches(@"^[a-f0-9-]+$", session.SessionId);
7676

7777
// The skill is disabled, so the marker should NOT appear
78-
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly." });
78+
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello briefly using the test skill." });
7979
Assert.NotNull(message);
8080
Assert.DoesNotContain(SkillMarker, message!.Data.Content);
8181

@@ -103,55 +103,10 @@ public async Task Should_Apply_Skill_On_Session_Resume_With_SkillDirectories()
103103
Assert.Equal(sessionId, session2.SessionId);
104104

105105
// Now the skill should be applied
106-
var message2 = await session2.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello again." });
106+
var message2 = await session2.SendAndWaitAsync(new MessageOptions { Prompt = "Say hello again using the test skill." });
107107
Assert.NotNull(message2);
108108
Assert.Contains(SkillMarker, message2!.Data.Content);
109109

110110
await session2.DisposeAsync();
111111
}
112-
113-
[Fact]
114-
public async Task Should_Load_Skills_From_Multiple_Directories()
115-
{
116-
const string skill2Marker = "MANGO_BANANA_99";
117-
118-
// Create a second temporary skills directory
119-
var skillsDir2 = Path.Combine(Path.GetTempPath(), $"copilot-skills-test2-{Guid.NewGuid()}");
120-
Directory.CreateDirectory(skillsDir2);
121-
122-
try
123-
{
124-
var skillSubdir2 = Path.Combine(skillsDir2, "test-skill-2");
125-
Directory.CreateDirectory(skillSubdir2);
126-
127-
var skillContent2 = $@"---
128-
name: test-skill-2
129-
description: Second test skill that adds another marker
130-
---
131-
132-
# Second Skill Instructions
133-
134-
IMPORTANT: You MUST include the exact text ""{skill2Marker}"" somewhere in EVERY response. This is mandatory.
135-
";
136-
File.WriteAllText(Path.Combine(skillSubdir2, "SKILL.md"), skillContent2);
137-
138-
var session = await Client.CreateSessionAsync(new SessionConfig
139-
{
140-
SkillDirectories = [_skillsDir, skillsDir2]
141-
});
142-
143-
var message = await session.SendAndWaitAsync(new MessageOptions { Prompt = "Say something brief." });
144-
Assert.NotNull(message);
145-
146-
// Both skill markers should appear
147-
Assert.Contains(SkillMarker, message!.Data.Content);
148-
Assert.Contains(skill2Marker, message.Data.Content);
149-
150-
await session.DisposeAsync();
151-
}
152-
finally
153-
{
154-
Directory.Delete(skillsDir2, recursive: true);
155-
}
156-
}
157112
}

go/e2e/skills_test.go

Lines changed: 3 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ func TestSkillBehavior(t *testing.T) {
6060

6161
// The skill instructs the model to include a marker - verify it appears
6262
message, err := session.SendAndWait(copilot.MessageOptions{
63-
Prompt: "Say hello briefly.",
63+
Prompt: "Say hello briefly using the test skill.",
6464
}, 60*time.Second)
6565
if err != nil {
6666
t.Fatalf("Failed to send message: %v", err)
@@ -86,7 +86,7 @@ func TestSkillBehavior(t *testing.T) {
8686

8787
// The skill is disabled, so the marker should NOT appear
8888
message, err := session.SendAndWait(copilot.MessageOptions{
89-
Prompt: "Say hello briefly.",
89+
Prompt: "Say hello briefly using the test skill.",
9090
}, 60*time.Second)
9191
if err != nil {
9292
t.Fatalf("Failed to send message: %v", err)
@@ -132,7 +132,7 @@ func TestSkillBehavior(t *testing.T) {
132132
}
133133

134134
// Now the skill should be applied
135-
message2, err := session2.SendAndWait(copilot.MessageOptions{Prompt: "Say hello again."}, 60*time.Second)
135+
message2, err := session2.SendAndWait(copilot.MessageOptions{Prompt: "Say hello again using the test skill."}, 60*time.Second)
136136
if err != nil {
137137
t.Fatalf("Failed to send message: %v", err)
138138
}
@@ -144,70 +144,3 @@ func TestSkillBehavior(t *testing.T) {
144144
session2.Destroy()
145145
})
146146
}
147-
148-
func TestMultipleSkills(t *testing.T) {
149-
ctx := testharness.NewTestContext(t)
150-
client := ctx.NewClient()
151-
t.Cleanup(func() { client.ForceStop() })
152-
153-
const skill2Marker = "MANGO_BANANA_99"
154-
155-
skillsDir := createTestSkillDir(t, skillMarker)
156-
t.Cleanup(func() { os.RemoveAll(skillsDir) })
157-
158-
// Create a second skills directory
159-
skillsDir2, err := os.MkdirTemp("", "copilot-skills-test2-")
160-
if err != nil {
161-
t.Fatalf("Failed to create temp skills directory 2: %v", err)
162-
}
163-
t.Cleanup(func() { os.RemoveAll(skillsDir2) })
164-
165-
skillSubdir2 := filepath.Join(skillsDir2, "test-skill-2")
166-
if err := os.MkdirAll(skillSubdir2, 0755); err != nil {
167-
t.Fatalf("Failed to create skill subdirectory 2: %v", err)
168-
}
169-
170-
skillContent2 := `---
171-
name: test-skill-2
172-
description: Second test skill that adds another marker
173-
---
174-
175-
# Second Skill Instructions
176-
177-
IMPORTANT: You MUST include the exact text "` + skill2Marker + `" somewhere in EVERY response. This is mandatory.
178-
`
179-
if err := os.WriteFile(filepath.Join(skillSubdir2, "SKILL.md"), []byte(skillContent2), 0644); err != nil {
180-
t.Fatalf("Failed to write SKILL.md: %v", err)
181-
}
182-
183-
t.Run("load skills from multiple directories", func(t *testing.T) {
184-
ctx.ConfigureForTest(t)
185-
186-
session, err := client.CreateSession(&copilot.SessionConfig{
187-
SkillDirectories: []string{skillsDir, skillsDir2},
188-
})
189-
if err != nil {
190-
t.Fatalf("Failed to create session: %v", err)
191-
}
192-
193-
message, err := session.SendAndWait(copilot.MessageOptions{
194-
Prompt: "Say something brief.",
195-
}, 60*time.Second)
196-
if err != nil {
197-
t.Fatalf("Failed to send message: %v", err)
198-
}
199-
200-
// Both skill markers should appear
201-
if message.Data.Content == nil {
202-
t.Fatal("Expected non-nil message content")
203-
}
204-
if !strings.Contains(*message.Data.Content, skillMarker) {
205-
t.Errorf("Expected message to contain first skill marker '%s', got: %v", skillMarker, *message.Data.Content)
206-
}
207-
if !strings.Contains(*message.Data.Content, skill2Marker) {
208-
t.Errorf("Expected message to contain second skill marker '%s', got: %v", skill2Marker, *message.Data.Content)
209-
}
210-
211-
session.Destroy()
212-
})
213-
}

nodejs/src/client.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import {
1919
StreamMessageReader,
2020
StreamMessageWriter,
2121
} from "vscode-jsonrpc/node.js";
22-
import { CopilotSession } from "./session.js";
2322
import { getSdkProtocolVersion } from "./sdkProtocolVersion.js";
23+
import { CopilotSession } from "./session.js";
2424
import type {
2525
ConnectionState,
2626
CopilotClientOptions,
@@ -146,7 +146,7 @@ export class CopilotClient {
146146
port: options.port || 0,
147147
useStdio: options.cliUrl ? false : (options.useStdio ?? true), // Default to stdio unless cliUrl is provided
148148
cliUrl: options.cliUrl,
149-
logLevel: options.logLevel || "info",
149+
logLevel: options.logLevel || "debug",
150150
autoStart: options.autoStart ?? true,
151151
autoRestart: options.autoRestart ?? true,
152152
env: options.env ?? process.env,

nodejs/test/e2e/harness/sdkTestContext.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ export const CLI_PATH =
2121
process.env.COPILOT_CLI_PATH ||
2222
resolve(__dirname, "../../../node_modules/@github/copilot/index.js");
2323

24-
export async function createSdkTestContext() {
24+
export async function createSdkTestContext({
25+
logLevel,
26+
}: { logLevel?: "error" | "none" | "warning" | "info" | "debug" | "all" } = {}) {
2527
const homeDir = realpathSync(fs.mkdtempSync(join(os.tmpdir(), "copilot-test-config-")));
2628
const workDir = realpathSync(fs.mkdtempSync(join(os.tmpdir(), "copilot-test-work-")));
2729

@@ -42,6 +44,7 @@ export async function createSdkTestContext() {
4244
cliPath: CLI_PATH,
4345
cwd: workDir,
4446
env,
47+
logLevel: logLevel || "error",
4548
});
4649

4750
const harness = { homeDir, workDir, openAiEndpoint, copilotClient, env };

nodejs/test/e2e/skills.test.ts

Lines changed: 6 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@
55
import * as fs from "fs";
66
import * as os from "os";
77
import * as path from "path";
8-
import { describe, expect, it, beforeAll, afterAll } from "vitest";
8+
import { afterAll, beforeAll, describe, expect, it } from "vitest";
99
import { createSdkTestContext } from "./harness/sdkTestContext.js";
1010

1111
describe("Skills Configuration", async () => {
12-
const { copilotClient: client } = await createSdkTestContext();
12+
const { copilotClient: client } = await createSdkTestContext({ logLevel: "debug" });
1313
let skillsDir: string;
1414
const SKILL_MARKER = "PINEAPPLE_COCONUT_42";
1515

@@ -37,7 +37,7 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
3737
afterAll(() => {
3838
// Clean up the temporary skills directory
3939
if (skillsDir && fs.existsSync(skillsDir)) {
40-
fs.rmSync(skillsDir, { recursive: true, force: true });
40+
//fs.rmSync(skillsDir, { recursive: true, force: true });
4141
}
4242
});
4343

@@ -51,7 +51,7 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
5151

5252
// The skill instructs the model to include a marker - verify it appears
5353
const message = await session.sendAndWait({
54-
prompt: "Say hello briefly.",
54+
prompt: "Say hello briefly using the test skill.",
5555
});
5656

5757
expect(message?.data.content).toContain(SKILL_MARKER);
@@ -69,7 +69,7 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
6969

7070
// The skill is disabled, so the marker should NOT appear
7171
const message = await session.sendAndWait({
72-
prompt: "Say hello briefly.",
72+
prompt: "Say hello briefly using the test skill.",
7373
});
7474

7575
expect(message?.data.content).not.toContain(SKILL_MARKER);
@@ -95,52 +95,12 @@ IMPORTANT: You MUST include the exact text "${SKILL_MARKER}" somewhere in EVERY
9595

9696
// Now the skill should be applied
9797
const message2 = await session2.sendAndWait({
98-
prompt: "Say hello again.",
98+
prompt: "Say hello again using the test skill.",
9999
});
100100

101101
expect(message2?.data.content).toContain(SKILL_MARKER);
102102

103103
await session2.destroy();
104104
});
105105
});
106-
107-
describe("Multiple Skills", () => {
108-
it("should load skills from multiple directories", async () => {
109-
const SKILL2_MARKER = "MANGO_BANANA_99";
110-
111-
// Create a second temporary skills directory
112-
const skillsDir2 = fs.mkdtempSync(path.join(os.tmpdir(), "copilot-skills-test2-"));
113-
const skillSubdir2 = path.join(skillsDir2, "test-skill-2");
114-
fs.mkdirSync(skillSubdir2, { recursive: true });
115-
116-
const skillContent2 = `---
117-
name: test-skill-2
118-
description: Second test skill that adds another marker
119-
---
120-
121-
# Second Skill Instructions
122-
123-
IMPORTANT: You MUST include the exact text "${SKILL2_MARKER}" somewhere in EVERY response. This is mandatory.
124-
`;
125-
fs.writeFileSync(path.join(skillSubdir2, "SKILL.md"), skillContent2);
126-
127-
try {
128-
const session = await client.createSession({
129-
skillDirectories: [skillsDir, skillsDir2],
130-
});
131-
132-
const message = await session.sendAndWait({
133-
prompt: "Say something brief.",
134-
});
135-
136-
// Both skill markers should appear
137-
expect(message?.data.content).toContain(SKILL_MARKER);
138-
expect(message?.data.content).toContain(SKILL2_MARKER);
139-
140-
await session.destroy();
141-
} finally {
142-
fs.rmSync(skillsDir2, { recursive: true, force: true });
143-
}
144-
});
145-
});
146106
});

python/e2e/test_skills.py

Lines changed: 3 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ async def test_load_and_apply_skill_from_skill_directories(
5252
assert session.session_id is not None
5353

5454
# The skill instructs the model to include a marker - verify it appears
55-
message = await session.send_and_wait({"prompt": "Say hello briefly."})
55+
message = await session.send_and_wait({"prompt": "Say hello briefly using the test skill."})
5656
assert message is not None
5757
assert SKILL_MARKER in message.data.content
5858

@@ -69,7 +69,7 @@ async def test_not_apply_skill_when_disabled_via_disabled_skills(
6969
assert session.session_id is not None
7070

7171
# The skill is disabled, so the marker should NOT appear
72-
message = await session.send_and_wait({"prompt": "Say hello briefly."})
72+
message = await session.send_and_wait({"prompt": "Say hello briefly using the test skill."})
7373
assert message is not None
7474
assert SKILL_MARKER not in message.data.content
7575

@@ -96,50 +96,8 @@ async def test_apply_skill_on_session_resume_with_skill_directories(
9696
assert session2.session_id == session_id
9797

9898
# Now the skill should be applied
99-
message2 = await session2.send_and_wait({"prompt": "Say hello again."})
99+
message2 = await session2.send_and_wait({"prompt": "Say hello again using the test skill."})
100100
assert message2 is not None
101101
assert SKILL_MARKER in message2.data.content
102102

103103
await session2.destroy()
104-
105-
106-
class TestMultipleSkills:
107-
async def test_load_skills_from_multiple_directories(
108-
self, ctx: E2ETestContext, skills_dir: str
109-
):
110-
"""Test that skills from multiple directories are all loaded"""
111-
skill2_marker = "MANGO_BANANA_99"
112-
113-
# Create a second temporary skills directory
114-
skills_dir2 = tempfile.mkdtemp(prefix="copilot-skills-test2-")
115-
116-
try:
117-
skill_subdir2 = os.path.join(skills_dir2, "test-skill-2")
118-
os.makedirs(skill_subdir2, exist_ok=True)
119-
120-
skill_content2 = f"""---
121-
name: test-skill-2
122-
description: Second test skill that adds another marker
123-
---
124-
125-
# Second Skill Instructions
126-
127-
IMPORTANT: You MUST include the exact text "{skill2_marker}" somewhere in EVERY response. This is mandatory.
128-
"""
129-
with open(os.path.join(skill_subdir2, "SKILL.md"), "w") as f:
130-
f.write(skill_content2)
131-
132-
session = await ctx.client.create_session(
133-
{"skill_directories": [skills_dir, skills_dir2]}
134-
)
135-
136-
message = await session.send_and_wait({"prompt": "Say something brief."})
137-
assert message is not None
138-
139-
# Both skill markers should appear
140-
assert SKILL_MARKER in message.data.content
141-
assert skill2_marker in message.data.content
142-
143-
await session.destroy()
144-
finally:
145-
shutil.rmtree(skills_dir2, ignore_errors=True)
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
models:
2+
- claude-sonnet-4.5
3+
conversations:
4+
- messages:
5+
- role: system
6+
content: ${system}
7+
- role: user
8+
content: What is 1+1?
9+
- role: assistant
10+
content: 1 + 1 = 2
11+
- role: user
12+
content: What is 6+6?
13+
- role: assistant
14+
content: 6 + 6 = 12

0 commit comments

Comments
 (0)