-
Notifications
You must be signed in to change notification settings - Fork 5
Expand file tree
/
Copy pathenv.test.ts
More file actions
239 lines (213 loc) · 9.56 KB
/
Copy pathenv.test.ts
File metadata and controls
239 lines (213 loc) · 9.56 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
// Copyright (c) 2025 ObjectStack. Licensed under the Apache-2.0 license.
import { afterEach, describe, expect, it, vi } from 'vitest';
import {
_resetEnvDeprecationWarnings,
readEnvWithDeprecation,
resolveAllowDegradedTenancy,
resolveSearchPinyinEnabled,
isMcpServerEnabled,
resolveMcpStdioAutoStart,
} from './env.js';
describe('readEnvWithDeprecation', () => {
const originalPreferred = process.env.OS_TEST_FOO;
const originalLegacy = process.env.TEST_FOO;
afterEach(() => {
if (originalPreferred === undefined) delete process.env.OS_TEST_FOO;
else process.env.OS_TEST_FOO = originalPreferred;
if (originalLegacy === undefined) delete process.env.TEST_FOO;
else process.env.TEST_FOO = originalLegacy;
_resetEnvDeprecationWarnings();
vi.restoreAllMocks();
});
it('returns the preferred OS_ value when set and stays silent', () => {
process.env.OS_TEST_FOO = 'os-value';
process.env.TEST_FOO = 'legacy-value';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO')).toBe('os-value');
expect(warn).not.toHaveBeenCalled();
});
it('falls back to the legacy alias and warns exactly once per process', () => {
delete process.env.OS_TEST_FOO;
process.env.TEST_FOO = 'legacy-value';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO')).toBe('legacy-value');
expect(readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO')).toBe('legacy-value');
expect(warn).toHaveBeenCalledTimes(1);
expect(String(warn.mock.calls[0][0])).toContain('TEST_FOO');
expect(String(warn.mock.calls[0][0])).toContain('OS_TEST_FOO');
expect(String(warn.mock.calls[0][0])).toContain('deprecated');
});
it('returns the legacy value without warning when silent is set', () => {
delete process.env.OS_TEST_FOO;
process.env.TEST_FOO = 'legacy-value';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(
readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO', { silent: true }),
).toBe('legacy-value');
expect(warn).not.toHaveBeenCalled();
});
it('returns undefined and does not warn when neither var is set', () => {
delete process.env.OS_TEST_FOO;
delete process.env.TEST_FOO;
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO')).toBeUndefined();
expect(warn).not.toHaveBeenCalled();
});
it('treats empty string as set (operator opt-in to blank value)', () => {
process.env.OS_TEST_FOO = '';
process.env.TEST_FOO = 'legacy-value';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(readEnvWithDeprecation('OS_TEST_FOO', 'TEST_FOO')).toBe('');
expect(warn).not.toHaveBeenCalled();
});
it('checks legacy aliases in order and warns for the matched one', () => {
const originalAlt = process.env.ALT_TEST_FOO;
try {
delete process.env.OS_TEST_FOO;
delete process.env.TEST_FOO;
process.env.ALT_TEST_FOO = 'alt-value';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(
readEnvWithDeprecation('OS_TEST_FOO', ['TEST_FOO', 'ALT_TEST_FOO']),
).toBe('alt-value');
expect(warn).toHaveBeenCalledTimes(1);
expect(String(warn.mock.calls[0][0])).toContain('ALT_TEST_FOO');
} finally {
if (originalAlt === undefined) delete process.env.ALT_TEST_FOO;
else process.env.ALT_TEST_FOO = originalAlt;
}
});
it('first legacy alias wins when multiple are set', () => {
const originalAlt = process.env.ALT_TEST_FOO;
try {
delete process.env.OS_TEST_FOO;
process.env.TEST_FOO = 'first-legacy';
process.env.ALT_TEST_FOO = 'second-legacy';
const warn = vi.spyOn(console, 'warn').mockImplementation(() => {});
expect(
readEnvWithDeprecation('OS_TEST_FOO', ['TEST_FOO', 'ALT_TEST_FOO']),
).toBe('first-legacy');
expect(warn).toHaveBeenCalledTimes(1);
expect(String(warn.mock.calls[0][0])).toContain('TEST_FOO');
} finally {
if (originalAlt === undefined) delete process.env.ALT_TEST_FOO;
else process.env.ALT_TEST_FOO = originalAlt;
}
});
});
describe('resolveAllowDegradedTenancy (ADR-0093 D5)', () => {
const original = process.env.OS_ALLOW_DEGRADED_TENANCY;
afterEach(() => {
if (original === undefined) delete process.env.OS_ALLOW_DEGRADED_TENANCY;
else process.env.OS_ALLOW_DEGRADED_TENANCY = original;
});
it('defaults OFF (unset → fail fast)', () => {
delete process.env.OS_ALLOW_DEGRADED_TENANCY;
expect(resolveAllowDegradedTenancy()).toBe(false);
});
it('accepts truthy opt-in values case-insensitively', () => {
for (const v of ['1', 'true', 'TRUE', 'on', 'Yes']) {
process.env.OS_ALLOW_DEGRADED_TENANCY = v;
expect(resolveAllowDegradedTenancy()).toBe(true);
}
});
it('treats anything else as off', () => {
for (const v of ['0', 'false', 'off', 'no', '', 'maybe']) {
process.env.OS_ALLOW_DEGRADED_TENANCY = v;
expect(resolveAllowDegradedTenancy()).toBe(false);
}
});
});
describe('resolveSearchPinyinEnabled (#2486)', () => {
const original = process.env.OS_SEARCH_PINYIN_ENABLED;
afterEach(() => {
if (original === undefined) delete process.env.OS_SEARCH_PINYIN_ENABLED;
else process.env.OS_SEARCH_PINYIN_ENABLED = original;
});
it('defaults OFF with no env and no locales', () => {
delete process.env.OS_SEARCH_PINYIN_ENABLED;
expect(resolveSearchPinyinEnabled()).toBe(false);
expect(resolveSearchPinyinEnabled({ locales: [] })).toBe(false);
expect(resolveSearchPinyinEnabled({ locales: ['en', 'ja-JP'] })).toBe(false);
});
it('derives ON from any configured zh-* locale when env is unset', () => {
delete process.env.OS_SEARCH_PINYIN_ENABLED;
for (const locales of [['zh-CN'], ['en', 'zh-TW'], ['zh'], ['ZH-hans'], ['en', 'zh_CN']]) {
expect(resolveSearchPinyinEnabled({ locales })).toBe(true);
}
expect(resolveSearchPinyinEnabled({ locales: ['zhx-nonsense'] })).toBe(false);
});
it('explicit env overrides the locale-derived default in both directions', () => {
process.env.OS_SEARCH_PINYIN_ENABLED = 'false';
expect(resolveSearchPinyinEnabled({ locales: ['zh-CN'] })).toBe(false);
process.env.OS_SEARCH_PINYIN_ENABLED = 'true';
expect(resolveSearchPinyinEnabled({ locales: ['en'] })).toBe(true);
expect(resolveSearchPinyinEnabled()).toBe(true);
});
it('accepts truthy values case-insensitively; anything else is off', () => {
for (const v of ['1', 'true', 'TRUE', 'on', 'Yes']) {
process.env.OS_SEARCH_PINYIN_ENABLED = v;
expect(resolveSearchPinyinEnabled()).toBe(true);
}
for (const v of ['0', 'false', 'off', 'no', 'maybe']) {
process.env.OS_SEARCH_PINYIN_ENABLED = v;
expect(resolveSearchPinyinEnabled()).toBe(false);
}
});
});
describe('MCP switches — HTTP surface vs stdio auto-start are decoupled (#3167)', () => {
const origServer = process.env.OS_MCP_SERVER_ENABLED;
const origServerLegacy = process.env.MCP_SERVER_ENABLED;
const origStdio = process.env.OS_MCP_STDIO_ENABLED;
const restore = (key: string, val: string | undefined) => {
if (val === undefined) delete process.env[key];
else process.env[key] = val;
};
afterEach(() => {
restore('OS_MCP_SERVER_ENABLED', origServer);
restore('MCP_SERVER_ENABLED', origServerLegacy);
restore('OS_MCP_STDIO_ENABLED', origStdio);
});
it('isMcpServerEnabled (HTTP surface): default-on, only explicit falsy opts out', () => {
delete process.env.OS_MCP_SERVER_ENABLED;
expect(isMcpServerEnabled()).toBe(true);
for (const v of ['false', '0', 'off', 'no', 'FALSE']) {
process.env.OS_MCP_SERVER_ENABLED = v;
expect(isMcpServerEnabled(), `${v} should opt out`).toBe(false);
}
for (const v of ['true', '1', 'anything']) {
process.env.OS_MCP_SERVER_ENABLED = v;
expect(isMcpServerEnabled(), `${v} keeps HTTP on`).toBe(true);
}
});
it('stdio auto-start: default OFF when nothing is set', () => {
delete process.env.OS_MCP_SERVER_ENABLED;
delete process.env.OS_MCP_STDIO_ENABLED;
expect(resolveMcpStdioAutoStart()).toEqual({ enabled: false, viaDeprecatedAlias: false });
});
it('stdio auto-start: canonical OS_MCP_STDIO_ENABLED (truthy, no deprecation)', () => {
delete process.env.OS_MCP_SERVER_ENABLED;
for (const v of ['1', 'true', 'on', 'yes', 'TRUE']) {
process.env.OS_MCP_STDIO_ENABLED = v;
expect(resolveMcpStdioAutoStart(), v).toEqual({ enabled: true, viaDeprecatedAlias: false });
}
});
it('stdio auto-start: legacy OS_MCP_SERVER_ENABLED=true still starts it, flagged deprecated', () => {
delete process.env.OS_MCP_STDIO_ENABLED;
process.env.OS_MCP_SERVER_ENABLED = 'true';
expect(resolveMcpStdioAutoStart()).toEqual({ enabled: true, viaDeprecatedAlias: true });
});
it('stdio auto-start: OS_MCP_SERVER_ENABLED=false (or other) never starts stdio — no footgun', () => {
delete process.env.OS_MCP_STDIO_ENABLED;
for (const v of ['false', '0', 'off', '1', 'on', 'yes']) {
process.env.OS_MCP_SERVER_ENABLED = v;
// Only the literal `true` was ever the legacy stdio trigger.
expect(resolveMcpStdioAutoStart().enabled, `server=${v}`).toBe(false);
}
});
it('canonical switch wins over the legacy alias (no deprecation flag)', () => {
process.env.OS_MCP_STDIO_ENABLED = 'true';
process.env.OS_MCP_SERVER_ENABLED = 'true';
expect(resolveMcpStdioAutoStart()).toEqual({ enabled: true, viaDeprecatedAlias: false });
});
});