1- import { test , expect , describe , mock } from "bun:test"
1+ import { test , expect , describe , mock , afterEach } from "bun:test"
22import { Config } from "../../src/config/config"
33import { Instance } from "../../src/project/instance"
44import { Auth } from "../../src/auth"
55import { tmpdir } from "../fixture/fixture"
66import path from "path"
77import fs from "fs/promises"
88import { pathToFileURL } from "url"
9+ import { Global } from "../../src/global"
10+
11+ // Get managed config directory from environment (set in preload.ts)
12+ const managedConfigDir = process . env . OPENCODE_TEST_MANAGED_CONFIG_DIR !
13+
14+ afterEach ( async ( ) => {
15+ await fs . rm ( managedConfigDir , { force : true , recursive : true } ) . catch ( ( ) => { } )
16+ } )
17+
18+ async function writeManagedSettings ( settings : object , filename = "opencode.json" ) {
19+ await fs . mkdir ( managedConfigDir , { recursive : true } )
20+ await Bun . write ( path . join ( managedConfigDir , filename ) , JSON . stringify ( settings ) )
21+ }
22+
23+ async function writeConfig ( dir : string , config : object , name = "opencode.json" ) {
24+ await Bun . write ( path . join ( dir , name ) , JSON . stringify ( config ) )
25+ }
926
1027test ( "loads config with defaults when no files exist" , async ( ) => {
1128 await using tmp = await tmpdir ( )
@@ -21,14 +38,11 @@ test("loads config with defaults when no files exist", async () => {
2138test ( "loads JSON config file" , async ( ) => {
2239 await using tmp = await tmpdir ( {
2340 init : async ( dir ) => {
24- await Bun . write (
25- path . join ( dir , "opencode.json" ) ,
26- JSON . stringify ( {
27- $schema : "https://opencode.ai/config.json" ,
28- model : "test/model" ,
29- username : "testuser" ,
30- } ) ,
31- )
41+ await writeConfig ( dir , {
42+ $schema : "https://opencode.ai/config.json" ,
43+ model : "test/model" ,
44+ username : "testuser" ,
45+ } )
3246 } ,
3347 } )
3448 await Instance . provide ( {
@@ -68,21 +82,19 @@ test("loads JSONC config file", async () => {
6882test ( "merges multiple config files with correct precedence" , async ( ) => {
6983 await using tmp = await tmpdir ( {
7084 init : async ( dir ) => {
71- await Bun . write (
72- path . join ( dir , "opencode.jsonc" ) ,
73- JSON . stringify ( {
85+ await writeConfig (
86+ dir ,
87+ {
7488 $schema : "https://opencode.ai/config.json" ,
7589 model : "base" ,
7690 username : "base" ,
77- } ) ,
78- )
79- await Bun . write (
80- path . join ( dir , "opencode.json" ) ,
81- JSON . stringify ( {
82- $schema : "https://opencode.ai/config.json" ,
83- model : "override" ,
84- } ) ,
91+ } ,
92+ "opencode.jsonc" ,
8593 )
94+ await writeConfig ( dir , {
95+ $schema : "https://opencode.ai/config.json" ,
96+ model : "override" ,
97+ } )
8698 } ,
8799 } )
88100 await Instance . provide ( {
@@ -102,13 +114,10 @@ test("handles environment variable substitution", async () => {
102114 try {
103115 await using tmp = await tmpdir ( {
104116 init : async ( dir ) => {
105- await Bun . write (
106- path . join ( dir , "opencode.json" ) ,
107- JSON . stringify ( {
108- $schema : "https://opencode.ai/config.json" ,
109- theme : "{env:TEST_VAR}" ,
110- } ) ,
111- )
117+ await writeConfig ( dir , {
118+ $schema : "https://opencode.ai/config.json" ,
119+ theme : "{env:TEST_VAR}" ,
120+ } )
112121 } ,
113122 } )
114123 await Instance . provide ( {
@@ -169,13 +178,10 @@ test("handles file inclusion substitution", async () => {
169178 await using tmp = await tmpdir ( {
170179 init : async ( dir ) => {
171180 await Bun . write ( path . join ( dir , "included.txt" ) , "test_theme" )
172- await Bun . write (
173- path . join ( dir , "opencode.json" ) ,
174- JSON . stringify ( {
175- $schema : "https://opencode.ai/config.json" ,
176- theme : "{file:included.txt}" ,
177- } ) ,
178- )
181+ await writeConfig ( dir , {
182+ $schema : "https://opencode.ai/config.json" ,
183+ theme : "{file:included.txt}" ,
184+ } )
179185 } ,
180186 } )
181187 await Instance . provide ( {
@@ -190,13 +196,10 @@ test("handles file inclusion substitution", async () => {
190196test ( "validates config schema and throws on invalid fields" , async ( ) => {
191197 await using tmp = await tmpdir ( {
192198 init : async ( dir ) => {
193- await Bun . write (
194- path . join ( dir , "opencode.json" ) ,
195- JSON . stringify ( {
196- $schema : "https://opencode.ai/config.json" ,
197- invalid_field : "should cause error" ,
198- } ) ,
199- )
199+ await writeConfig ( dir , {
200+ $schema : "https://opencode.ai/config.json" ,
201+ invalid_field : "should cause error" ,
202+ } )
200203 } ,
201204 } )
202205 await Instance . provide ( {
@@ -225,19 +228,16 @@ test("throws error for invalid JSON", async () => {
225228test ( "handles agent configuration" , async ( ) => {
226229 await using tmp = await tmpdir ( {
227230 init : async ( dir ) => {
228- await Bun . write (
229- path . join ( dir , "opencode.json" ) ,
230- JSON . stringify ( {
231- $schema : "https://opencode.ai/config.json" ,
232- agent : {
233- test_agent : {
234- model : "test/model" ,
235- temperature : 0.7 ,
236- description : "test agent" ,
237- } ,
231+ await writeConfig ( dir , {
232+ $schema : "https://opencode.ai/config.json" ,
233+ agent : {
234+ test_agent : {
235+ model : "test/model" ,
236+ temperature : 0.7 ,
237+ description : "test agent" ,
238238 } ,
239- } ) ,
240- )
239+ } ,
240+ } )
241241 } ,
242242 } )
243243 await Instance . provide ( {
@@ -258,19 +258,16 @@ test("handles agent configuration", async () => {
258258test ( "handles command configuration" , async ( ) => {
259259 await using tmp = await tmpdir ( {
260260 init : async ( dir ) => {
261- await Bun . write (
262- path . join ( dir , "opencode.json" ) ,
263- JSON . stringify ( {
264- $schema : "https://opencode.ai/config.json" ,
265- command : {
266- test_command : {
267- template : "test template" ,
268- description : "test command" ,
269- agent : "test_agent" ,
270- } ,
261+ await writeConfig ( dir , {
262+ $schema : "https://opencode.ai/config.json" ,
263+ command : {
264+ test_command : {
265+ template : "test template" ,
266+ description : "test command" ,
267+ agent : "test_agent" ,
271268 } ,
272- } ) ,
273- )
269+ } ,
270+ } )
274271 } ,
275272 } )
276273 await Instance . provide ( {
@@ -894,6 +891,86 @@ test("migrates legacy write tool to edit permission", async () => {
894891 } )
895892} )
896893
894+ // Managed settings tests
895+ // Note: preload.ts sets OPENCODE_TEST_MANAGED_CONFIG which Global.Path.managedConfig uses
896+
897+ test ( "managed settings override user settings" , async ( ) => {
898+ await using tmp = await tmpdir ( {
899+ init : async ( dir ) => {
900+ await writeConfig ( dir , {
901+ $schema : "https://opencode.ai/config.json" ,
902+ model : "user/model" ,
903+ share : "auto" ,
904+ username : "testuser" ,
905+ } )
906+ } ,
907+ } )
908+
909+ await writeManagedSettings ( {
910+ $schema : "https://opencode.ai/config.json" ,
911+ model : "managed/model" ,
912+ share : "disabled" ,
913+ } )
914+
915+ await Instance . provide ( {
916+ directory : tmp . path ,
917+ fn : async ( ) => {
918+ const config = await Config . get ( )
919+ expect ( config . model ) . toBe ( "managed/model" )
920+ expect ( config . share ) . toBe ( "disabled" )
921+ expect ( config . username ) . toBe ( "testuser" )
922+ } ,
923+ } )
924+ } )
925+
926+ test ( "managed settings override project settings" , async ( ) => {
927+ await using tmp = await tmpdir ( {
928+ init : async ( dir ) => {
929+ await writeConfig ( dir , {
930+ $schema : "https://opencode.ai/config.json" ,
931+ autoupdate : true ,
932+ disabled_providers : [ ] ,
933+ theme : "dark" ,
934+ } )
935+ } ,
936+ } )
937+
938+ await writeManagedSettings ( {
939+ $schema : "https://opencode.ai/config.json" ,
940+ autoupdate : false ,
941+ disabled_providers : [ "openai" ] ,
942+ } )
943+
944+ await Instance . provide ( {
945+ directory : tmp . path ,
946+ fn : async ( ) => {
947+ const config = await Config . get ( )
948+ expect ( config . autoupdate ) . toBe ( false )
949+ expect ( config . disabled_providers ) . toEqual ( [ "openai" ] )
950+ expect ( config . theme ) . toBe ( "dark" )
951+ } ,
952+ } )
953+ } )
954+
955+ test ( "missing managed settings file is not an error" , async ( ) => {
956+ await using tmp = await tmpdir ( {
957+ init : async ( dir ) => {
958+ await writeConfig ( dir , {
959+ $schema : "https://opencode.ai/config.json" ,
960+ model : "user/model" ,
961+ } )
962+ } ,
963+ } )
964+
965+ await Instance . provide ( {
966+ directory : tmp . path ,
967+ fn : async ( ) => {
968+ const config = await Config . get ( )
969+ expect ( config . model ) . toBe ( "user/model" )
970+ } ,
971+ } )
972+ } )
973+
897974test ( "migrates legacy edit tool to edit permission" , async ( ) => {
898975 await using tmp = await tmpdir ( {
899976 init : async ( dir ) => {
0 commit comments