This is an automated email from the ASF dual-hosted git repository.
lizhimins pushed a commit to branch rocketmq-studio
in repository https://gitbox.apache.org/repos/asf/rocketmq-dashboard.git
The following commit(s) were added to refs/heads/rocketmq-studio by this push:
new 2f07500ba test(web): cover the auth-session, engine-preference and
data-mode stores (#5650)
2f07500ba is described below
commit 2f07500ba4c734ad885df8f694967f29e89f01d2
Author: Chstercc <[email protected]>
AuthorDate: Sat Oct 10 12:01:11 2026 +0800
test(web): cover the auth-session, engine-preference and data-mode stores
(#5650)
Three store-level test suites from the same author, none of which touched
production code.
- #5650 `test(auth-store): cover session persistence and reload`
- #5657 `test(ai): cover engine preference persistence and migration`
- #5659 `test(data-mode): cover runtime toggles and saved preferences`
3 suites, 15 tests green; prettier and eslint clean.
Folded in #5657, #5659 (same author). Merged through #5650.
---
web/src/services/dataMode.test.ts | 64 +++++++++++++++++++++++++++++++
web/src/stores/authStore.test.ts | 77 +++++++++++++++++++++++++++++++++++++
web/src/stores/engineStore.test.ts | 78 ++++++++++++++++++++++++++++++++++++++
3 files changed, 219 insertions(+)
diff --git a/web/src/services/dataMode.test.ts
b/web/src/services/dataMode.test.ts
new file mode 100644
index 000000000..11a71a2c7
--- /dev/null
+++ b/web/src/services/dataMode.test.ts
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { afterEach, describe, expect, it, vi } from 'vitest';
+
+async function loadDataMode() {
+ vi.resetModules();
+ const { isMockMode } = await import('./dataMode');
+ const { useDataModeStore } = await import('../stores/dataModeStore');
+ return { isMockMode, store: useDataModeStore };
+}
+
+describe('dataMode service', () => {
+ afterEach(() => {
+ vi.unstubAllEnvs();
+ vi.resetModules();
+ localStorage.clear();
+ });
+
+ it.each(['true', 'false'])('reflectsRuntimeTogglesWithViteUseMock_%sTest',
async (envValue) => {
+ vi.stubEnv('VITE_USE_MOCK', envValue);
+ const { isMockMode, store } = await loadDataMode();
+ const initialMode = envValue === 'true';
+
+ expect(isMockMode()).toBe(initialMode);
+
+ store.getState().toggle();
+ expect(isMockMode()).toBe(!initialMode);
+
+ store.getState().toggle();
+ expect(isMockMode()).toBe(initialMode);
+ });
+
+ it.each([
+ ['false', true],
+ ['true', false],
+ ] as const)('usesPersistedModeInsteadOfBuildDefault_%sTest', async
(envValue, persistedMode) => {
+ vi.stubEnv('VITE_USE_MOCK', envValue);
+ localStorage.setItem(
+ 'rocketmq-studio-data-mode',
+ JSON.stringify({ state: { useMock: persistedMode }, version: 0 }),
+ );
+ const { isMockMode, store } = await loadDataMode();
+
+ expect(isMockMode()).toBe(persistedMode);
+
+ store.getState().toggle();
+ expect(isMockMode()).toBe(!persistedMode);
+ });
+});
diff --git a/web/src/stores/authStore.test.ts b/web/src/stores/authStore.test.ts
new file mode 100644
index 000000000..d53c65af2
--- /dev/null
+++ b/web/src/stores/authStore.test.ts
@@ -0,0 +1,77 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { afterEach, describe, expect, it, vi } from 'vitest';
+
+async function loadStore() {
+ vi.resetModules();
+ return (await import('./authStore')).default;
+}
+
+describe('authStore', () => {
+ afterEach(() => {
+ localStorage.clear();
+ vi.resetModules();
+ });
+
+ it('starts without a display identity when storage is empty', async () => {
+ const store = await loadStore();
+
+ expect(store.getState()).toMatchObject({ user: null, userId: null, admin:
null });
+ });
+
+ it('updates and persists the display identity on login without storing a
bearer token', async () => {
+ const store = await loadStore();
+
+ store.getState().login('studio-admin', 7, true);
+
+ expect(store.getState()).toMatchObject({ user: 'studio-admin', userId: 7,
admin: true });
+ expect(localStorage.getItem('rocketmq-studio-user')).toBe('studio-admin');
+ expect(localStorage.getItem('rocketmq-studio-user-id')).toBe('7');
+ expect(localStorage.getItem('rocketmq-studio-user-admin')).toBe('true');
+ expect(localStorage.getItem('token')).toBeNull();
+ });
+
+ it.each([true, false])(
+ 'restores the persisted display identity with admin=%s on reload',
+ async (admin) => {
+ const store = await loadStore();
+ store.getState().login('studio-user', 7, admin);
+
+ const reloadedStore = await loadStore();
+
+ expect(reloadedStore).not.toBe(store);
+ expect(reloadedStore.getState()).toMatchObject({ user: 'studio-user',
userId: 7, admin });
+ },
+ );
+
+ it('clears the display identity and legacy bearer token on logout, including
after reload', async () => {
+ const store = await loadStore();
+ store.getState().login('studio-admin', 7, true);
+ localStorage.setItem('token', 'legacy-token');
+
+ store.getState().logout();
+
+ expect(store.getState()).toMatchObject({ user: null, userId: null, admin:
null });
+ expect(localStorage.getItem('rocketmq-studio-user')).toBeNull();
+ expect(localStorage.getItem('rocketmq-studio-user-id')).toBeNull();
+ expect(localStorage.getItem('rocketmq-studio-user-admin')).toBeNull();
+ expect(localStorage.getItem('token')).toBeNull();
+ const reloadedStore = await loadStore();
+ expect(reloadedStore.getState()).toMatchObject({ user: null, userId: null,
admin: null });
+ });
+});
diff --git a/web/src/stores/engineStore.test.ts
b/web/src/stores/engineStore.test.ts
new file mode 100644
index 000000000..117ba8ca0
--- /dev/null
+++ b/web/src/stores/engineStore.test.ts
@@ -0,0 +1,78 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { afterEach, describe, expect, it, vi } from 'vitest';
+
+const STORAGE_KEY = 'rocketmq-studio-agent-engine';
+
+async function loadStore() {
+ vi.resetModules();
+ return (await import('./engineStore')).useEngineStore;
+}
+
+describe('engineStore', () => {
+ afterEach(() => {
+ localStorage.clear();
+ vi.resetModules();
+ });
+
+ it('defaultsToClaudeCodeWithoutAPersistedPreferenceTest', async () => {
+ const store = await loadStore();
+
+ expect(store.getState().engine).toBe('claude-code');
+ });
+
+ it.each(['claude-code', 'qoder', 'http'] as const)(
+ 'persistsAndRestoresTheSelectedEngine_%sTest',
+ async (engine) => {
+ const store = await loadStore();
+
+ store.getState().setEngine(engine);
+
+ expect(store.getState().engine).toBe(engine);
+ expect(JSON.parse(localStorage.getItem(STORAGE_KEY)!)).toEqual({
+ state: { engine },
+ version: 1,
+ });
+
+ const reloadedStore = await loadStore();
+
+ expect(reloadedStore).not.toBe(store);
+ expect(reloadedStore.getState().engine).toBe(engine);
+ },
+ );
+
+ it.each(['qoder', 'stale-engine'])(
+ 'migratesVersionZeroPreference_%sToClaudeCodeTest',
+ async (engine) => {
+ localStorage.setItem(STORAGE_KEY, JSON.stringify({ state: { engine },
version: 0 }));
+
+ const store = await loadStore();
+
+ expect(store.getState().engine).toBe('claude-code');
+ expect(JSON.parse(localStorage.getItem(STORAGE_KEY)!)).toEqual({
+ state: { engine: 'claude-code' },
+ version: 1,
+ });
+
+ const reloadedStore = await loadStore();
+
+ expect(reloadedStore).not.toBe(store);
+ expect(reloadedStore.getState().engine).toBe('claude-code');
+ },
+ );
+});