Skip to content
Draft
Show file tree
Hide file tree
Changes from all 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
56 changes: 55 additions & 1 deletion dotnet/src/Generated/Rpc.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

71 changes: 58 additions & 13 deletions dotnet/src/Generated/SessionEvents.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

55 changes: 55 additions & 0 deletions dotnet/test/E2E/SessionTodosChangedE2ETests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
*--------------------------------------------------------------------------------------------*/

using GitHub.Copilot.Rpc;
using GitHub.Copilot.Test.Harness;
using Xunit;
using Xunit.Abstractions;

namespace GitHub.Copilot.Test.E2E;

public class SessionTodosChangedE2ETests(E2ETestFixture fixture, ITestOutputHelper output)
: E2ETestBase(fixture, "session_todos_changed", output)
{
private static readonly string[] ExpectedTodoIds = ["alpha", "beta"];

[Fact]
public async Task Fires_Session_Todos_Changed_And_Exposes_Rows_And_Dependencies()
{
await using var session = await CreateSessionAsync(new SessionConfig
{
OnPermissionRequest = PermissionHandler.ApproveAll,
});

var todosChangedTask = TestHelper.GetNextEventOfTypeAsync<SessionTodosChangedEvent>(
session,
TimeSpan.FromSeconds(30));

await session.SendAndWaitAsync(new MessageOptions
{
Prompt =
"Use the sql tool to execute exactly these statements, in order, with no extra rows:\n" +
"1. INSERT INTO todos (id, title, status) VALUES ('alpha', 'First todo', 'pending');\n" +
"2. INSERT INTO todos (id, title, status) VALUES ('beta', 'Second todo', 'done');\n" +
"3. INSERT INTO todo_deps (todo_id, depends_on) VALUES ('beta', 'alpha');\n" +
"Then stop. Do not insert any other rows or create any other tables.",
});

await todosChangedTask;

var result = await session.Rpc.Plan.ReadSqlTodosWithDependenciesAsync();

var ids = result.Rows
.Select(row => row.Id)
.OfType<string>()
.Order(StringComparer.Ordinal)
.ToArray();

Assert.Equal(ExpectedTodoIds, ids);

Assert.Contains(result.Dependencies, dependency =>
dependency.TodoId == "beta" &&
dependency.DependsOn == "alpha");
}
}
79 changes: 79 additions & 0 deletions go/internal/e2e/session_todos_changed_e2e_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
package e2e

import (
"context"
"slices"
"sort"
"testing"
"time"

copilot "github.com/github/copilot-sdk/go"
"github.com/github/copilot-sdk/go/internal/e2e/testharness"
)

func TestFiresSessionTodosChangedAndExposesRowsAndDependencies(t *testing.T) {
ctx := testharness.NewTestContext(t)
client := ctx.NewClient()
t.Cleanup(func() { client.ForceStop() })

t.Run("fires session.todos_changed and exposes rows and dependencies", func(t *testing.T) {
ctx.ConfigureForTest(t)

session, err := client.CreateSession(t.Context(), &copilot.SessionConfig{
OnPermissionRequest: copilot.PermissionHandler.ApproveAll,
})
if err != nil {
t.Fatalf("Failed to create session: %v", err)
}
defer session.Disconnect()

awaitTodosChanged := waitForMatchingEvent(
session,
copilot.SessionEventType("session.todos_changed"),
func(copilot.SessionEvent) bool { return true },
"session.todos_changed event",
)

sendCtx, cancel := context.WithTimeout(t.Context(), 120*time.Second)
defer cancel()
_, err = session.SendAndWait(sendCtx, copilot.MessageOptions{
Prompt: "Use the sql tool to execute exactly these statements, in order, with no extra rows:\n" +
"1. INSERT INTO todos (id, title, status) VALUES ('alpha', 'First todo', 'pending');\n" +
"2. INSERT INTO todos (id, title, status) VALUES ('beta', 'Second todo', 'done');\n" +
"3. INSERT INTO todo_deps (todo_id, depends_on) VALUES ('beta', 'alpha');\n" +
"Then stop. Do not insert any other rows or create any other tables.",
})
if err != nil {
t.Fatalf("Failed to send message: %v", err)
}

awaitEvent(t, awaitTodosChanged)

result, err := session.RPC.Plan.ReadSqlTodosWithDependencies(t.Context())
if err != nil {
t.Fatalf("Plan.ReadSqlTodosWithDependencies failed: %v", err)
}

var ids []string
for _, row := range result.Rows {
if row.ID != nil && *row.ID != "" {
ids = append(ids, *row.ID)
}
}
sort.Strings(ids)
if !slices.Equal(ids, []string{"alpha", "beta"}) {
t.Fatalf("Expected todo ids [alpha beta], got %v", ids)
}

foundDependency := false
for _, dependency := range result.Dependencies {
if dependency.TodoID == "beta" && dependency.DependsOn == "alpha" {
foundDependency = true
break
}
}
if !foundDependency {
t.Fatalf("Expected dependency beta -> alpha, got %+v", result.Dependencies)
}
})
}
Loading