Skip to content

Commit 7cb9087

Browse files
committed
fixed unit test errors
1 parent c9b6a41 commit 7cb9087

File tree

6 files changed

+286
-34
lines changed

6 files changed

+286
-34
lines changed

etc/firebase-admin.auth.api.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,11 @@ export class PasskeyConfig {
367367
export class PasskeyConfigManager {
368368
constructor(app: App);
369369
// (undocumented)
370-
createPasskeyConfig(rpId: string, passkeyConfigRequest: PasskeyConfigRequest, tenantId?: string): Promise<PasskeyConfigRequest>;
370+
createPasskeyConfig(rpId: string, passkeyConfigRequest: PasskeyConfigRequest, tenantId?: string): Promise<PasskeyConfig>;
371371
// (undocumented)
372372
getPasskeyConfig(tenantId?: string): Promise<PasskeyConfig>;
373373
// (undocumented)
374-
updatePasskeyConfig(passkeyConfigRequest: PasskeyConfigRequest, tenantId?: string): Promise<PasskeyConfigRequest>;
374+
updatePasskeyConfig(passkeyConfigRequest: PasskeyConfigRequest, tenantId?: string): Promise<PasskeyConfig>;
375375
}
376376

377377
// @public (undocumented)

src/auth/passkey-config-manager.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { App } from '../app';
1717
import {
1818
AuthRequestHandler,
1919
} from './auth-api-request';
20-
import {PasskeyConfig, PasskeyConfigClientRequest, PasskeyConfigRequest, PasskeyConfigServerResponse} from './passkey-config';
20+
import { PasskeyConfig, PasskeyConfigClientRequest, PasskeyConfigRequest, PasskeyConfigServerResponse } from './passkey-config';
2121

2222

2323
export class PasskeyConfigManager {
@@ -28,7 +28,7 @@ export class PasskeyConfigManager {
2828
}
2929

3030
public getPasskeyConfig(tenantId?: string): Promise<PasskeyConfig> {
31-
return this.authRequestHandler.getPasskeyConfig()
31+
return this.authRequestHandler.getPasskeyConfig(tenantId)
3232
.then((response: PasskeyConfigServerResponse) => {
3333
return new PasskeyConfig(response);
3434
});
@@ -38,13 +38,13 @@ export class PasskeyConfigManager {
3838
return this.authRequestHandler.updatePasskeyConfig(true, tenantId, passkeyConfigRequest, rpId)
3939
.then((response: PasskeyConfigClientRequest) => {
4040
return new PasskeyConfig(response);
41-
})
41+
});
4242
}
4343

4444
public updatePasskeyConfig(passkeyConfigRequest: PasskeyConfigRequest, tenantId?: string): Promise<PasskeyConfig> {
4545
return this.authRequestHandler.updatePasskeyConfig(false, tenantId, passkeyConfigRequest)
4646
.then((response: PasskeyConfigClientRequest) => {
4747
return new PasskeyConfig(response);
48-
})
48+
});
4949
}
5050
}

src/auth/passkey-config.ts

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -69,20 +69,20 @@ export class PasskeyConfig {
6969
);
7070
}
7171
}
72-
if(!validator.isNonEmptyArray(passkeyConfigRequest.expectedOrigins) || !validator.isNonNullObject(passkeyConfigRequest.expectedOrigins)) {
72+
if(!validator.isNonEmptyArray(passkeyConfigRequest.expectedOrigins)) {
7373
throw new FirebaseAuthError(
7474
AuthClientErrorCode.INVALID_ARGUMENT,
7575
`'passkeyConfigRequest.expectedOrigins' must be a valid non-empty array of strings.'`,
7676
);
7777
}
78-
for(const origin in passkeyConfigRequest.expectedOrigins) {
79-
if(!validator.isString(origin)) {
80-
throw new FirebaseAuthError(
81-
AuthClientErrorCode.INVALID_ARGUMENT,
82-
`'passkeyConfigRequest.expectedOrigins' must be a valid non-empty array of strings.'`,
83-
);
84-
}
85-
}
78+
for (const origin of passkeyConfigRequest.expectedOrigins) {
79+
if (!validator.isNonEmptyString(origin)) {
80+
throw new FirebaseAuthError(
81+
AuthClientErrorCode.INVALID_ARGUMENT,
82+
`'passkeyConfigRequest.expectedOrigins' must be a valid non-empty array of strings.'`,
83+
);
84+
}
85+
}
8686
};
8787

