f99010fae1
CI / lint (push) Failing after 1s
CI / frontend (push) Failing after 1s
CI / scripts (push) Failing after 1s
CI / Go Test (ubuntu-latest) (push) Failing after 0s
CI / frontend-node-25 (push) Failing after 1s
CI / docs (push) Failing after 0s
CI / coverage (push) Failing after 0s
CI / e2e (push) Failing after 0s
Docker / build-and-push (push) Failing after 1s
CI / integration (push) Failing after 4m43s
CI / Go Test (windows-latest) (push) Has been cancelled
CI / Desktop Unit Tests (Windows) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux (arm64)) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Linux) (push) Has been cancelled
Desktop Artifacts / Desktop Build (Windows) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (aarch64)) (push) Has been cancelled
Desktop Artifacts (macOS) / Desktop Build (macOS (x86_64)) (push) Has been cancelled
1072 lines
36 KiB
Go
1072 lines
36 KiB
Go
package parser
|
|
|
|
import (
|
|
"context"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
)
|
|
|
|
func TestVSCodeCopilotProviderSourceMethods(t *testing.T) {
|
|
root := t.TempDir()
|
|
sessionID := "vscode-provider"
|
|
hashDir := filepath.Join(root, "workspaceStorage", "workspace-hash")
|
|
chatDir := filepath.Join(hashDir, "chatSessions")
|
|
jsonPath := filepath.Join(chatDir, sessionID+".json")
|
|
jsonlPath := filepath.Join(chatDir, sessionID+".jsonl")
|
|
writeSourceFile(t, filepath.Join(hashDir, "workspace.json"),
|
|
`{"folder":"file:///Users/alice/code/copilot-app"}`)
|
|
writeSourceFile(t, jsonPath, `{"version":3,"sessionId":"`+sessionID+`","requests":[]}`)
|
|
writeSourceFile(t, jsonlPath, strings.Join([]string{
|
|
`{"kind":0,"v":{"version":3,"sessionId":"` + sessionID + `","creationDate":1770650022790,"requests":[]}}`,
|
|
`{"kind":2,"k":["requests"],"v":[{"requestId":"req1","timestamp":1770650031889,"message":{"text":"Hello VS Code","parts":[]},"response":[{"value":"Hi from VS Code"}],"modelId":"copilot/claude-opus-4.8","result":{"metadata":{"promptTokens":42,"outputTokens":7,"resolvedModel":"claude-opus-4-8"}}}]}`,
|
|
}, "\n")+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCodeCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 2)
|
|
assert.Equal(t, filepath.Join(root, "workspaceStorage"), plan.Roots[0].Path)
|
|
assert.True(t, plan.Roots[0].Recursive)
|
|
assert.Equal(t, filepath.Join(root, "globalStorage"), plan.Roots[1].Path)
|
|
assert.True(t, plan.Roots[1].Recursive)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
assert.Equal(t, jsonlPath, discovered[0].DisplayPath)
|
|
assert.Equal(t, "copilot-app", discovered[0].ProjectHint)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
FullSessionID: "host~vscode-copilot:" + sessionID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, jsonlPath, found.DisplayPath)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: jsonlPath, EventKind: "write", WatchRoot: filepath.Join(root, "workspaceStorage")},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, jsonlPath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), found)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, jsonlPath, fingerprint.Key)
|
|
assert.Positive(t, fingerprint.Size)
|
|
assert.Positive(t, fingerprint.MTimeNS)
|
|
assert.NotEmpty(t, fingerprint.Hash)
|
|
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: found,
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, outcome.ResultSetComplete)
|
|
require.Len(t, outcome.Results, 1)
|
|
require.False(t, outcome.ForceReplace)
|
|
result := outcome.Results[0]
|
|
assert.Equal(t, DataVersionCurrent, result.DataVersion)
|
|
assert.Equal(t, "vscode-copilot:"+sessionID, result.Result.Session.ID)
|
|
assert.Equal(t, AgentVSCodeCopilot, result.Result.Session.Agent)
|
|
assert.Equal(t, "copilot-app", result.Result.Session.Project)
|
|
assert.Equal(t, "devbox", result.Result.Session.Machine)
|
|
assert.Equal(t, fingerprint.Hash, result.Result.Session.File.Hash)
|
|
assert.Equal(t, fingerprint.Size, result.Result.Session.File.Size)
|
|
assert.Equal(t, fingerprint.MTimeNS, result.Result.Session.File.Mtime)
|
|
assert.Len(t, result.Result.Messages, 2)
|
|
require.Len(t, result.Result.UsageEvents, 1)
|
|
assert.Equal(t, "vscode-copilot", result.Result.UsageEvents[0].Source)
|
|
assert.Equal(t, "claude-opus-4-8", result.Result.UsageEvents[0].Model)
|
|
assert.Equal(t, 42, result.Result.UsageEvents[0].InputTokens)
|
|
assert.Equal(t, 7, result.Result.UsageEvents[0].OutputTokens)
|
|
}
|
|
|
|
func TestVSCodeCopilotProviderClassifiesDeletedAndMetadataPaths(t *testing.T) {
|
|
root := t.TempDir()
|
|
hashDir := filepath.Join(root, "workspaceStorage", "workspace-hash")
|
|
chatDir := filepath.Join(hashDir, "chatSessions")
|
|
workspacePath := filepath.Join(hashDir, "workspace.json")
|
|
jsonlPath := filepath.Join(chatDir, "deleted-jsonl.jsonl")
|
|
jsonPath := filepath.Join(chatDir, "fallback-json.json")
|
|
globalPath := filepath.Join(
|
|
root,
|
|
"globalStorage",
|
|
"emptyWindowChatSessions",
|
|
"deleted-global.json",
|
|
)
|
|
writeSourceFile(t, workspacePath,
|
|
`{"folder":"file:///Users/alice/code/copilot-app"}`)
|
|
writeSourceFile(t, jsonlPath, vscodeCopilotProviderJSONL("deleted-jsonl", "Hello deleted"))
|
|
writeSourceFile(t, jsonPath, vscodeCopilotProviderJSON("fallback-json", "Hello fallback"))
|
|
writeSourceFile(t, globalPath, vscodeCopilotProviderJSON("deleted-global", "Hello global"))
|
|
|
|
provider, ok := NewProvider(AgentVSCodeCopilot, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
metadataChanged, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: workspacePath, EventKind: "write"},
|
|
)
|
|
require.NoError(t, err)
|
|
assert.ElementsMatch(t,
|
|
[]string{jsonlPath, jsonPath},
|
|
sourceDisplayPaths(metadataChanged),
|
|
)
|
|
require.Len(t, metadataChanged, 2)
|
|
beforeMetadata, err := provider.Fingerprint(context.Background(), metadataChanged[0])
|
|
require.NoError(t, err)
|
|
writeSourceFile(t, workspacePath,
|
|
`{"folder":"file:///Users/alice/code/copilot-renamed-app"}`)
|
|
afterMetadata, err := provider.Fingerprint(context.Background(), metadataChanged[0])
|
|
require.NoError(t, err)
|
|
assert.NotEqual(t, beforeMetadata.Hash, afterMetadata.Hash)
|
|
|
|
require.NoError(t, os.Remove(jsonlPath))
|
|
deletedJSONL, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: jsonlPath, EventKind: "remove"},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, deletedJSONL, 1)
|
|
assert.Equal(t, jsonlPath, deletedJSONL[0].DisplayPath)
|
|
|
|
require.NoError(t, os.Remove(globalPath))
|
|
deletedGlobal, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: globalPath, EventKind: "remove"},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, deletedGlobal, 1)
|
|
assert.Equal(t, globalPath, deletedGlobal[0].DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderSourceMethods(t *testing.T) {
|
|
root := t.TempDir()
|
|
conversationID := "4a8f63f6-7626-4416-a874-fc7bd2c3f005"
|
|
tracePath := filepath.Join(
|
|
root,
|
|
"20260612T194439_257709a3_VSGitHubCopilot_traces.jsonl",
|
|
)
|
|
writeSourceFile(t, tracePath, strings.Join([]string{
|
|
vsCopilotTraceLineJSON(conversationID,
|
|
"execute_tool run_command_in_terminal",
|
|
"1781293588624985000", "1781293588769581200",
|
|
map[string]string{
|
|
"gen_ai.tool.name": "run_command_in_terminal",
|
|
"gen_ai.tool.call.id": "call_123",
|
|
"gen_ai.tool.call.arguments": `{"command":"go test ./..."}`,
|
|
"gen_ai.tool.call.result": `{"Value":"ok"}`,
|
|
}),
|
|
vsCopilotTraceLineJSON(conversationID,
|
|
"invoke_agent GitHub Copilot",
|
|
"1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.agent.name": "GitHub Copilot",
|
|
"gen_ai.request.model": "gpt-5.5",
|
|
"copilot_chat.mode": "Agent",
|
|
"copilot_chat.turn_count": "1",
|
|
}),
|
|
}, "\n")+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 1)
|
|
assert.Equal(t, root, plan.Roots[0].Path)
|
|
assert.False(t, plan.Roots[0].Recursive)
|
|
assert.Equal(t, []string{"*_VSGitHubCopilot_traces.jsonl"}, plan.Roots[0].IncludeGlobs)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(tracePath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
assert.Equal(t, "visualstudio", discovered[0].ProjectHint)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, virtualPath, found.DisplayPath)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: tracePath, EventKind: "write", WatchRoot: root},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, tracePath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), found)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, virtualPath, fingerprint.Key)
|
|
assert.Positive(t, fingerprint.Size)
|
|
assert.Positive(t, fingerprint.MTimeNS)
|
|
assert.NotEmpty(t, fingerprint.Hash)
|
|
|
|
foundWithProject := found
|
|
foundWithProject.ProjectHint = "stored-solution"
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: foundWithProject,
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, outcome.ResultSetComplete)
|
|
require.True(t, outcome.ForceReplace)
|
|
require.Len(t, outcome.Results, 1)
|
|
result := outcome.Results[0]
|
|
assert.Equal(t, DataVersionCurrent, result.DataVersion)
|
|
assert.Equal(t, "visualstudio-copilot:"+conversationID, result.Result.Session.ID)
|
|
assert.Equal(t, AgentVSCopilot, result.Result.Session.Agent)
|
|
assert.Equal(t, "stored-solution", result.Result.Session.Project)
|
|
assert.Equal(t, "devbox", result.Result.Session.Machine)
|
|
assert.Equal(t, fingerprint.Hash, result.Result.Session.File.Hash)
|
|
assert.Len(t, result.Result.Messages, 1)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderClassifiesDeletedTraceAndFansOutPhysicalTrace(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
firstConversationID := "4a8f63f6-7626-4416-a874-fc7bd2c3f005"
|
|
secondConversationID := "5b9f63f6-7626-4416-a874-fc7bd2c3f006"
|
|
tracePath := filepath.Join(
|
|
root,
|
|
"20260612T194439_257709a3_VSGitHubCopilot_traces.jsonl",
|
|
)
|
|
writeSourceFile(t, tracePath, strings.Join([]string{
|
|
vsCopilotTraceLineJSON(firstConversationID,
|
|
"execute_tool run_command_in_terminal",
|
|
"1781293588624985000", "1781293588769581200",
|
|
map[string]string{
|
|
"gen_ai.tool.name": "run_command_in_terminal",
|
|
"gen_ai.tool.call.id": "call_123",
|
|
"gen_ai.tool.call.arguments": `{"command":"go test ./..."}`,
|
|
"gen_ai.tool.call.result": `{"Value":"ok"}`,
|
|
}),
|
|
vsCopilotTraceLineJSON(secondConversationID,
|
|
"execute_tool run_command_in_terminal",
|
|
"1781293688624985000", "1781293688769581200",
|
|
map[string]string{
|
|
"gen_ai.tool.name": "run_command_in_terminal",
|
|
"gen_ai.tool.call.id": "call_456",
|
|
"gen_ai.tool.call.arguments": `{"command":"go vet ./..."}`,
|
|
"gen_ai.tool.call.result": `{"Value":"ok"}`,
|
|
}),
|
|
}, "\n")+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
Machine: "devbox",
|
|
})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 2)
|
|
assert.ElementsMatch(t, []string{
|
|
VisualStudioCopilotVirtualPath(tracePath, firstConversationID),
|
|
VisualStudioCopilotVirtualPath(tracePath, secondConversationID),
|
|
}, sourceDisplayPaths(discovered))
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{Path: tracePath, EventKind: "write"},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, tracePath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), changed[0])
|
|
require.NoError(t, err)
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: changed[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, outcome.ForceReplace)
|
|
require.Len(t, outcome.Results, 2)
|
|
assert.ElementsMatch(t, []string{
|
|
"visualstudio-copilot:" + firstConversationID,
|
|
"visualstudio-copilot:" + secondConversationID,
|
|
}, parseOutcomeSessionIDs(outcome))
|
|
|
|
require.NoError(t, os.Remove(tracePath))
|
|
deleted, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: tracePath,
|
|
EventKind: "remove",
|
|
StoredSourcePaths: sourceDisplayPaths(discovered),
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, deleted, 1)
|
|
assert.Equal(t, tracePath, deleted[0].DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderTombstonesDeletedVS2026SessionFile(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
writeSourceFile(t, sessionPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
require.NoError(t, os.Remove(sessionPath))
|
|
deleted, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "remove",
|
|
StoredSourcePaths: sourceDisplayPaths(discovered),
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, deleted, 1)
|
|
assert.Equal(t, virtualPath, deleted[0].DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderCanonicalizesVS2026SessionFileIDCase(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
upperID := strings.ToUpper(conversationID)
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
upperID,
|
|
)
|
|
traceData := vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
) + "\n"
|
|
legacyPath := filepath.Join(
|
|
root,
|
|
"20260612T194439_257709a3_VSGitHubCopilot_traces.jsonl",
|
|
)
|
|
writeSourceFile(t, legacyPath, traceData)
|
|
writeSourceFile(t, sessionPath, traceData)
|
|
older := time.Date(2026, 6, 12, 0, 0, 0, 0, time.UTC)
|
|
newer := time.Date(2026, 6, 13, 0, 0, 0, 0, time.UTC)
|
|
require.NoError(t, os.Chtimes(legacyPath, older, older))
|
|
require.NoError(t, os.Chtimes(sessionPath, newer, newer))
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), discovered[0])
|
|
require.NoError(t, err)
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: discovered[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
require.Len(t, outcome.Results, 1)
|
|
assert.Equal(t,
|
|
"visualstudio-copilot:"+conversationID,
|
|
outcome.Results[0].Result.Session.ID,
|
|
)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderTombstonesUppercaseVS2026SessionFileID(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
strings.ToUpper(conversationID),
|
|
)
|
|
writeSourceFile(t, sessionPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
require.NoError(t, os.Remove(sessionPath))
|
|
deleted, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "remove",
|
|
StoredSourcePaths: sourceDisplayPaths(discovered),
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, deleted, 1)
|
|
assert.Equal(t, virtualPath, deleted[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), deleted[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, SourceFingerprint{Key: virtualPath}, fingerprint)
|
|
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: deleted[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.True(t, outcome.ForceReplace)
|
|
assert.Equal(t, SkipNoSession, outcome.SkipReason)
|
|
assert.Empty(t, outcome.Results)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderSupportsVS2026RootModes(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
vsRoot := filepath.Join(root, ".vs")
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
vsRoot, "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
traceData := vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
) + "\n"
|
|
writeSourceFile(t, sessionPath, traceData)
|
|
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
cases := []struct {
|
|
name string
|
|
root string
|
|
}{
|
|
{name: "project root", root: root},
|
|
{name: ".vs root", root: vsRoot},
|
|
{name: "copilot-chat root", root: filepath.Join(vsRoot, "SampleApp", "copilot-chat")},
|
|
{name: "thread root", root: filepath.Join(vsRoot, "SampleApp", "copilot-chat", "thread")},
|
|
{name: "sessions root", root: filepath.Join(vsRoot, "SampleApp", "copilot-chat", "thread", "sessions")},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{tc.root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
switch tc.name {
|
|
case "project root":
|
|
require.Len(t, plan.Roots, 3)
|
|
assert.Equal(t, root, plan.Roots[0].Path)
|
|
assert.False(t, plan.Roots[0].Recursive)
|
|
assert.Equal(t, vsRoot, plan.Roots[1].Path)
|
|
assert.True(t, plan.Roots[1].Recursive)
|
|
assert.Equal(t, filepath.Dir(sessionPath), plan.Roots[2].Path)
|
|
assert.False(t, plan.Roots[2].Recursive)
|
|
case "sessions root":
|
|
require.Len(t, plan.Roots, 1)
|
|
assert.Equal(t, tc.root, plan.Roots[0].Path)
|
|
assert.False(t, plan.Roots[0].Recursive)
|
|
default:
|
|
require.Len(t, plan.Roots, 2)
|
|
assert.Equal(t, tc.root, plan.Roots[0].Path)
|
|
assert.True(t, plan.Roots[0].Recursive)
|
|
assert.Equal(t, filepath.Dir(sessionPath), plan.Roots[1].Path)
|
|
assert.False(t, plan.Roots[1].Recursive)
|
|
}
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, virtualPath, found.DisplayPath)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "write",
|
|
WatchRoot: tc.root,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, virtualPath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), changed[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, virtualPath, fingerprint.Key)
|
|
assert.Positive(t, fingerprint.Size)
|
|
assert.Positive(t, fingerprint.MTimeNS)
|
|
assert.NotEmpty(t, fingerprint.Hash)
|
|
|
|
parseOutcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: changed[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, parseOutcome.ForceReplace)
|
|
require.Len(t, parseOutcome.Results, 1)
|
|
assert.Equal(t, "visualstudio-copilot:"+conversationID,
|
|
parseOutcome.Results[0].Result.Session.ID)
|
|
assert.Equal(t, virtualPath,
|
|
parseOutcome.Results[0].Result.Session.File.Path)
|
|
assert.Equal(t, fingerprint.Hash,
|
|
parseOutcome.Results[0].Result.Session.File.Hash)
|
|
assert.Equal(t, fingerprint.Size,
|
|
parseOutcome.Results[0].Result.Session.File.Size)
|
|
assert.Equal(t, fingerprint.MTimeNS,
|
|
parseOutcome.Results[0].Result.Session.File.Mtime)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderClassifiesMixedCaseVS2026Layout(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
root, ".VS", "SampleApp", "Copilot-Chat", "thread", "Sessions",
|
|
conversationID,
|
|
)
|
|
writeSourceFile(t, sessionPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
cases := []struct {
|
|
name string
|
|
root string
|
|
}{
|
|
{name: "project root", root: root},
|
|
{name: ".vs root", root: filepath.Join(root, ".VS")},
|
|
{name: "copilot-chat root", root: filepath.Join(root, ".VS", "SampleApp", "Copilot-Chat")},
|
|
{name: "thread root", root: filepath.Join(root, ".VS", "SampleApp", "Copilot-Chat", "thread")},
|
|
{name: "sessions root", root: filepath.Join(root, ".VS", "SampleApp", "Copilot-Chat", "thread", "Sessions")},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{tc.root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "write",
|
|
WatchRoot: tc.root,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, virtualPath, changed[0].DisplayPath)
|
|
})
|
|
}
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderDiscoversMixedCaseVS2026Layout(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
vsRoot := filepath.Join(root, ".VS")
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
vsRoot, "SampleApp", "Copilot-Chat", "thread", "Sessions",
|
|
conversationID,
|
|
)
|
|
writeSourceFile(t, sessionPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 3)
|
|
assert.Equal(t, root, plan.Roots[0].Path)
|
|
assert.False(t, plan.Roots[0].Recursive)
|
|
assert.Equal(t, vsRoot, plan.Roots[1].Path)
|
|
assert.True(t, plan.Roots[1].Recursive)
|
|
assert.Equal(t, filepath.Dir(sessionPath), plan.Roots[2].Path)
|
|
assert.False(t, plan.Roots[2].Recursive)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, virtualPath, found.DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderDiscoversSymlinkedVS2026Dirs(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
targetRoot := t.TempDir()
|
|
targetVSRoot := filepath.Join(targetRoot, "vs-data")
|
|
targetSolutionRoot := filepath.Join(targetRoot, "solution-data")
|
|
targetCopilotChatRoot := filepath.Join(targetRoot, "chat-data")
|
|
targetThreadRoot := filepath.Join(targetRoot, "thread-data")
|
|
targetSessionsRoot := filepath.Join(targetRoot, "sessions-data")
|
|
require.NoError(t, os.MkdirAll(targetVSRoot, 0o755))
|
|
require.NoError(t, os.MkdirAll(targetSolutionRoot, 0o755))
|
|
require.NoError(t, os.MkdirAll(targetCopilotChatRoot, 0o755))
|
|
require.NoError(t, os.MkdirAll(targetThreadRoot, 0o755))
|
|
require.NoError(t, os.MkdirAll(targetSessionsRoot, 0o755))
|
|
|
|
vsRoot := filepath.Join(root, ".VS")
|
|
if err := os.Symlink(targetVSRoot, vsRoot); err != nil {
|
|
t.Skipf("symlink not supported: %v", err)
|
|
}
|
|
require.NoError(t, os.Symlink(
|
|
targetSolutionRoot,
|
|
filepath.Join(targetVSRoot, "SampleApp"),
|
|
))
|
|
require.NoError(t, os.Symlink(
|
|
targetCopilotChatRoot,
|
|
filepath.Join(targetSolutionRoot, "Copilot-Chat"),
|
|
))
|
|
require.NoError(t, os.Symlink(
|
|
targetThreadRoot,
|
|
filepath.Join(targetCopilotChatRoot, "thread"),
|
|
))
|
|
require.NoError(t, os.Symlink(
|
|
targetSessionsRoot,
|
|
filepath.Join(targetThreadRoot, "Sessions"),
|
|
))
|
|
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
writeSourceFile(t, filepath.Join(targetSessionsRoot, conversationID),
|
|
vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
sessionPath := filepath.Join(
|
|
vsRoot, "SampleApp", "Copilot-Chat", "thread", "Sessions",
|
|
conversationID,
|
|
)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
plan, err := provider.WatchPlan(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, plan.Roots, 3)
|
|
assert.Equal(t, vsRoot, plan.Roots[1].Path)
|
|
assert.True(t, plan.Roots[1].Recursive)
|
|
assert.Equal(t, filepath.Dir(sessionPath), plan.Roots[2].Path)
|
|
assert.False(t, plan.Roots[2].Recursive)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, virtualPath, found.DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderCanonicalizesMixedLegacyAndVS2026Sources(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
legacyPath := filepath.Join(
|
|
root,
|
|
"20260612T194439_257709a3_VSGitHubCopilot_traces.jsonl",
|
|
)
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
traceData := vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
) + "\n"
|
|
writeSourceFile(t, legacyPath, traceData)
|
|
writeSourceFile(t, sessionPath, traceData)
|
|
|
|
older := time.Date(2026, 6, 12, 0, 0, 0, 0, time.UTC)
|
|
newer := time.Date(2026, 6, 13, 0, 0, 0, 0, time.UTC)
|
|
require.NoError(t, os.Chtimes(legacyPath, older, older))
|
|
require.NoError(t, os.Chtimes(sessionPath, newer, newer))
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{Roots: []string{root}})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
})
|
|
require.NoError(t, err)
|
|
require.True(t, ok)
|
|
assert.Equal(t, virtualPath, found.DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderDeletesVS2026SessionTombstone(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
writeSourceFile(t, sessionPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, virtualPath, discovered[0].DisplayPath)
|
|
|
|
require.NoError(t, os.Remove(sessionPath))
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "remove",
|
|
WatchRoot: root,
|
|
StoredSourcePaths: []string{virtualPath},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
assert.Equal(t, virtualPath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), changed[0])
|
|
require.NoError(t, err)
|
|
assert.Equal(t, SourceFingerprint{Key: virtualPath}, fingerprint)
|
|
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: changed[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.True(t, outcome.ForceReplace,
|
|
"deleted VS 2026 session files must force-replace the archived member")
|
|
assert.Equal(t, SkipNoSession, outcome.SkipReason)
|
|
assert.Empty(t, outcome.Results)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderRecanonicalizesDeletedVS2026Session(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
legacyPath := filepath.Join(
|
|
root,
|
|
"20260612T194439_257709a3_VSGitHubCopilot_traces.jsonl",
|
|
)
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
traceData := vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
) + "\n"
|
|
writeSourceFile(t, legacyPath, traceData)
|
|
writeSourceFile(t, sessionPath, traceData)
|
|
|
|
older := time.Date(2026, 6, 12, 0, 0, 0, 0, time.UTC)
|
|
newer := time.Date(2026, 6, 13, 0, 0, 0, 0, time.UTC)
|
|
require.NoError(t, os.Chtimes(legacyPath, older, older))
|
|
require.NoError(t, os.Chtimes(sessionPath, newer, newer))
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
require.Len(t, discovered, 1)
|
|
vsVirtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
assert.Equal(t, vsVirtualPath, discovered[0].DisplayPath)
|
|
|
|
require.NoError(t, os.Remove(sessionPath))
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: sessionPath,
|
|
EventKind: "remove",
|
|
WatchRoot: root,
|
|
StoredSourcePaths: []string{vsVirtualPath},
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
require.Len(t, changed, 1)
|
|
|
|
legacyVirtualPath := VisualStudioCopilotVirtualPath(legacyPath, conversationID)
|
|
assert.Equal(t, legacyVirtualPath, changed[0].DisplayPath)
|
|
|
|
fingerprint, err := provider.Fingerprint(context.Background(), changed[0])
|
|
require.NoError(t, err)
|
|
outcome, err := provider.Parse(context.Background(), ParseRequest{
|
|
Source: changed[0],
|
|
Fingerprint: fingerprint,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.True(t, outcome.ResultSetComplete)
|
|
assert.True(t, outcome.ForceReplace)
|
|
require.Len(t, outcome.Results, 1)
|
|
assert.Equal(
|
|
t,
|
|
string(AgentVSCopilot)+":"+conversationID,
|
|
outcome.Results[0].Result.Session.ID,
|
|
)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderFindSourceRejectsMissingVS2026VirtualPath(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20"
|
|
sessionPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
conversationID,
|
|
)
|
|
virtualPath := VisualStudioCopilotVirtualPath(sessionPath, conversationID)
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
found, ok, err := provider.FindSource(context.Background(), FindSourceRequest{
|
|
RawSessionID: conversationID,
|
|
StoredFilePath: virtualPath,
|
|
FingerprintKey: virtualPath,
|
|
RequireFreshSource: true,
|
|
PreferStoredSource: true,
|
|
})
|
|
require.NoError(t, err)
|
|
assert.False(t, ok)
|
|
assert.Empty(t, found.DisplayPath)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderRejectsOutsideVS2026SessionLayout(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
conversationID := "8e5bb2d4-ef8e-4a90-a1f2-3d3d4d6fc9e9"
|
|
invalidPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "transcripts",
|
|
conversationID,
|
|
)
|
|
require.NoError(t, os.MkdirAll(filepath.Dir(invalidPath), 0o755))
|
|
writeSourceFile(t, invalidPath, vsCopilotTraceLineJSON(
|
|
conversationID,
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: invalidPath,
|
|
EventKind: "write",
|
|
WatchRoot: root,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, changed)
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Empty(t, discovered)
|
|
}
|
|
|
|
func TestVisualStudioCopilotProviderRejectsNonGUIDVS2026SessionFileNames(
|
|
t *testing.T,
|
|
) {
|
|
root := t.TempDir()
|
|
invalidName := "not-a-guid"
|
|
invalidPath := filepath.Join(
|
|
root, ".vs", "SampleApp", "copilot-chat", "thread", "sessions",
|
|
invalidName,
|
|
)
|
|
writeSourceFile(t, invalidPath, vsCopilotTraceLineJSON(
|
|
"5bc5f6d7-9a6e-4f9c-8f3c-b7be2e7d9f20",
|
|
"chat gpt-5.5", "1781293600000000000", "1781293610000000000",
|
|
map[string]string{
|
|
"gen_ai.operation.name": "chat",
|
|
"gen_ai.input.messages": `[{"role":"user","parts":[{"type":"text","content":"Run the tests."}]}]`,
|
|
},
|
|
)+"\n")
|
|
|
|
provider, ok := NewProvider(AgentVSCopilot, ProviderConfig{
|
|
Roots: []string{root},
|
|
})
|
|
require.True(t, ok)
|
|
|
|
changed, err := provider.SourcesForChangedPath(
|
|
context.Background(),
|
|
ChangedPathRequest{
|
|
Path: invalidPath,
|
|
EventKind: "write",
|
|
WatchRoot: root,
|
|
},
|
|
)
|
|
require.NoError(t, err)
|
|
assert.Empty(t, changed)
|
|
|
|
discovered, err := provider.Discover(context.Background())
|
|
require.NoError(t, err)
|
|
assert.Empty(t, discovered)
|
|
}
|
|
|
|
func vscodeCopilotProviderJSON(sessionID, prompt string) string {
|
|
return `{"version":3,"sessionId":"` + sessionID + `","creationDate":1770650022790,"requests":[{"requestId":"req1","timestamp":1770650031889,"message":{"text":"` + prompt + `","parts":[]},"response":[{"value":"Hi from VS Code"}],"modelId":"copilot/gpt-4o"}]}`
|
|
}
|
|
|
|
func vscodeCopilotProviderJSONL(sessionID, prompt string) string {
|
|
return strings.Join([]string{
|
|
`{"kind":0,"v":{"version":3,"sessionId":"` + sessionID + `","creationDate":1770650022790,"requests":[]}}`,
|
|
`{"kind":2,"k":["requests"],"v":[{"requestId":"req1","timestamp":1770650031889,"message":{"text":"` + prompt + `","parts":[]},"response":[{"value":"Hi from VS Code"}],"modelId":"copilot/gpt-4o"}]}`,
|
|
}, "\n") + "\n"
|
|
}
|
|
|
|
func parseOutcomeSessionIDs(outcome ParseOutcome) []string {
|
|
ids := make([]string, 0, len(outcome.Results))
|
|
for _, result := range outcome.Results {
|
|
ids = append(ids, result.Result.Session.ID)
|
|
}
|
|
return ids
|
|
}
|