qwen-code/packages/web-shell/client/utils/sessionPath.test.ts
ytahdn 2ab3681157
fix(web-shell): preserve token and base path in session URLs (#7926)
* fix(web-shell): preserve session URL context

* fix(web-shell): keep daemon token out of the session URL (#7926)

Restore the daemon token stripping that was dropped alongside the
base-path fix: removeDaemonTokenFromUrl() on startup and the ?token=
delete in replaceStandaloneSessionUrl. The ?token= query path is still
supported for backward compatibility, so without stripping it leaks into
the address bar, history, access logs, and Referer headers.

Also extract the session pathname building into buildSessionPathname()
with unit tests covering root/sub-path deployments, the no-session case,
trailing slashes, and id encoding.

* fix(web-shell): anchor session URL parser to agree with writer (#7926)

Extract parseSessionId() next to buildSessionPathname() and anchor it to the last /session/<id> segment so the parser agrees with the greedy writer. Previously a base path ending in a session segment produced /app/session/session/<id>, which the first-match parser read back as the literal id "session". Add round-trip and trailing-slash coverage.

* test(web-shell): cover parseSessionId malformed-encoding catch branch (#7926)

* fix(web-shell): preserve base path in split-view URL (#7926)

---------

Co-authored-by: 钉萁 <dingqi.jww@alibaba-inc.com>
Co-authored-by: qwen-code-dev-bot <qwen-code-dev@service.alibaba.com>
Co-authored-by: Shaojin Wen <shaojin.wensj@alibaba-inc.com>
2026-07-29 06:12:37 +00:00

98 lines
3.1 KiB
TypeScript

/**
* @license
* Copyright 2026 Qwen Team
* SPDX-License-Identifier: Apache-2.0
*/
import { describe, expect, it } from 'vitest';
import { buildSessionPathname, parseSessionId } from './sessionPath';
describe('buildSessionPathname', () => {
it('replaces an existing session segment at the root', () => {
expect(buildSessionPathname('/session/old', 'new')).toBe('/session/new');
});
it('preserves a sub-path deployment base', () => {
expect(buildSessionPathname('/app/session/old', 'new')).toBe(
'/app/session/new',
);
});
it('appends a session under a base path with no existing session', () => {
expect(buildSessionPathname('/app', 'new')).toBe('/app/session/new');
});
it('appends a session at the root when there is no existing session', () => {
expect(buildSessionPathname('/', 'new')).toBe('/session/new');
});
it('strips a trailing slash from the base path', () => {
expect(buildSessionPathname('/app/', 'new')).toBe('/app/session/new');
});
it('strips a trailing slash after an existing session id', () => {
expect(buildSessionPathname('/session/old/', 'new')).toBe('/session/new');
expect(buildSessionPathname('/app/session/old/', 'new')).toBe(
'/app/session/new',
);
});
it('encodes the session id', () => {
expect(buildSessionPathname('/', 'a b/c')).toBe('/session/a%20b%2Fc');
});
it('returns the base path when no session is given', () => {
expect(buildSessionPathname('/app/session/old', undefined)).toBe('/app');
});
it('returns "/" when no session is given at the root', () => {
expect(buildSessionPathname('/', undefined)).toBe('/');
expect(buildSessionPathname('/session/old', undefined)).toBe('/');
});
});
describe('parseSessionId', () => {
it('reads the session id at the root', () => {
expect(parseSessionId('/session/abc')).toBe('abc');
});
it('reads the last session segment under a base path', () => {
expect(parseSessionId('/app/session/abc')).toBe('abc');
});
it('ignores a trailing slash', () => {
expect(parseSessionId('/session/abc/')).toBe('abc');
});
it('decodes the session id', () => {
expect(parseSessionId('/session/a%20b%2Fc')).toBe('a b/c');
});
it('returns undefined for malformed percent-encoding', () => {
expect(parseSessionId('/session/%E0%A4%A')).toBeUndefined();
});
it('returns undefined when there is no session segment', () => {
expect(parseSessionId('/')).toBeUndefined();
expect(parseSessionId('/app')).toBeUndefined();
});
it('returns undefined for an empty session id', () => {
expect(parseSessionId('/app/session/')).toBeUndefined();
});
});
describe('build/parse round-trip', () => {
it('reads back the written session id', () => {
for (const base of ['/', '/app', '/app/', '/app/session/old']) {
expect(parseSessionId(buildSessionPathname(base, 'real-id'))).toBe(
'real-id',
);
}
});
it('reads back the written id when the base path ends in a session segment', () => {
const pathname = buildSessionPathname('/app/session/', 'real-id');
expect(parseSessionId(pathname)).toBe('real-id');
});
});