8888
public static buildServerRequest(isCreateRequest: boolean, passkeyConfigRequest?: PasskeyConfigRequest, rpId?: string): PasskeyConfigClientRequest {
@@ -91,8 +91,8 @@ export class PasskeyConfig {
9191
if(isCreateRequest && typeof rpId !== 'undefined') {
9292
request.rpId = rpId;
9393
}
94-
if(typeof request.expectedOrigins !== 'undefined') {
95-
request.expectedOrigins = passkeyConfigRequest?.expectedOrigins;
94+
if(typeof passkeyConfigRequest?.expectedOrigins !== 'undefined') {
95+
request.expectedOrigins = passkeyConfigRequest.expectedOrigins;
9696
}
9797
return request;
9898
};

test/integration/auth.spec.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ import {
3636
} from '../../lib/auth/index';
3737
import * as sinon from 'sinon';
3838
import * as sinonChai from 'sinon-chai';
39-
import {PasskeyConfigRequest} from '../../src/auth';
4039

4140
const chalk = require('chalk'); // eslint-disable-line @typescript-eslint/no-var-requires
4241

@@ -2206,15 +2205,10 @@ describe('admin.auth', () => {
22062205
expectedOrigins: ['app1', 'example.com'],
22072206
};
22082207

2209-
// Helper function to reset passkey config to the initial state
2210-
async function resetPasskeyConfig() {
2211-
const resetRequest = { expectedOrigins: expectedPasskeyConfig.expectedOrigins };
2212-
await getAuth().passkeyConfigManager().updatePasskeyConfig(resetRequest);
2213-
}
2214-
22152208
// Before each test, reset the passkey config to the initial state
22162209
beforeEach(async () => {
2217-
await resetPasskeyConfig();
2210+
const resetRequest = { expectedOrigins: expectedPasskeyConfig.expectedOrigins };
2211+
await getAuth().passkeyConfigManager().updatePasskeyConfig(resetRequest);
22182212
});
22192213

22202214
it('createPasskeyConfig() should create passkey config with expected passkeyConfig', async () => {
@@ -2223,7 +2217,7 @@ describe('admin.auth', () => {
22232217

22242218
const createdPasskeyConfig = await getAuth().passkeyConfigManager().createPasskeyConfig(rpId, createRequest);
22252219
const passkeyConfigObj = createdPasskeyConfig.toJSON();
2226-
2220+
22272221
expect(passkeyConfigObj).to.deep.equal(expectedPasskeyConfig);
22282222
});
22292223

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,256 @@
1+
/*!
2+
* Copyright 2023 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
'use strict';
18+
19+
import * as _ from 'lodash';
20+
import * as chai from 'chai';
21+
import * as sinon from 'sinon';
22+
import * as sinonChai from 'sinon-chai';
23+
import * as chaiAsPromised from 'chai-as-promised';
24+
25+
import * as mocks from '../../resources/mocks';
26+
import { FirebaseApp } from '../../../src/app/firebase-app';
27+
import { AuthRequestHandler } from '../../../src/auth/auth-api-request';
28+
import { AuthClientErrorCode, FirebaseAuthError } from '../../../src/utils/error';
29+
import { PasskeyConfigManager } from '../../../src/auth/passkey-config-manager';
30+
import {
31+
PasskeyConfig, PasskeyConfigServerResponse, PasskeyConfigRequest,
32+
} from '../../../src/auth/passkey-config';
33+
34+
chai.should();
35+
chai.use(sinonChai);
36+
chai.use(chaiAsPromised);
37+
38+
const expect = chai.expect;
39+
40+
describe('PasskeyConfigManager', () => {
41+
let mockApp: FirebaseApp;
42+
let passkeyConfigManager: PasskeyConfigManager;
43+
let nullAccessTokenPasskeyConfigManager: PasskeyConfigManager;
44+
let malformedAccessTokenPasskeyConfigManager: PasskeyConfigManager;
45+
let rejectedPromiseAccessTokenPasskeyConfigManager: PasskeyConfigManager;
46+
const GET_CONFIG_RESPONSE: PasskeyConfigServerResponse = {
47+
name: `projects/project-id/passkeyConfig`,
48+
rpId: `project-id.firebaseapp.com`,
49+
expectedOrigins: ['app1', 'example.com'],
50+
};
51+
52+
before(() => {
53+
mockApp = mocks.app();
54+
passkeyConfigManager = new PasskeyConfigManager(mockApp);
55+
nullAccessTokenPasskeyConfigManager = new PasskeyConfigManager(
56+
mocks.appReturningNullAccessToken());
57+
malformedAccessTokenPasskeyConfigManager = new PasskeyConfigManager(
58+
mocks.appReturningMalformedAccessToken());
59+
rejectedPromiseAccessTokenPasskeyConfigManager = new PasskeyConfigManager(
60+
mocks.appRejectedWhileFetchingAccessToken());
61+
});
62+
63+
after(() => {
64+
return mockApp.delete();
65+
});
66+
67+
describe('getPasskeyConfig()', () => {
68+
const expectedPasskeyConfig = new PasskeyConfig(GET_CONFIG_RESPONSE);
69+
const expectedError = new FirebaseAuthError(AuthClientErrorCode.INVALID_CONFIG);
70+
// Stubs used to simulate underlying API calls.
71+
let stubs: sinon.SinonStub[] = [];
72+
afterEach(() => {
73+
_.forEach(stubs, (stub) => stub.restore());
74+
stubs = [];
75+
});
76+
77+
it('should be rejected given an app which returns null access tokens', () => {
78+
return nullAccessTokenPasskeyConfigManager.getPasskeyConfig()
79+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
80+
});
81+
82+
it('should be rejected given an app which returns invalid access tokens', () => {
83+
return malformedAccessTokenPasskeyConfigManager.getPasskeyConfig()
84+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
85+
});
86+
87+
it('should be rejected given an app which fails to generate access tokens', () => {
88+
return rejectedPromiseAccessTokenPasskeyConfigManager.getPasskeyConfig()
89+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
90+
});
91+
92+
it('should resolve with a Passkey Config on success', () => {
93+
// Stub getPasskeyConfig to return expected result.
94+
const stub = sinon.stub(AuthRequestHandler.prototype, 'getPasskeyConfig')
95+
.returns(Promise.resolve(GET_CONFIG_RESPONSE));
96+
stubs.push(stub);
97+
return passkeyConfigManager.getPasskeyConfig()
98+
.then((result) => {
99+
// Confirm underlying API called with expected parameters.
100+
expect(stub).to.have.been.calledOnce;
101+
// Confirm expected project config returned.
102+
expect(result).to.deep.equal(expectedPasskeyConfig);
103+
});
104+
});
105+
106+
it('should throw an error when the backend returns an error', () => {
107+
// Stub getConfig to throw a backend error.
108+
const stub = sinon.stub(AuthRequestHandler.prototype, 'getPasskeyConfig')
109+
.returns(Promise.reject(expectedError));
110+
stubs.push(stub);
111+
return passkeyConfigManager.getPasskeyConfig()
112+
.then(() => {
113+
throw new Error('Unexpected success');
114+
}, (error) => {
115+
// Confirm underlying API called with expected parameters.
116+
expect(stub).to.have.been.calledOnce;
117+
// Confirm expected error returned.
118+
expect(error).to.equal(expectedError);
119+
});
120+
});
121+
});
122+
123+
describe('createPasskeyConfig()', () => {
124+
const rpId: string = 'project-id.firebaseapp.com';
125+
const expectedOrigins: string[] = ['app1', 'example.com']
126+
const passkeyConfigRequest: PasskeyConfigRequest = {
127+
expectedOrigins: expectedOrigins ,
128+
};
129+
const expectedPasskeyConfig = new PasskeyConfig(GET_CONFIG_RESPONSE);
130+
const expectedError = new FirebaseAuthError(
131+
AuthClientErrorCode.INTERNAL_ERROR,
132+
'Unable to create the config provided.');
133+
// Stubs used to simulate underlying API calls.
134+
let stubs: sinon.SinonStub[] = [];
135+
afterEach(() => {
136+
sinon.restore();
137+
});
138+
139+
it('should be rejected given no passkeyConfigOptions', () => {
140+
return (passkeyConfigManager as any).createPasskeyConfig(null as unknown as PasskeyConfigRequest)
141+
.should.eventually.be.rejected.and.have.property('code', 'auth/argument-error');
142+
});
143+
144+
it('should be rejected given an app which returns null access tokens', () => {
145+
console.log("TEST===" + JSON.stringify(passkeyConfigRequest));
146+
return nullAccessTokenPasskeyConfigManager.createPasskeyConfig(rpId, passkeyConfigRequest)
147+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
148+
});
149+
150+
it('should be rejected given an app which returns invalid access tokens', () => {
151+
return malformedAccessTokenPasskeyConfigManager.createPasskeyConfig(rpId, passkeyConfigRequest)
152+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
153+
});
154+
155+
it('should be rejected given an app which fails to generate access tokens', () => {
156+
return rejectedPromiseAccessTokenPasskeyConfigManager.createPasskeyConfig(rpId, passkeyConfigRequest)
157+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
158+
});
159+
160+
it('should resolve with a PasskeyConfig on createPasskeyConfig request success', () => {
161+
// Stub createPasskeyConfig to return expected result.
162+
const stub = sinon.stub(AuthRequestHandler.prototype, 'updatePasskeyConfig')
163+
.returns(Promise.resolve(GET_CONFIG_RESPONSE));
164+
stubs.push(stub);
165+
return passkeyConfigManager.createPasskeyConfig(rpId, passkeyConfigRequest)
166+
.then((actualPasskeyConfig) => {
167+
// Confirm underlying API called with expected parameters.
168+
expect(stub).to.have.been.calledOnce.and.calledWith(true, undefined, passkeyConfigRequest, rpId);
169+
// Confirm expected Passkey Config object returned.
170+
expect(actualPasskeyConfig).to.deep.equal(expectedPasskeyConfig);
171+
});
172+
});
173+
174+
it('should throw an error when createPasskeyConfig returns an error', () => {
175+
// Stub createPasskeyConfig to throw a backend error.
176+
const stub = sinon.stub(AuthRequestHandler.prototype, 'updatePasskeyConfig')
177+
.returns(Promise.reject(expectedError));
178+
stubs.push(stub);
179+
return passkeyConfigManager.createPasskeyConfig(rpId, passkeyConfigRequest)
180+
.then(() => {
181+
throw new Error('Unexpected success');
182+
}, (error) => {
183+
// Confirm underlying API called with expected parameters.
184+
expect(stub).to.have.been.calledOnce.and.calledWith(true, undefined, passkeyConfigRequest, rpId);
185+
// Confirm expected error returned.
186+
expect(error).to.equal(expectedError);
187+
});
188+
});
189+
});
190+
191+
describe('updatePasskeyConfig()', () => {
192+
const passkeyConfigOptions: PasskeyConfigRequest = {
193+
expectedOrigins: ['app1', 'example.com', 'app2'],
194+
};
195+
const expectedPasskeyConfig = new PasskeyConfig(GET_CONFIG_RESPONSE);
196+
const expectedError = new FirebaseAuthError(
197+
AuthClientErrorCode.INTERNAL_ERROR,
198+
'Unable to update the config provided.');
199+
// Stubs used to simulate underlying API calls.
200+
let stubs: sinon.SinonStub[] = [];
201+
afterEach(() => {
202+
_.forEach(stubs, (stub) => stub.restore());
203+
stubs = [];
204+
});
205+
206+
it('should be rejected given no passkeyConfigOptions', () => {
207+
return (passkeyConfigManager as any).updatePasskeyConfig(null as unknown as PasskeyConfigRequest)
208+
.should.eventually.be.rejected.and.have.property('code', 'auth/argument-error');
209+
});
210+
211+
it('should be rejected given an app which returns null access tokens', () => {
212+
return nullAccessTokenPasskeyConfigManager.updatePasskeyConfig(passkeyConfigOptions)
213+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
214+
});
215+
216+
it('should be rejected given an app which returns invalid access tokens', () => {
217+
return malformedAccessTokenPasskeyConfigManager.updatePasskeyConfig(passkeyConfigOptions)
218+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
219+
});
220+
221+
it('should be rejected given an app which fails to generate access tokens', () => {
222+
return rejectedPromiseAccessTokenPasskeyConfigManager.updatePasskeyConfig(passkeyConfigOptions)
223+
.should.eventually.be.rejected.and.have.property('code', 'app/invalid-credential');
224+
});
225+
226+
it('should resolve with a PasskeyConfig on updatePasskeyConfig request success', () => {
227+
// Stub updatePasskeyConfig to return expected result.
228+
const updateConfigStub = sinon.stub(AuthRequestHandler.prototype, 'updatePasskeyConfig')
229+
.returns(Promise.resolve(GET_CONFIG_RESPONSE));
230+
stubs.push(updateConfigStub);
231+
return passkeyConfigManager.updatePasskeyConfig(passkeyConfigOptions)
232+
.then((actualPasskeyConfig) => {
233+
// Confirm underlying API called with expected parameters.
234+
expect(updateConfigStub).to.have.been.calledOnce.and.calledWith(false, undefined, passkeyConfigOptions);
235+
// Confirm expected Project Config object returned.
236+
expect(actualPasskeyConfig).to.deep.equal(expectedPasskeyConfig);
237+
});
238+
});
239+
240+
it('should throw an error when updatePasskeyConfig returns an error', () => {
241+
// Stub updatePasskeyConfig to throw a backend error.
242+
const updateConfigStub = sinon.stub(AuthRequestHandler.prototype, 'updatePasskeyConfig')
243+
.returns(Promise.reject(expectedError));
244+
stubs.push(updateConfigStub);
245+
return passkeyConfigManager.updatePasskeyConfig(passkeyConfigOptions)
246+
.then(() => {
247+
throw new Error('Unexpected success');
248+
}, (error) => {
249+
// Confirm underlying API called with expected parameters.
250+
expect(updateConfigStub).to.have.been.calledOnce.and.calledWith(false, undefined, passkeyConfigOptions);
251+
// Confirm expected error returned.
252+
expect(error).to.equal(expectedError);
253+
});
254+
});
255+
});
256+
});

0 commit comments

Comments
 (0)