1- import { describe , it , expect , beforeEach , afterEach } from 'vitest' ;
1+ import { describe , it , expect , beforeEach , afterEach , vi } from 'vitest' ;
22import Database from 'better-sqlite3' ;
3- import { mkdtempSync , mkdirSync , copyFileSync } from 'fs' ;
3+ import { mkdtempSync , mkdirSync , copyFileSync , existsSync } from 'fs' ;
44import { join } from 'path' ;
55import { tmpdir } from 'os' ;
66import { execSync } from 'child_process' ;
7+ import { createTeamCommands } from '../team.js' ;
78
89/**
910 * Create a minimal .stackmemory/context.db that FrameManager can open.
@@ -66,33 +67,70 @@ function setupProjectWithData(dir: string): Database.Database {
6667 return db ;
6768}
6869
69- const cliPath = join ( __dirname , '..' , '..' , 'index.ts' ) ;
70+ /**
71+ * Run a team command programmatically via commander.
72+ * Temporarily changes cwd and captures console output.
73+ */
74+ async function runTeamCommand (
75+ args : string [ ] ,
76+ cwd : string
77+ ) : Promise < { stdout : string ; exitCode : number } > {
78+ const originalCwd = process . cwd ( ) ;
79+ const originalExitCode = process . exitCode ;
80+ process . chdir ( cwd ) ;
81+ process . exitCode = 0 ;
82+
83+ const logs : string [ ] = [ ] ;
84+ const spy = vi . spyOn ( console , 'log' ) . mockImplementation ( ( ...a ) => {
85+ logs . push ( a . map ( String ) . join ( ' ' ) ) ;
86+ } ) ;
87+ const errSpy = vi . spyOn ( console , 'error' ) . mockImplementation ( ( ...a ) => {
88+ logs . push ( a . map ( String ) . join ( ' ' ) ) ;
89+ } ) ;
90+
91+ try {
92+ const cmd = createTeamCommands ( ) ;
93+ // Commander expects program name + subcommand in argv
94+ await cmd . parseAsync ( [ 'node' , 'team' , ...args ] ) ;
95+ } finally {
96+ spy . mockRestore ( ) ;
97+ errSpy . mockRestore ( ) ;
98+ process . chdir ( originalCwd ) ;
99+ }
100+
101+ const exitCode = process . exitCode ?? 0 ;
102+ process . exitCode = originalExitCode ;
103+ return { stdout : logs . join ( '\n' ) , exitCode } ;
104+ }
70105
71106describe ( 'team CLI commands' , ( ) => {
72107 let tmpDir : string ;
73- let originalCwd : string ;
74108
75109 beforeEach ( ( ) => {
76110 tmpDir = mkdtempSync ( join ( tmpdir ( ) , 'sm-team-test-' ) ) ;
77- originalCwd = process . cwd ( ) ;
78- } ) ;
79-
80- afterEach ( ( ) => {
81- process . chdir ( originalCwd ) ;
82111 } ) ;
83112
84113 describe ( 'team share' , ( ) => {
85- it ( 'should create shared anchor with correct metadata' , ( ) => {
114+ it ( 'should create shared anchor with correct metadata' , async ( ) => {
86115 setupEmptyProject ( tmpDir ) ;
87- process . chdir ( tmpDir ) ;
88116
89- const result = execSync (
90- `npx tsx ${ cliPath } team share -c "API endpoint is /v2/users" -t DECISION -p 9 --source manual` ,
91- { cwd : tmpDir , encoding : 'utf-8' , timeout : 15000 }
117+ const { stdout } = await runTeamCommand (
118+ [
119+ 'share' ,
120+ '-c' ,
121+ 'API endpoint is /v2/users' ,
122+ '-t' ,
123+ 'DECISION' ,
124+ '-p' ,
125+ '9' ,
126+ '--source' ,
127+ 'manual' ,
128+ ] ,
129+ tmpDir
92130 ) ;
93131
94- expect ( result ) . toContain ( '[DECISION]' ) ;
95- expect ( result ) . toContain ( 'priority 9' ) ;
132+ expect ( stdout ) . toContain ( '[DECISION]' ) ;
133+ expect ( stdout ) . toContain ( 'priority 9' ) ;
96134
97135 const checkDb = new Database ( join ( tmpDir , '.stackmemory' , 'context.db' ) ) ;
98136 const anchors = checkDb
@@ -116,15 +154,10 @@ describe('team CLI commands', () => {
116154 checkDb . close ( ) ;
117155 } ) ;
118156
119- it ( 'should default to type=FACT priority=7' , ( ) => {
157+ it ( 'should default to type=FACT priority=7' , async ( ) => {
120158 setupEmptyProject ( tmpDir ) ;
121- process . chdir ( tmpDir ) ;
122159
123- execSync ( `npx tsx ${ cliPath } team share -c "some fact"` , {
124- cwd : tmpDir ,
125- encoding : 'utf-8' ,
126- timeout : 15000 ,
127- } ) ;
160+ await runTeamCommand ( [ 'share' , '-c' , 'some fact' ] , tmpDir ) ;
128161
129162 const checkDb = new Database ( join ( tmpDir , '.stackmemory' , 'context.db' ) ) ;
130163 const anchors = checkDb
@@ -137,15 +170,10 @@ describe('team CLI commands', () => {
137170 checkDb . close ( ) ;
138171 } ) ;
139172
140- it ( 'should auto-create frame if none active' , ( ) => {
173+ it ( 'should auto-create frame if none active' , async ( ) => {
141174 setupEmptyProject ( tmpDir ) ;
142- process . chdir ( tmpDir ) ;
143175
144- execSync ( `npx tsx ${ cliPath } team share -c "auto-frame test"` , {
145- cwd : tmpDir ,
146- encoding : 'utf-8' ,
147- timeout : 15000 ,
148- } ) ;
176+ await runTeamCommand ( [ 'share' , '-c' , 'auto-frame test' ] , tmpDir ) ;
149177
150178 const checkDb = new Database ( join ( tmpDir , '.stackmemory' , 'context.db' ) ) ;
151179 const frames = checkDb . prepare ( `SELECT * FROM frames` ) . all ( ) as Array < {
@@ -157,13 +185,22 @@ describe('team CLI commands', () => {
157185 checkDb . close ( ) ;
158186 } ) ;
159187
160- it ( 'should store source, agentId, taskId in metadata' , ( ) => {
188+ it ( 'should store source, agentId, taskId in metadata' , async ( ) => {
161189 setupEmptyProject ( tmpDir ) ;
162- process . chdir ( tmpDir ) ;
163190
164- execSync (
165- `npx tsx ${ cliPath } team share -c "context with ids" --source subagent --agent-id agent-1 --task-id task-42` ,
166- { cwd : tmpDir , encoding : 'utf-8' , timeout : 15000 }
191+ await runTeamCommand (
192+ [
193+ 'share' ,
194+ '-c' ,
195+ 'context with ids' ,
196+ '--source' ,
197+ 'subagent' ,
198+ '--agent-id' ,
199+ 'agent-1' ,
200+ '--task-id' ,
201+ 'task-42' ,
202+ ] ,
203+ tmpDir
167204 ) ;
168205
169206 const checkDb = new Database ( join ( tmpDir , '.stackmemory' , 'context.db' ) ) ;
@@ -180,16 +217,11 @@ describe('team CLI commands', () => {
180217 checkDb . close ( ) ;
181218 } ) ;
182219
183- it ( 'should truncate content > 2000 chars' , ( ) => {
220+ it ( 'should truncate content > 2000 chars' , async ( ) => {
184221 setupEmptyProject ( tmpDir ) ;
185- process . chdir ( tmpDir ) ;
186222
187223 const longContent = 'x' . repeat ( 3000 ) ;
188- execSync ( `npx tsx ${ cliPath } team share -c "${ longContent } "` , {
189- cwd : tmpDir ,
190- encoding : 'utf-8' ,
191- timeout : 15000 ,
192- } ) ;
224+ await runTeamCommand ( [ 'share' , '-c' , longContent ] , tmpDir ) ;
193225
194226 const checkDb = new Database ( join ( tmpDir , '.stackmemory' , 'context.db' ) ) ;
195227 const anchors = checkDb
@@ -204,7 +236,7 @@ describe('team CLI commands', () => {
204236 } ) ;
205237
206238 describe ( 'team list' , ( ) => {
207- it ( 'should list shared anchors' , ( ) => {
239+ it ( 'should list shared anchors' , async ( ) => {
208240 const db = setupProjectWithData ( tmpDir ) ;
209241 const now = Math . floor ( Date . now ( ) / 1000 ) ;
210242 db . prepare (
@@ -217,20 +249,14 @@ describe('team CLI commands', () => {
217249 ) . run ( now - 30 ) ;
218250 db . close ( ) ;
219251
220- process . chdir ( tmpDir ) ;
221-
222- const result = execSync ( `npx tsx ${ cliPath } team list` , {
223- cwd : tmpDir ,
224- encoding : 'utf-8' ,
225- timeout : 15000 ,
226- } ) ;
252+ const { stdout } = await runTeamCommand ( [ 'list' ] , tmpDir ) ;
227253
228- expect ( result ) . toContain ( 'shared finding' ) ;
229- expect ( result ) . toContain ( '[FACT]' ) ;
230- expect ( result ) . toContain ( 'p8' ) ;
254+ expect ( stdout ) . toContain ( 'shared finding' ) ;
255+ expect ( stdout ) . toContain ( '[FACT]' ) ;
256+ expect ( stdout ) . toContain ( 'p8' ) ;
231257 } ) ;
232258
233- it ( 'should respect --limit' , ( ) => {
259+ it ( 'should respect --limit' , async ( ) => {
234260 const db = setupProjectWithData ( tmpDir ) ;
235261 const now = Math . floor ( Date . now ( ) / 1000 ) ;
236262
@@ -247,29 +273,18 @@ describe('team CLI commands', () => {
247273 }
248274 db . close ( ) ;
249275
250- process . chdir ( tmpDir ) ;
276+ const { stdout } = await runTeamCommand ( [ 'list' , '--limit' , '2' ] , tmpDir ) ;
251277
252- const result = execSync ( `npx tsx ${ cliPath } team list --limit 2` , {
253- cwd : tmpDir ,
254- encoding : 'utf-8' ,
255- timeout : 15000 ,
256- } ) ;
257-
258- expect ( result ) . toContain ( '2 anchors' ) ;
278+ expect ( stdout ) . toContain ( '2 anchors' ) ;
259279 } ) ;
260280
261- it ( 'should show no results when no shared anchors exist' , ( ) => {
281+ it ( 'should show no results when no shared anchors exist' , async ( ) => {
262282 const db = setupProjectWithData ( tmpDir ) ;
263283 db . close ( ) ;
264- process . chdir ( tmpDir ) ;
265284
266- const result = execSync ( `npx tsx ${ cliPath } team list` , {
267- cwd : tmpDir ,
268- encoding : 'utf-8' ,
269- timeout : 15000 ,
270- } ) ;
285+ const { stdout } = await runTeamCommand ( [ 'list' ] , tmpDir ) ;
271286
272- expect ( result ) . toContain ( 'No shared context found' ) ;
287+ expect ( stdout ) . toContain ( 'No shared context found' ) ;
273288 } ) ;
274289 } ) ;
275290
0 commit comments