AI Implementation feature(842): First-Start Create Admin Modal 1.01 (#4)

This commit was merged in pull request #4.
This commit is contained in:
2026-07-21 15:32:51 +00:00
parent 960516a7bc
commit 6feaf08bdb
17 changed files with 834 additions and 217 deletions
+118
View File
@@ -0,0 +1,118 @@
import {
STORAGE_KEY,
readStoredSession,
writeStoredSession,
clearStoredSession,
StoredSession,
} from '../../frontend/src/app/core/services/auth.session-storage';
import { of, throwError } from 'rxjs';
function makeStorage(initial: Record<string, string> = {}): {
store: Record<string, string>;
getItem: jest.Mock;
setItem: jest.Mock;
removeItem: jest.Mock;
} {
const store = { ...initial };
return {
store,
getItem: jest.fn((k: string) => (k in store ? store[k] : null)),
setItem: jest.fn((k: string, v: string) => {
store[k] = v;
}),
removeItem: jest.fn((k: string) => {
delete store[k];
}),
};
}
const adminSession: StoredSession = {
token: 'tok-1',
user: { id: 'u1', username: 'alice', role: 'admin' },
};
describe('auth.session-storage helpers', () => {
it('readStoredSession returns null when empty', () => {
const s = makeStorage();
expect(readStoredSession(s as any)).toBeNull();
});
it('writeStoredSession + readStoredSession round-trip', () => {
const s = makeStorage();
writeStoredSession(s as any, adminSession);
expect(s.setItem).toHaveBeenCalledWith(STORAGE_KEY, JSON.stringify(adminSession));
expect(readStoredSession(s as any)).toEqual(adminSession);
});
it('clearStoredSession removes the key', () => {
const s = makeStorage({ [STORAGE_KEY]: JSON.stringify(adminSession) });
clearStoredSession(s as any);
expect(s.removeItem).toHaveBeenCalledWith(STORAGE_KEY);
expect(readStoredSession(s as any)).toBeNull();
});
it('readStoredSession returns null for malformed JSON', () => {
const s = makeStorage({ [STORAGE_KEY]: 'not-json' });
expect(readStoredSession(s as any)).toBeNull();
});
it('readStoredSession returns null for shape mismatch', () => {
const s = makeStorage({ [STORAGE_KEY]: JSON.stringify({ foo: 'bar' }) });
expect(readStoredSession(s as any)).toBeNull();
});
});
describe('restoreSession refresh flow (pure)', () => {
it('successful refresh updates state and persists new token', async () => {
const stored = makeStorage({ [STORAGE_KEY]: JSON.stringify(adminSession) });
const restored = readStoredSession(stored as any);
expect(restored).toEqual(adminSession);
const httpPost = jest.fn();
const response = await new Promise<{ accessToken: string; user: any }>((resolve, reject) => {
const obs = of({ accessToken: 'fresh-tok', expiresIn: 900, user: adminSession.user });
httpPost.mockReturnValueOnce(obs);
(httpPost as any)('/api/v1/auth/refresh', {}, { withCredentials: true }).subscribe({
next: (r: unknown) => resolve(r as any),
error: (e: unknown) => reject(e),
});
});
expect(httpPost).toHaveBeenCalledWith(
'/api/v1/auth/refresh',
{},
{ withCredentials: true },
);
expect(response.accessToken).toBe('fresh-tok');
writeStoredSession(stored as any, {
token: response.accessToken,
user: response.user,
});
expect(stored.store[STORAGE_KEY]).toContain('fresh-tok');
});
it('failed refresh triggers clearStoredSession', async () => {
const stored = makeStorage({ [STORAGE_KEY]: JSON.stringify(adminSession) });
const httpPost = jest.fn();
let errored = false;
try {
await new Promise((resolve, reject) => {
httpPost.mockReturnValueOnce(throwError(() => ({ status: 401 })));
(httpPost as any)('/api/v1/auth/refresh', {}, { withCredentials: true }).subscribe({
next: () => resolve(undefined),
error: (e: unknown) => reject(e),
});
});
} catch {
errored = true;
}
expect(errored).toBe(true);
clearStoredSession(stored as any);
expect(stored.removeItem).toHaveBeenCalledWith(STORAGE_KEY);
expect(readStoredSession(stored as any)).toBeNull();
});
});