chore(integration): snapshot local main worktree
This commit is contained in:
@@ -43,6 +43,22 @@ describe('auth store', () => {
|
||||
vi.useRealTimers();
|
||||
});
|
||||
|
||||
it('does not treat an orphaned access token as an authenticated identity', async () => {
|
||||
useAuthStore.setState({
|
||||
initialized: true,
|
||||
accessToken: 'orphaned-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
user: null,
|
||||
});
|
||||
|
||||
expect(useAuthStore.getState().isAuthenticated()).toBe(false);
|
||||
await expect(useAuthStore.getState().getValidAccessToken()).resolves.toBeNull();
|
||||
expect(hostApiFetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('logs in with a password through the host api and stores the hydrated session', async () => {
|
||||
hostApiFetchMock
|
||||
.mockResolvedValueOnce({
|
||||
@@ -250,6 +266,137 @@ describe('auth store', () => {
|
||||
});
|
||||
});
|
||||
|
||||
it('rehydrates a missing persisted identity from the current-user projection', async () => {
|
||||
hostApiFetchMock
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
session: {
|
||||
accessToken: 'persisted-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
user: {
|
||||
username: 'restored-user',
|
||||
userId: '42',
|
||||
tenantId: 7,
|
||||
deptId: 'engineering',
|
||||
authorities: ['ROLE_USER'],
|
||||
},
|
||||
moduleAccess: { design: false },
|
||||
});
|
||||
useAuthStore.setState({
|
||||
authBase: 'https://biz.nianxx.cn/auth/',
|
||||
accessToken: 'persisted-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
user: null,
|
||||
});
|
||||
|
||||
await useAuthStore.getState().init();
|
||||
|
||||
expect(useAuthStore.getState()).toMatchObject({
|
||||
initialized: true,
|
||||
loading: false,
|
||||
error: null,
|
||||
user: {
|
||||
username: 'restored-user',
|
||||
userId: '42',
|
||||
tenantId: 7,
|
||||
deptId: 'engineering',
|
||||
authorities: ['ROLE_USER'],
|
||||
},
|
||||
moduleAccess: {
|
||||
programming: true,
|
||||
design: false,
|
||||
learning: true,
|
||||
robot: true,
|
||||
},
|
||||
});
|
||||
expect(useAuthStore.getState().isAuthenticated()).toBe(true);
|
||||
});
|
||||
|
||||
it('clears a synchronized session when no user identity can be recovered', async () => {
|
||||
hostApiFetchMock
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
session: {
|
||||
accessToken: 'orphaned-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
},
|
||||
})
|
||||
.mockResolvedValueOnce({
|
||||
success: true,
|
||||
moduleAccess: { programming: true },
|
||||
})
|
||||
.mockResolvedValueOnce({ success: true });
|
||||
useAuthStore.setState({
|
||||
authBase: 'https://biz.nianxx.cn/auth/',
|
||||
accessToken: 'orphaned-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
user: null,
|
||||
});
|
||||
|
||||
await useAuthStore.getState().init();
|
||||
|
||||
expect(hostApiFetchMock).toHaveBeenNthCalledWith(
|
||||
3,
|
||||
'/api/auth/session/clear',
|
||||
{ method: 'POST' },
|
||||
);
|
||||
expect(useAuthStore.getState()).toMatchObject({
|
||||
initialized: true,
|
||||
loading: false,
|
||||
error: '登录身份无法确认,请重新登录。',
|
||||
accessToken: null,
|
||||
user: null,
|
||||
canRefresh: false,
|
||||
});
|
||||
expect(useAuthStore.getState().isAuthenticated()).toBe(false);
|
||||
});
|
||||
|
||||
it('fails closed when session sync is unavailable and only an orphaned token was persisted', async () => {
|
||||
hostApiFetchMock
|
||||
.mockRejectedValueOnce(new Error('Host API unavailable'))
|
||||
.mockResolvedValueOnce({ success: true });
|
||||
useAuthStore.setState({
|
||||
authBase: 'https://biz.nianxx.cn/auth/',
|
||||
accessToken: 'orphaned-access-token',
|
||||
tokenType: 'Bearer',
|
||||
expiresAt: Date.now() + 60_000,
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
user: null,
|
||||
});
|
||||
|
||||
await useAuthStore.getState().init();
|
||||
|
||||
expect(hostApiFetchMock).toHaveBeenNthCalledWith(
|
||||
2,
|
||||
'/api/auth/session/clear',
|
||||
{ method: 'POST' },
|
||||
);
|
||||
expect(useAuthStore.getState()).toMatchObject({
|
||||
initialized: true,
|
||||
loading: false,
|
||||
error: '登录身份无法确认,请重新登录。',
|
||||
accessToken: null,
|
||||
user: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('clears restored auth when the current-user policy lookup is unauthorized', async () => {
|
||||
hostApiFetchMock
|
||||
.mockResolvedValueOnce({
|
||||
@@ -939,6 +1086,13 @@ describe('auth store', () => {
|
||||
lastActiveAt: Date.now(),
|
||||
canRefresh: true,
|
||||
legacyRefreshToken: null,
|
||||
user: {
|
||||
username: 'zhangsan',
|
||||
userId: '1',
|
||||
tenantId: null,
|
||||
deptId: null,
|
||||
authorities: [],
|
||||
},
|
||||
});
|
||||
|
||||
await expect(useAuthStore.getState().refreshSession()).resolves.toBe('new-access-token');
|
||||
@@ -970,6 +1124,13 @@ describe('auth store', () => {
|
||||
lastActiveAt,
|
||||
canRefresh: true,
|
||||
legacyRefreshToken: null,
|
||||
user: {
|
||||
username: 'zhangsan',
|
||||
userId: '1',
|
||||
tenantId: null,
|
||||
deptId: null,
|
||||
authorities: [],
|
||||
},
|
||||
});
|
||||
|
||||
await useAuthStore.getState().maintainSession();
|
||||
|
||||
Reference in New Issue
Block a user