Skip to content

Commit 1941e30

Browse files
fix(config): treat empty env vars as unset in loadConfig
Empty or whitespace-only TESTSPRITE_API_URL / TESTSPRITE_API_KEY values (e.g. export TESTSPRITE_API_URL= in a shell profile) no longer short-circuit the resolution chain. Runtime commands now fall through to the credentials file and prod default, matching auth configure behavior. Also normalize the auth whoami dry-run endpoint resolution path.
1 parent 18f6e6e commit 1941e30

3 files changed

Lines changed: 32 additions & 3 deletions

File tree

src/commands/auth.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,8 @@ export async function runWhoami(opts: CommonOptions, deps: AuthDeps = {}): Promi
191191
// displayed URL always matches where requests actually go (dogfood L1788).
192192
let resolvedEndpoint: string;
193193
if (opts.dryRun) {
194-
resolvedEndpoint = opts.endpointUrl ?? env.TESTSPRITE_API_URL ?? 'https://api.testsprite.com';
194+
const envApiUrl = env.TESTSPRITE_API_URL?.trim() || undefined;
195+
resolvedEndpoint = opts.endpointUrl ?? envApiUrl ?? 'https://api.testsprite.com';
195196
} else {
196197
const credentialsPath = deps.credentialsPath ?? defaultCredentialsPath();
197198
const config = loadConfig({

src/lib/config.test.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,28 @@ describe('loadConfig', () => {
7676
expect(config.apiUrl).toBe('https://from-file.example.com');
7777
});
7878

79+
it('treats empty / whitespace TESTSPRITE_API_URL as unset (falls through to profile)', () => {
80+
writeProfile(
81+
'default',
82+
{ apiKey: 'sk-file', apiUrl: 'https://api.example.com:8443' },
83+
{ path: credentialsPath },
84+
);
85+
const config = loadConfig({
86+
env: { TESTSPRITE_API_URL: ' ' },
87+
credentialsPath,
88+
});
89+
expect(config.apiUrl).toBe('https://api.example.com:8443');
90+
});
91+
92+
it('treats empty / whitespace TESTSPRITE_API_KEY as unset (falls through to profile)', () => {
93+
writeProfile('default', { apiKey: 'sk-file' }, { path: credentialsPath });
94+
const config = loadConfig({
95+
env: { TESTSPRITE_API_KEY: '' },
96+
credentialsPath,
97+
});
98+
expect(config.apiKey).toBe('sk-file');
99+
});
100+
79101
it('reads the requested profile, not just default', () => {
80102
writeProfile('dev', { apiKey: 'sk-dev' }, { path: credentialsPath });
81103
const config = loadConfig({ profile: 'dev', env: {}, credentialsPath });

src/lib/config.ts

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,15 @@ export function loadConfig(options: LoadConfigOptions = {}): Config {
3838
const credentialsPath = options.credentialsPath ?? defaultCredentialsPath();
3939
const fileEntry = readProfile(profile, { path: credentialsPath });
4040

41+
// Empty / whitespace-only env vars are treated as unset so they do not
42+
// short-circuit the `??` chain (e.g. `export TESTSPRITE_API_URL=` in a shell
43+
// profile). Matches the normalization in auth configure and init/setup.
44+
const envApiUrl = env.TESTSPRITE_API_URL?.trim() || undefined;
45+
const envApiKey = env.TESTSPRITE_API_KEY?.trim() || undefined;
46+
4147
return {
42-
apiUrl: options.endpointUrl ?? env.TESTSPRITE_API_URL ?? fileEntry?.apiUrl ?? DEFAULT_API_URL,
43-
apiKey: env.TESTSPRITE_API_KEY ?? fileEntry?.apiKey,
48+
apiUrl: options.endpointUrl ?? envApiUrl ?? fileEntry?.apiUrl ?? DEFAULT_API_URL,
49+
apiKey: envApiKey ?? fileEntry?.apiKey,
4450
profile,
4551
};
4652
}

0 commit comments

Comments
 (0)