1- import { expect } from 'chai' ;
1+ // The MIT License (MIT)
2+ //
3+ // Copyright (c) 2015 Firebase
4+ //
5+ // Permission is hereby granted, free of charge, to any person obtaining a copy
6+ // of this software and associated documentation files (the "Software"), to deal
7+ // in the Software without restriction, including without limitation the rights
8+ // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+ // copies of the Software, and to permit persons to whom the Software is
10+ // furnished to do so, subject to the following conditions:
11+ //
12+ // The above copyright notice and this permission notice shall be included in all
13+ // copies or substantial portions of the Software.
14+ //
15+ // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+ // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+ // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+ // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+ // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+ // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+ // SOFTWARE.
222
3- import { FakeEnv } from './support/helpers' ;
4- import Apps from '../src/apps' ;
5- import * as firebase from 'firebase' ;
23+ import { expect } from 'chai' ;
24+ import { fakeConfig } from './support/helpers' ;
25+ import { apps as appsNamespace , apps } from '../src/apps' ;
26+ import * as firebase from 'firebase-admin' ;
27+ import * as _ from 'lodash' ;
28+ import * as sinon from 'sinon' ;
629
730describe ( 'apps' , ( ) => {
8- let apps ;
31+ let apps : appsNamespace . Apps ;
32+ let claims ;
933 beforeEach ( ( ) => {
10- apps = new Apps ( new FakeEnv ( ) ) ;
34+ apps = new appsNamespace . Apps ( fakeConfig ( ) ) ;
35+ // mock claims intentionally contains dots, square brackets, and nested paths
36+ claims = { 'token' : { 'firebase' : { 'identities' :{ 'google.com' :[ '111' ] } } } } ;
37+ } ) ;
38+
39+ afterEach ( ( ) => {
40+ _ . forEach ( firebase . apps , app => {
41+ app . delete ( ) ;
42+ } ) ;
1143 } ) ;
1244
1345 it ( 'should load the admin app for admin impersonation' , function ( ) {
@@ -19,16 +51,116 @@ describe('apps', () => {
1951 } ) ;
2052
2153 it ( 'should create a user app for user impersonation' , function ( ) {
22- const claims = { uid : 'inlined' , team : 'firebase' } ;
23- const key = JSON . stringify ( claims ) ;
54+ const auth = { admin : false , variable : claims } ;
55+ const key = apps . _appName ( auth ) ;
2456 expect ( function ( ) {
2557 return firebase . app ( key ) ;
2658 } ) . to . throw ( Error ) ;
2759
28- const userApp = apps . forMode ( { admin : false , variable : claims } ) ;
60+ const userApp = apps . forMode ( auth ) ;
2961 expect ( firebase . app ( key ) ) . to . equal ( userApp ) ;
3062
31- const userAppAgain = apps . forMode ( { admin : false , variable : claims } ) ;
63+ const userAppAgain = apps . forMode ( auth ) ;
3264 expect ( userApp ) . to . equal ( userAppAgain ) ;
3365 } ) ;
66+
67+ it ( 'should retain/release ref counters appropriately without auth' , function ( ) {
68+ apps . retain ( { } ) ;
69+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
70+ __admin__ : 1 ,
71+ __noauth__ : 1 ,
72+ } ) ;
73+ apps . release ( { } ) ;
74+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
75+ __admin__ : 0 ,
76+ __noauth__ : 0 ,
77+ } ) ;
78+ } ) ;
79+
80+ it ( 'should retain/release ref counters appropriately with admin auth' , function ( ) {
81+ apps . retain ( { auth : { admin : true } } ) ;
82+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
83+ __admin__ : 2 ,
84+ } ) ;
85+ apps . release ( { auth : { admin : true } } ) ;
86+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
87+ __admin__ : 0 ,
88+ } ) ;
89+ } ) ;
90+
91+ it ( 'should retain/release ref counters appropriately with user auth' , function ( ) {
92+ const payload = { auth : { admin : false , variable : claims } } ;
93+ const userAppName = apps . _appName ( payload . auth ) ;
94+ apps . retain ( payload ) ;
95+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
96+ __admin__ : 1 ,
97+ [ userAppName ] : 1 ,
98+ } ) ;
99+ apps . release ( payload ) ;
100+ expect ( apps [ '_refCounter' ] ) . to . deep . equal ( {
101+ __admin__ : 0 ,
102+ [ userAppName ] : 0 ,
103+ } ) ;
104+ } ) ;
105+
106+ describe ( '#_waitToDestroyApp' , ( ) => {
107+ let clock ;
108+ let noauthApp ;
109+ let deleteNoauth ;
110+
111+ beforeEach ( ( ) => {
112+ clock = sinon . useFakeTimers ( ) ;
113+ noauthApp = apps . forMode ( { admin : false } ) ;
114+ deleteNoauth = noauthApp . delete . bind ( noauthApp ) ;
115+ sinon . spy ( noauthApp , 'delete' ) ;
116+ } ) ;
117+
118+ afterEach ( ( ) => {
119+ clock . restore ( ) ;
120+ noauthApp . delete . restore ( ) ;
121+ } ) ;
122+
123+ it ( 'should delete app after 2 minutes and not earlier' , function ( ) {
124+ apps [ '_refCounter' ] = { '__noauth__' : 0 } ;
125+ apps . _waitToDestroyApp ( '__noauth__' ) ;
126+ clock . tick ( appsNamespace . garbageCollectionInterval - 1 ) ;
127+ expect ( noauthApp . delete . called ) . to . be . false ;
128+ clock . tick ( 1 ) ;
129+
130+ // Technically the delete happens on the next tick *after* 2 min
131+ return Promise . resolve ( ) . then ( ( ) => {
132+ expect ( noauthApp . delete . called ) . to . be . true ;
133+ } ) ;
134+ } ) ;
135+
136+ it ( 'should exit right away if app was already deleted' , function ( ) {
137+ return deleteNoauth ( ) . then ( ( ) => {
138+ let spy = sinon . spy ( appsNamespace , 'delay' ) ;
139+ apps . _waitToDestroyApp ( '__noauth__' ) ;
140+ spy . restore ( ) ;
141+ expect ( spy . called ) . to . be . false ;
142+ } ) ;
143+ } ) ;
144+
145+ it ( 'should not delete app if it gets deleted while function is waiting' , function ( ) {
146+ apps . _waitToDestroyApp ( '__noauth__' ) ;
147+ deleteNoauth ( ) ;
148+ clock . tick ( appsNamespace . garbageCollectionInterval ) ;
149+ return Promise . resolve ( ) . then ( ( ) => {
150+ expect ( noauthApp . delete . called ) . to . be . false ;
151+ } ) ;
152+ } ) ;
153+
154+ it ( 'should not delete app if ref count rises above 0' , function ( ) {
155+ apps [ '_refCounter' ] = {
156+ '__admin__' : 0 ,
157+ '__noauth__' : 1 ,
158+ } ;
159+ apps . _waitToDestroyApp ( '__noauth__' ) ;
160+ clock . tick ( appsNamespace . garbageCollectionInterval ) ;
161+ return Promise . resolve ( ) . then ( ( ) => {
162+ expect ( noauthApp . delete . called ) . to . be . false ;
163+ } ) ;
164+ } ) ;
165+ } ) ;
34166} ) ;
0 commit comments