1- import { describe , expect , it , vi } from "vitest" ;
1+ import { mkdtempSync , rmSync } from "node:fs" ;
2+ import { tmpdir } from "node:os" ;
3+ import path from "node:path" ;
4+ import { beforeEach , describe , expect , it , vi } from "vitest" ;
5+ import { createMockRepositoryRepository } from "../../db/repositories/repository-repository.mock" ;
6+ import { createMockWorkspaceRepository } from "../../db/repositories/workspace-repository.mock" ;
7+ import { createMockWorktreeRepository } from "../../db/repositories/worktree-repository.mock" ;
28import { ShellEvent } from "./schemas" ;
39
410const mockPty = vi . hoisted ( ( ) => ( {
@@ -7,6 +13,13 @@ const mockPty = vi.hoisted(() => ({
713
814vi . mock ( "node-pty" , ( ) => mockPty ) ;
915
16+ const mockGitQueries = vi . hoisted ( ( ) => ( {
17+ getCurrentBranch : vi . fn ( async ( ) => "feature-branch" ) ,
18+ getDefaultBranch : vi . fn ( async ( ) => "main" ) ,
19+ } ) ) ;
20+
21+ vi . mock ( "@posthog/git/queries" , ( ) => mockGitQueries ) ;
22+
1023import { ShellService } from "./shell" ;
1124
1225function createMockPtyProcess ( ) {
@@ -21,7 +34,11 @@ function createMockPtyProcess() {
2134 } ;
2235}
2336
24- function createService ( ) {
37+ function createService ( overrides ?: {
38+ repositoryRepo ?: unknown ;
39+ workspaceRepo ?: unknown ;
40+ worktreeRepo ?: unknown ;
41+ } ) {
2542 const processTracking = {
2643 register : vi . fn ( ) ,
2744 unregister : vi . fn ( ) ,
@@ -37,9 +54,9 @@ function createService() {
3754 } ;
3855 const service = new ShellService (
3956 processTracking as never ,
40- { } as never ,
41- { } as never ,
42- { } as never ,
57+ ( overrides ?. repositoryRepo ?? { } ) as never ,
58+ ( overrides ?. workspaceRepo ?? { } ) as never ,
59+ ( overrides ?. worktreeRepo ?? { } ) as never ,
4360 { getWorktreeLocation : vi . fn ( ( ) => "/tmp/worktrees" ) } as never ,
4461 logger as never ,
4562 ) ;
@@ -68,3 +85,72 @@ describe("ShellService.destroy", () => {
6885 expect ( ( ) => service . destroy ( "nonexistent" ) ) . not . toThrow ( ) ;
6986 } ) ;
7087} ) ;
88+
89+ describe ( "ShellService.createSession workspace env" , ( ) => {
90+ function createWorktreeTaskService ( worktreePath : string ) {
91+ const repositoryRepo = createMockRepositoryRepository ( ) ;
92+ const workspaceRepo = createMockWorkspaceRepository ( ) ;
93+ const worktreeRepo = createMockWorktreeRepository ( ) ;
94+ const repo = repositoryRepo . create ( { path : "/repos/code" } ) ;
95+ const workspace = workspaceRepo . create ( {
96+ taskId : "task-1" ,
97+ repositoryId : repo . id ,
98+ mode : "worktree" ,
99+ } ) ;
100+ worktreeRepo . create ( {
101+ workspaceId : workspace . id ,
102+ name : "spawn-tasks" ,
103+ path : worktreePath ,
104+ } ) ;
105+ return createService ( { repositoryRepo, workspaceRepo, worktreeRepo } ) ;
106+ }
107+
108+ function spawnedEnv ( ) : Record < string , string | undefined > {
109+ return mockPty . spawn . mock . calls [ 0 ] [ 2 ] . env ;
110+ }
111+
112+ beforeEach ( ( ) => {
113+ mockPty . spawn . mockReset ( ) ;
114+ mockPty . spawn . mockReturnValue ( createMockPtyProcess ( ) ) ;
115+ mockGitQueries . getCurrentBranch . mockResolvedValue ( "feature-branch" ) ;
116+ mockGitQueries . getDefaultBranch . mockResolvedValue ( "main" ) ;
117+ } ) ;
118+
119+ it ( "uses the stored worktree path when it exists on disk" , async ( ) => {
120+ const tempDir = mkdtempSync ( path . join ( tmpdir ( ) , "shell-test-" ) ) ;
121+ try {
122+ const { service } = createWorktreeTaskService ( tempDir ) ;
123+
124+ await service . createSession ( { sessionId : "session-1" , taskId : "task-1" } ) ;
125+
126+ expect ( spawnedEnv ( ) . POSTHOG_CODE_WORKSPACE_PATH ) . toBe ( tempDir ) ;
127+ expect ( mockGitQueries . getCurrentBranch ) . toHaveBeenCalledWith ( tempDir ) ;
128+ } finally {
129+ rmSync ( tempDir , { recursive : true , force : true } ) ;
130+ }
131+ } ) ;
132+
133+ it ( "falls back to the derived path when the stored path is missing" , async ( ) => {
134+ const { service } = createWorktreeTaskService ( "/does/not/exist" ) ;
135+
136+ await service . createSession ( { sessionId : "session-1" , taskId : "task-1" } ) ;
137+
138+ const derivedPath = path . join ( "/tmp/worktrees" , "spawn-tasks" , "code" ) ;
139+ expect ( spawnedEnv ( ) . POSTHOG_CODE_WORKSPACE_PATH ) . toBe ( derivedPath ) ;
140+ expect ( mockGitQueries . getCurrentBranch ) . toHaveBeenCalledWith ( derivedPath ) ;
141+ } ) ;
142+
143+ it ( "still creates the shell when env construction fails" , async ( ) => {
144+ mockGitQueries . getDefaultBranch . mockRejectedValue (
145+ new Error ( "Cannot use simple-git on a directory that does not exist" ) ,
146+ ) ;
147+ const { service } = createWorktreeTaskService ( "/does/not/exist" ) ;
148+
149+ await expect (
150+ service . createSession ( { sessionId : "session-1" , taskId : "task-1" } ) ,
151+ ) . resolves . toBeDefined ( ) ;
152+
153+ expect ( mockPty . spawn ) . toHaveBeenCalledTimes ( 1 ) ;
154+ expect ( spawnedEnv ( ) . POSTHOG_CODE_WORKSPACE_PATH ) . toBeUndefined ( ) ;
155+ } ) ;
156+ } ) ;
0 commit comments