11import { afterEach , beforeEach , describe , expect , it } from 'bun:test' ;
2- import { mkdtempSync , readFileSync , rmSync , writeFileSync } from 'node:fs' ;
2+ import { mkdirSync , mkdtempSync , readFileSync , rmSync , writeFileSync } from 'node:fs' ;
33import { tmpdir } from 'node:os' ;
44import path from 'node:path' ;
55
@@ -10,18 +10,27 @@ import { loadStudioConfig, saveStudioConfig } from '../../../src/commands/result
1010
1111describe ( 'loadStudioConfig' , ( ) => {
1212 let tempDir : string ;
13+ let previousAgentvHome : string | undefined ;
1314
1415 beforeEach ( ( ) => {
1516 tempDir = mkdtempSync ( path . join ( tmpdir ( ) , 'studio-config-' ) ) ;
17+ previousAgentvHome = process . env . AGENTV_HOME ;
18+ process . env . AGENTV_HOME = path . join ( tempDir , 'home' ) ;
1619 } ) ;
1720
1821 afterEach ( ( ) => {
22+ if ( previousAgentvHome === undefined ) {
23+ process . env . AGENTV_HOME = undefined ;
24+ } else {
25+ process . env . AGENTV_HOME = previousAgentvHome ;
26+ }
1927 rmSync ( tempDir , { recursive : true , force : true } ) ;
2028 } ) ;
2129
2230 it ( 'returns defaults when no config.yaml exists' , ( ) => {
2331 const config = loadStudioConfig ( tempDir ) ;
2432 expect ( config . threshold ) . toBe ( DEFAULT_THRESHOLD ) ;
33+ expect ( config . appName ) . toBe ( 'agentv' ) ;
2534 } ) ;
2635
2736 it . each ( [
@@ -53,6 +62,48 @@ describe('loadStudioConfig', () => {
5362 expect ( config . threshold ) . toBe ( 0.9 ) ;
5463 } ) ;
5564
65+ it ( 'reads dashboard.app_name for white labelling' , ( ) => {
66+ writeFileSync ( path . join ( tempDir , 'config.yaml' ) , 'dashboard:\n app_name: ai evals\n' ) ;
67+ expect ( loadStudioConfig ( tempDir ) . appName ) . toBe ( 'ai evals' ) ;
68+ } ) ;
69+
70+ it ( 'ignores blank dashboard.app_name' , ( ) => {
71+ writeFileSync ( path . join ( tempDir , 'config.yaml' ) , 'dashboard:\n app_name: " "\n' ) ;
72+ expect ( loadStudioConfig ( tempDir ) . appName ) . toBe ( 'agentv' ) ;
73+ } ) ;
74+
75+ it ( 'falls back to global config.yaml for dashboard settings' , ( ) => {
76+ const homeDir = process . env . AGENTV_HOME ;
77+ if ( ! homeDir ) throw new Error ( 'AGENTV_HOME test setup failed' ) ;
78+ mkdirSync ( homeDir , { recursive : true } ) ;
79+ writeFileSync (
80+ path . join ( homeDir , 'config.yaml' ) ,
81+ 'dashboard:\n app_name: ai evals\n threshold: 0.6\n' ,
82+ ) ;
83+
84+ const config = loadStudioConfig ( tempDir ) ;
85+ expect ( config . appName ) . toBe ( 'ai evals' ) ;
86+ expect ( config . threshold ) . toBe ( 0.6 ) ;
87+ } ) ;
88+
89+ it ( 'prefers local config.yaml over global config.yaml' , ( ) => {
90+ const homeDir = process . env . AGENTV_HOME ;
91+ if ( ! homeDir ) throw new Error ( 'AGENTV_HOME test setup failed' ) ;
92+ mkdirSync ( homeDir , { recursive : true } ) ;
93+ writeFileSync (
94+ path . join ( homeDir , 'config.yaml' ) ,
95+ 'dashboard:\n app_name: ai evals\n threshold: 0.6\n' ,
96+ ) ;
97+ writeFileSync (
98+ path . join ( tempDir , 'config.yaml' ) ,
99+ 'dashboard:\n app_name: local evals\n threshold: 0.9\n' ,
100+ ) ;
101+
102+ const config = loadStudioConfig ( tempDir ) ;
103+ expect ( config . appName ) . toBe ( 'local evals' ) ;
104+ expect ( config . threshold ) . toBe ( 0.9 ) ;
105+ } ) ;
106+
56107 it ( 'falls back to legacy studio section when dashboard has no threshold' , ( ) => {
57108 writeFileSync (
58109 path . join ( tempDir , 'config.yaml' ) ,
@@ -94,12 +145,20 @@ describe('loadStudioConfig', () => {
94145
95146describe ( 'saveStudioConfig' , ( ) => {
96147 let tempDir : string ;
148+ let previousAgentvHome : string | undefined ;
97149
98150 beforeEach ( ( ) => {
99151 tempDir = mkdtempSync ( path . join ( tmpdir ( ) , 'studio-config-' ) ) ;
152+ previousAgentvHome = process . env . AGENTV_HOME ;
153+ process . env . AGENTV_HOME = path . join ( tempDir , 'home' ) ;
100154 } ) ;
101155
102156 afterEach ( ( ) => {
157+ if ( previousAgentvHome === undefined ) {
158+ process . env . AGENTV_HOME = undefined ;
159+ } else {
160+ process . env . AGENTV_HOME = previousAgentvHome ;
161+ }
103162 rmSync ( tempDir , { recursive : true , force : true } ) ;
104163 } ) ;
105164
@@ -108,21 +167,23 @@ describe('saveStudioConfig', () => {
108167 path . join ( tempDir , 'config.yaml' ) ,
109168 'required_version: ">=4.2.0"\neval_patterns:\n - "**/*.eval.yaml"\n' ,
110169 ) ;
111- saveStudioConfig ( tempDir , { threshold : 0.9 } ) ;
170+ saveStudioConfig ( tempDir , { threshold : 0.9 , appName : 'ai evals' } ) ;
112171
113172 const raw = readFileSync ( path . join ( tempDir , 'config.yaml' ) , 'utf-8' ) ;
114173 const parsed = parseYaml ( raw ) as Record < string , unknown > ;
115174 expect ( parsed . required_version ) . toBe ( '>=4.2.0' ) ;
116175 expect ( parsed . eval_patterns ) . toEqual ( [ '**/*.eval.yaml' ] ) ;
117176 expect ( ( parsed . dashboard as Record < string , unknown > ) . threshold ) . toBe ( 0.9 ) ;
177+ expect ( ( parsed . dashboard as Record < string , unknown > ) . app_name ) . toBe ( 'ai evals' ) ;
178+ expect ( ( parsed . dashboard as Record < string , unknown > ) . appName ) . toBeUndefined ( ) ;
118179 } ) ;
119180
120181 it ( 'writes canonical dashboard.threshold and removes legacy threshold fields on save' , ( ) => {
121182 writeFileSync (
122183 path . join ( tempDir , 'config.yaml' ) ,
123184 'required_version: ">=4.2.0"\npass_threshold: 0.8\ndashboard:\n pass_threshold: 0.6\nstudio:\n theme: dark\n pass_threshold: 0.5\n' ,
124185 ) ;
125- saveStudioConfig ( tempDir , { threshold : 0.7 } ) ;
186+ saveStudioConfig ( tempDir , { threshold : 0.7 , appName : 'agentv' } ) ;
126187
127188 const raw = readFileSync ( path . join ( tempDir , 'config.yaml' ) , 'utf-8' ) ;
128189 const parsed = parseYaml ( raw ) as Record < string , unknown > ;
@@ -133,10 +194,12 @@ describe('saveStudioConfig', () => {
133194 expect ( dashboard . theme ) . toBe ( 'dark' ) ;
134195 expect ( dashboard . pass_threshold ) . toBeUndefined ( ) ;
135196 expect ( dashboard . threshold ) . toBe ( 0.7 ) ;
197+ expect ( dashboard . app_name ) . toBe ( 'agentv' ) ;
198+ expect ( dashboard . appName ) . toBeUndefined ( ) ;
136199 } ) ;
137200
138201 it ( 'creates config.yaml when it does not exist' , ( ) => {
139- saveStudioConfig ( tempDir , { threshold : 0.6 } ) ;
202+ saveStudioConfig ( tempDir , { threshold : 0.6 , appName : 'agentv' } ) ;
140203
141204 const raw = readFileSync ( path . join ( tempDir , 'config.yaml' ) , 'utf-8' ) ;
142205 const parsed = parseYaml ( raw ) as Record < string , unknown > ;
@@ -145,7 +208,7 @@ describe('saveStudioConfig', () => {
145208
146209 it ( 'creates directory if it does not exist' , ( ) => {
147210 const nestedDir = path . join ( tempDir , 'nested' , '.agentv' ) ;
148- saveStudioConfig ( nestedDir , { threshold : 0.5 } ) ;
211+ saveStudioConfig ( nestedDir , { threshold : 0.5 , appName : 'agentv' } ) ;
149212
150213 const raw = readFileSync ( path . join ( nestedDir , 'config.yaml' ) , 'utf-8' ) ;
151214 const parsed = parseYaml ( raw ) as Record < string , unknown > ;
0 commit comments