1- import { describe , expect , mock , test } from "bun:test"
1+ import { afterEach , describe , expect , mock , spyOn , test } from "bun:test"
22import fs from "fs/promises"
33import path from "path"
44import { tmpdir } from "../../fixture/fixture"
5+ import * as App from "../../../src/cli/cmd/tui/app"
6+ import { Rpc } from "../../../src/util/rpc"
7+ import { UI } from "../../../src/cli/ui"
8+ import * as Timeout from "../../../src/util/timeout"
9+ import * as Network from "../../../src/cli/network"
10+ import * as Win32 from "../../../src/cli/cmd/tui/win32"
11+ import { TuiConfig } from "../../../src/config/tui"
12+ import { Instance } from "../../../src/project/instance"
513
614const stop = new Error ( "stop" )
715const seen = {
816 tui : [ ] as string [ ] ,
917 inst : [ ] as string [ ] ,
1018}
1119
12- mock . module ( "../../../src/cli/cmd/tui/app" , ( ) => ( {
13- tui : async ( input : { directory : string } ) => {
14- seen . tui . push ( input . directory )
20+ function setup ( ) {
21+ // Intentionally avoid mock.module() here: Bun keeps module overrides in cache
22+ // and mock.restore() does not reset mock.module values. If this switches back
23+ // to module mocks, later suites can see mocked @/config/tui and fail (e.g.
24+ // plugin-loader tests expecting real TuiConfig.waitForDependencies). See:
25+ // https://github.com/oven-sh/bun/issues/7823 and #12823.
26+ spyOn ( App , "tui" ) . mockImplementation ( async ( input ) => {
27+ if ( input . directory ) seen . tui . push ( input . directory )
1528 throw stop
16- } ,
17- } ) )
18-
19- mock . module ( "@/util/rpc" , ( ) => ( {
20- Rpc : {
21- client : ( ) => ( {
22- call : async ( ) => ( { url : "http://127.0.0.1" } ) ,
23- on : ( ) => { } ,
24- } ) ,
25- } ,
26- } ) )
27-
28- mock . module ( "@/cli/ui" , ( ) => ( {
29- UI : {
30- error : ( ) => { } ,
31- } ,
32- } ) )
33-
34- mock . module ( "@/util/log" , ( ) => ( {
35- Log : {
36- init : async ( ) => { } ,
37- create : ( ) => ( {
38- error : ( ) => { } ,
39- info : ( ) => { } ,
40- warn : ( ) => { } ,
41- debug : ( ) => { } ,
42- time : ( ) => ( { stop : ( ) => { } } ) ,
43- } ) ,
44- Default : {
45- error : ( ) => { } ,
46- info : ( ) => { } ,
47- warn : ( ) => { } ,
48- debug : ( ) => { } ,
49- } ,
50- } ,
51- } ) )
52-
53- mock . module ( "@/util/timeout" , ( ) => ( {
54- withTimeout : < T > ( input : Promise < T > ) => input ,
55- } ) )
56-
57- mock . module ( "@/cli/network" , ( ) => ( {
58- withNetworkOptions : < T > ( input : T ) => input ,
59- resolveNetworkOptions : async ( ) => ( {
29+ } )
30+ spyOn ( Rpc , "client" ) . mockImplementation ( ( ) => ( {
31+ call : async ( ) => ( { url : "http://127.0.0.1" } ) as never ,
32+ on : ( ) => ( ) => { } ,
33+ } ) )
34+ spyOn ( UI , "error" ) . mockImplementation ( ( ) => { } )
35+ spyOn ( Timeout , "withTimeout" ) . mockImplementation ( ( input ) => input )
36+ spyOn ( Network , "resolveNetworkOptions" ) . mockResolvedValue ( {
6037 mdns : false ,
6138 port : 0 ,
6239 hostname : "127.0.0.1" ,
63- } ) ,
64- } ) )
65-
66- mock . module ( "../../../src/cli/cmd/tui/win32" , ( ) => ( {
67- win32DisableProcessedInput : ( ) => { } ,
68- win32InstallCtrlCGuard : ( ) => undefined ,
69- } ) )
70-
71- mock . module ( "@/config/tui" , ( ) => ( {
72- TuiConfig : {
73- get : ( ) => ( { } ) ,
74- } ,
75- } ) )
76-
77- mock . module ( "@/project/instance" , ( ) => ( {
78- Instance : {
79- provide : async ( input : { directory : string ; fn : ( ) => Promise < unknown > | unknown } ) => {
80- seen . inst . push ( input . directory )
81- return input . fn ( )
82- } ,
83- } ,
84- } ) )
40+ mdnsDomain : "opencode.local" ,
41+ cors : [ ] ,
42+ } )
43+ spyOn ( Win32 , "win32DisableProcessedInput" ) . mockImplementation ( ( ) => { } )
44+ spyOn ( Win32 , "win32InstallCtrlCGuard" ) . mockReturnValue ( undefined )
45+ spyOn ( TuiConfig , "get" ) . mockResolvedValue ( { } )
46+ spyOn ( Instance , "provide" ) . mockImplementation ( async ( input ) => {
47+ seen . inst . push ( input . directory )
48+ return input . fn ( )
49+ } )
50+ }
8551
8652describe ( "tui thread" , ( ) => {
53+ afterEach ( ( ) => {
54+ mock . restore ( )
55+ } )
56+
8757 async function call ( project ?: string ) {
8858 const { TuiThreadCommand } = await import ( "../../../src/cli/cmd/tui/thread" )
8959 const args : Parameters < NonNullable < typeof TuiThreadCommand . handler > > [ 0 ] = {
@@ -107,6 +77,7 @@ describe("tui thread", () => {
10777 }
10878
10979 async function check ( project ?: string ) {
80+ setup ( )
11081 await using tmp = await tmpdir ( { git : true } )
11182 const cwd = process . cwd ( )
11283 const pwd = process . env . PWD
0 commit comments