fix(account): Login bei bereits aktiver Sitzung, showModal-Fehler abfangen
- login(): bei user_session_already_exists aktuelle Sitzung beenden und genau einmal neu versuchen; Meldung für den Restfall in errorMessage - requireLogin(): schlägt showModal fehl, wird aufgeräumt und der Fehler geworfen, damit spätere Aufrufe nicht an einem toten Promise hängen Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
1 parent
10c55ca933
commit
a147d3b496
6 files changed
+143
-9
No files matched your search
@@ -106,6 +106,82 @@ describe('login', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('login bei bereits aktiver Sitzung (user_session_already_exists)', () => {
|
||||
const existing = () => fail(401, 'user_session_already_exists', 'Creation of a session is prohibited when a session is active.');
|
||||
|
||||
it('löscht die aktuelle Sitzung, versucht es einmal neu und liefert den Nutzer', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.deleteSession).toHaveBeenCalledWith({ sessionId: 'current' });
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenLastCalledWith({ email: 'a@b.de', password: 'geheim123' });
|
||||
expect(auth.currentUserId()).toBe('u1');
|
||||
});
|
||||
|
||||
it('ignoriert 401 beim Löschen (Sitzung war schon weg) und versucht es trotzdem neu', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockRejectedValue(fail(401, 'general_unauthorized_scope'));
|
||||
mocks.get.mockResolvedValue({ $id: 'u1', email: 'a@b.de' });
|
||||
await expect(auth.login('a@b.de', 'geheim123')).resolves.toEqual({ $id: 'u1', email: 'a@b.de' });
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it('scheitert der zweite Versuch auch, fliegt dieser Fehler weiter (keine Endlosschleife)', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.createEmailPasswordSession.mockRejectedValue(existing());
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
await expect(auth.login('a@b.de', 'geheim123')).rejects.toMatchObject({ type: 'user_session_already_exists' });
|
||||
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(2);
|
||||
expect(mocks.get).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('falsche Zugangsdaten im zweiten Versuch kommen als solche an, der alte Nutzer ist dann abgemeldet', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.get.mockResolvedValueOnce({ $id: 'old', email: 'old@b.de' });
|
||||
await auth.getCurrentUser();
|
||||
const listener = vi.fn();
|
||||
auth.onAuthChange(listener);
|
||||
const wrong = fail(401, 'user_invalid_credentials');
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockRejectedValueOnce(wrong);
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
await expect(auth.login('a@b.de', 'falsch123')).rejects.toBe(wrong);
|
||||
expect(auth.currentUserId()).toBeNull();
|
||||
expect(listener).toHaveBeenCalledWith(null);
|
||||
});
|
||||
|
||||
it('scheitert das Löschen anders (z. B. Netz), fliegt dieser Fehler weiter und es gibt keinen zweiten Versuch', async () => {
|
||||
const auth = await freshAuth();
|
||||
const error = new TypeError('Failed to fetch');
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing());
|
||||
mocks.deleteSession.mockRejectedValue(error);
|
||||
await expect(auth.login('a@b.de', 'geheim123')).rejects.toBe(error);
|
||||
expect(mocks.createEmailPasswordSession).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('andere Fehler beim ersten Versuch löschen nichts', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.createEmailPasswordSession.mockRejectedValue(fail(401, 'user_invalid_credentials'));
|
||||
await expect(auth.login('a@b.de', 'falsch123')).rejects.toMatchObject({ type: 'user_invalid_credentials' });
|
||||
expect(mocks.deleteSession).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('register: das anschließende Login räumt eine vorhandene Sitzung ebenfalls ab', async () => {
|
||||
const auth = await freshAuth();
|
||||
mocks.create.mockResolvedValue({});
|
||||
mocks.createEmailPasswordSession.mockRejectedValueOnce(existing()).mockResolvedValueOnce({});
|
||||
mocks.deleteSession.mockResolvedValue({});
|
||||
mocks.get.mockResolvedValue({ $id: 'u2', email: 'neu@b.de' });
|
||||
await expect(auth.register('neu@b.de', 'geheim123')).resolves.toEqual({ $id: 'u2', email: 'neu@b.de' });
|
||||
expect(mocks.deleteSession).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('register', () => {
|
||||
it('legt das Konto an (Name aus der E-Mail) und meldet danach an', async () => {
|
||||
const auth = await freshAuth();
|
||||
|
||||
Reference in new issue
Block a user