UNPKG

@j2blasco/ts-auth

Version:

TypeScript authentication abstraction library that eliminates vendor lock-in and provides mock-free testing for both frontend and backend authentication systems

319 lines (318 loc) 14.7 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.testAuthBackend = testAuthBackend; /** * Helper functions for testing Result types */ function isResultSuccess(result) { try { result.unwrapOrThrow(); return true; } catch { return false; } } function getResultError(result) { try { result.unwrapOrThrow(); throw new Error('Result is not an error'); } catch (error) { return error; } } /** * Comprehensive test suite for IAuthBackend implementations. * Run this against any implementation to verify it satisfies the interface contract. */ function testAuthBackend(authBackend) { describe('IAuthBackend implementation tests', () => { describe('Observable events', () => { it('should have onUserCreated$ Observable', () => { expect(authBackend.onUserCreated$).toBeDefined(); expect(typeof authBackend.onUserCreated$.subscribe).toBe('function'); }); it('should have onUserDeleted$ Observable', () => { expect(authBackend.onUserDeleted$).toBeDefined(); expect(typeof authBackend.onUserDeleted$.subscribe).toBe('function'); }); }); describe('signUpWithEmailPassword', () => { it('should create a new user and return a uid', async () => { const email = `test-${Date.now()}@example.com`; const password = 'testPassword123'; const result = await authBackend.signUpWithEmailPassword({ email, password, }); expect(isResultSuccess(result)).toBe(true); const success = result.unwrapOrThrow(); expect(typeof success.uid).toBe('string'); expect(success.uid.length).toBeGreaterThan(0); }); it('should emit onUserCreated$ event when user is created', async () => { const email = `event-test-${Date.now()}@example.com`; const password = 'testPassword123'; let emittedUid; const subscription = authBackend.onUserCreated$.subscribe((event) => { emittedUid = event.uid; }); const result = await authBackend.signUpWithEmailPassword({ email, password, }); // Give some time for the observable to emit await new Promise((resolve) => setTimeout(resolve, 10)); subscription.unsubscribe(); const success = result.unwrapOrThrow(); expect(emittedUid).toBe(success.uid); }); }); describe('signInWithEmailAndPassword', () => { const testEmail = `signin-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; let testUid; beforeEach(async () => { const result = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = result.unwrapOrThrow().uid; }); it('should sign in with valid credentials', async () => { const result = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: testPassword, }); expect(isResultSuccess(result)).toBe(true); const success = result.unwrapOrThrow(); expect(success.uid).toBe(testUid); expect(typeof success.refreshToken).toBe('string'); expect(typeof success.idToken).toBe('string'); expect(success.refreshToken.length).toBeGreaterThan(0); expect(success.idToken.length).toBeGreaterThan(0); }); it('should return error for invalid email format', async () => { const result = await authBackend.signInWithEmailAndPassword({ email: 'invalid-email', password: testPassword, }); expect(isResultSuccess(result)).toBe(false); const error = getResultError(result); expect(error.code).toBe('invalid-email'); }); it('should return error for non-existent user', async () => { const result = await authBackend.signInWithEmailAndPassword({ email: `nonexistent-${Date.now()}@example.com`, password: testPassword, }); expect(isResultSuccess(result)).toBe(false); const error = getResultError(result); expect(error.code).toBe('user-not-found'); }); it('should return error for wrong password', async () => { const result = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: 'wrongPassword', }); expect(isResultSuccess(result)).toBe(false); const error = getResultError(result); expect(error.code).toBe('wrong-password'); }); }); describe('getUidFromIdToken', () => { const testEmail = `token-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; let testUid; let testIdToken; beforeEach(async () => { const signUpResult = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = signUpResult.unwrapOrThrow().uid; const signInResult = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: testPassword, }); testIdToken = signInResult.unwrapOrThrow().idToken; }); it('should return uid for valid idToken', async () => { const result = await authBackend.getUidFromIdToken(testIdToken); expect(isResultSuccess(result)).toBe(true); const uid = result.unwrapOrThrow(); expect(uid).toBe(testUid); }); it('should return error for invalid idToken', async () => { const result = await authBackend.getUidFromIdToken('invalid-token'); expect(isResultSuccess(result)).toBe(false); }); }); describe('signInWithRefreshToken', () => { const testEmail = `refresh-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; let testUid; let testRefreshToken; beforeEach(async () => { const signUpResult = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = signUpResult.unwrapOrThrow().uid; const signInResult = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: testPassword, }); testRefreshToken = signInResult.unwrapOrThrow().refreshToken; }); it('should sign in with valid refresh token', async () => { const result = await authBackend.signInWithRefreshToken(testRefreshToken); expect(isResultSuccess(result)).toBe(true); const success = result.unwrapOrThrow(); expect(success.uid).toBe(testUid); expect(typeof success.idToken).toBe('string'); expect(success.idToken.length).toBeGreaterThan(0); }); it('should return error for invalid refresh token', async () => { const result = await authBackend.signInWithRefreshToken('invalid-refresh-token'); expect(isResultSuccess(result)).toBe(false); }); }); describe('getUidByEmail', () => { const testEmail = `uidbyemail-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; let testUid; beforeEach(async () => { const result = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = result.unwrapOrThrow().uid; }); it('should return uid for existing email', async () => { const result = await authBackend.getUidByEmail(testEmail); expect(isResultSuccess(result)).toBe(true); const success = result.unwrapOrThrow(); expect(success.uid).toBe(testUid); }); it('should return error for non-existent email', async () => { const result = await authBackend.getUidByEmail(`nonexistent-${Date.now()}@example.com`); expect(isResultSuccess(result)).toBe(false); const error = getResultError(result); expect(error.code).toBe('email-not-found'); }); }); describe('changeEmail', () => { const testEmail = `changeemail-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; const newEmail = `new-${Date.now()}@example.com`; let testUid; beforeEach(async () => { const result = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = result.unwrapOrThrow().uid; }); it('should change email for existing user', async () => { const result = await authBackend.changeEmail({ uid: testUid, newEmail, }); expect(isResultSuccess(result)).toBe(true); // Verify the email was actually changed const uidResult = await authBackend.getUidByEmail(newEmail); expect(isResultSuccess(uidResult)).toBe(true); expect(uidResult.unwrapOrThrow().uid).toBe(testUid); }); it('should return error for non-existent user', async () => { const result = await authBackend.changeEmail({ uid: 'non-existent-uid', newEmail, }); expect(isResultSuccess(result)).toBe(false); }); }); describe('changePassword', () => { const testEmail = `changepassword-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; const newPassword = 'newPassword456'; let testUid; beforeEach(async () => { const result = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = result.unwrapOrThrow().uid; }); it('should change password for existing user', async () => { const result = await authBackend.changePassword({ uid: testUid, newPassword, }); expect(isResultSuccess(result)).toBe(true); // Verify the password was actually changed by trying to sign in const signInResult = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: newPassword, }); expect(isResultSuccess(signInResult)).toBe(true); // Verify old password no longer works const oldPasswordResult = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: testPassword, }); expect(isResultSuccess(oldPasswordResult)).toBe(false); }); it('should return error for non-existent user', async () => { const result = await authBackend.changePassword({ uid: 'non-existent-uid', newPassword, }); expect(isResultSuccess(result)).toBe(false); }); }); describe('deleteUser', () => { const testEmail = `delete-backend-test-${Date.now()}@example.com`; const testPassword = 'testPassword123'; let testUid; beforeEach(async () => { const result = await authBackend.signUpWithEmailPassword({ email: testEmail, password: testPassword, }); testUid = result.unwrapOrThrow().uid; }); it('should delete existing user', async () => { const result = await authBackend.deleteUser({ uid: testUid }); expect(isResultSuccess(result)).toBe(true); // Verify user was actually deleted const signInResult = await authBackend.signInWithEmailAndPassword({ email: testEmail, password: testPassword, }); expect(isResultSuccess(signInResult)).toBe(false); const error = getResultError(signInResult); expect(error.code).toBe('user-not-found'); }); it('should emit onUserDeleted$ event when user is deleted', async () => { let emittedUid; const subscription = authBackend.onUserDeleted$.subscribe((event) => { emittedUid = event.uid; }); await authBackend.deleteUser({ uid: testUid }); // Give some time for the observable to emit await new Promise((resolve) => setTimeout(resolve, 10)); subscription.unsubscribe(); expect(emittedUid).toBe(testUid); }); it('should return error for non-existent user', async () => { const result = await authBackend.deleteUser({ uid: 'non-existent-uid', }); expect(isResultSuccess(result)).toBe(false); }); }); }); }