11import { DataSource , DataSourceOptions } from 'typeorm' ;
2- import { getParameterDirect } from '../../utils/ssmUtil' ;
32
4- // Mock SSM and logger dependencies
3+ // Provide a manual mock for logger so its methods are jest.fn() instances.
4+ jest . mock ( '../../utils/logger' , ( ) => ( {
5+ error : jest . fn ( ) ,
6+ info : jest . fn ( ) ,
7+ } ) ) ;
8+
9+ // Mock SSM dependency
510jest . mock ( '../../utils/ssmUtil' ) ;
6- jest . mock ( '../../utils/logger' ) ;
711
812describe ( 'Database Configuration' , ( ) => {
9- const mockGetParameterDirect = getParameterDirect as jest . MockedFunction <
10- typeof getParameterDirect
11- > ;
12- let MockedDataSource : jest . Mock ;
13- let getAppDataSource : ( ) => Promise < DataSource > ;
14-
1513 const testDbConfig = {
1614 type : 'postgres' as const ,
1715 host : 'test-host' ,
@@ -21,11 +19,16 @@ describe('Database Configuration', () => {
2119 database : 'test-db' ,
2220 } ;
2321
22+ let MockedDataSource : jest . Mock ;
23+ /* eslint-disable no-unused-vars, @typescript-eslint/no-unused-vars */
24+ let getAppDataSource : ( ) => Promise < DataSource > ;
25+
2426 beforeEach ( ( ) => {
27+ // Reset modules and clear mocks for tests that don't require error simulation.
2528 jest . resetModules ( ) ;
2629 jest . clearAllMocks ( ) ;
2730
28- // Reset environment variables
31+ // Set environment variables for production branch.
2932 process . env . NODE_ENV = 'development' ;
3033 process . env . SSM_DB_TYPE = 'db-type-param' ;
3134 process . env . SSM_DB_HOST = 'db-host-param' ;
@@ -34,7 +37,7 @@ describe('Database Configuration', () => {
3437 process . env . SSM_DB_PASSWORD = 'db-password-param' ;
3538 process . env . SSM_DB_NAME = 'db-name-param' ;
3639
37- // Setup mock for DataSource
40+ // Setup a default mock for DataSource (used in successful cases)
3841 MockedDataSource = jest
3942 . fn ( )
4043 . mockImplementation ( ( options : DataSourceOptions ) => ( {
@@ -43,54 +46,168 @@ describe('Database Configuration', () => {
4346 initialize : jest . fn ( ) . mockResolvedValue ( true ) ,
4447 } ) ) ;
4548
46- // Mock typeorm
47- jest . mock ( 'typeorm' , ( ) => ( {
49+ // Mock TypeORM's DataSource using our MockedDataSource.
50+ jest . doMock ( 'typeorm' , ( ) => ( {
4851 ...jest . requireActual ( 'typeorm' ) ,
4952 DataSource : MockedDataSource ,
5053 } ) ) ;
5154
52- // Setup SSM mock responses
53- mockGetParameterDirect . mockImplementation ( async ( paramName : string ) => {
54- switch ( paramName ) {
55- case process . env . SSM_DB_TYPE :
56- return testDbConfig . type ;
57- case process . env . SSM_DB_HOST :
58- return testDbConfig . host ;
59- case process . env . SSM_DB_PORT :
60- return testDbConfig . port ;
61- case process . env . SSM_DB_USERNAME :
62- return testDbConfig . username ;
63- case process . env . SSM_DB_PASSWORD :
64- return testDbConfig . password ;
65- case process . env . SSM_DB_NAME :
66- return testDbConfig . database ;
67- default :
68- throw new Error ( `Unexpected SSM param: ${ paramName } ` ) ;
69- }
70- } ) ;
71-
72- // Import the module under test after setting up mocks
55+ // Setup default SSM responses for production branch.
56+ const { getParameterDirect } = require ( '../../utils/ssmUtil' ) ;
57+ ( getParameterDirect as jest . Mock ) . mockImplementation (
58+ async ( paramName : string ) => {
59+ switch ( paramName ) {
60+ case process . env . SSM_DB_TYPE :
61+ return testDbConfig . type ;
62+ case process . env . SSM_DB_HOST :
63+ return testDbConfig . host ;
64+ case process . env . SSM_DB_PORT :
65+ return testDbConfig . port ;
66+ case process . env . SSM_DB_USERNAME :
67+ return testDbConfig . username ;
68+ case process . env . SSM_DB_PASSWORD :
69+ return testDbConfig . password ;
70+ case process . env . SSM_DB_NAME :
71+ return testDbConfig . database ;
72+ default :
73+ throw new Error ( `Unexpected SSM param: ${ paramName } ` ) ;
74+ }
75+ } ,
76+ ) ;
77+
78+ // Import the module under test.
7379 const databaseModule = require ( '../../config/database' ) ;
7480 getAppDataSource = databaseModule . getAppDataSource ;
7581 } ) ;
7682
7783 it ( 'should create SQLite in-memory database for test environment' , async ( ) => {
7884 process . env . NODE_ENV = 'test' ;
79-
85+ // Re-import the module under test to pick up test env branch.
86+ const databaseModule = require ( '../../config/database' ) ;
87+ const getAppDataSource = databaseModule . getAppDataSource ;
8088 await getAppDataSource ( ) ;
81-
8289 expect ( MockedDataSource ) . toHaveBeenCalled ( ) ;
8390 } ) ;
8491
85- it ( 'should throw BaseAppException when database initialization fails' , async ( ) => {
86- MockedDataSource . mockImplementationOnce ( ( ) => ( {
87- isInitialized : false ,
88- options : { } as DataSourceOptions ,
89- initialize : jest . fn ( ) . mockRejectedValue ( new Error ( 'Connection failed' ) ) ,
92+ it ( 'should successfully create production MySQL DataSource' , async ( ) => {
93+ // Override SSM responses for MySQL.
94+ process . env . NODE_ENV = 'development' ;
95+ process . env . SSM_DB_TYPE = 'db-type-param' ;
96+ process . env . SSM_DB_HOST = 'db-host-param' ;
97+ process . env . SSM_DB_PORT = 'db-port-param' ;
98+ process . env . SSM_DB_USERNAME = 'db-username-param' ;
99+ process . env . SSM_DB_PASSWORD = 'db-password-param' ;
100+ process . env . SSM_DB_NAME = 'db-name-param' ;
101+
102+ // Reset modules so updated mocks take effect.
103+ jest . resetModules ( ) ;
104+ // Reapply TypeORM mock.
105+ jest . doMock ( 'typeorm' , ( ) => ( {
106+ ...jest . requireActual ( 'typeorm' ) ,
107+ DataSource : MockedDataSource ,
90108 } ) ) ;
109+ // Override SSM to return MySQL values.
110+ const { getParameterDirect } = require ( '../../utils/ssmUtil' ) ;
111+ ( getParameterDirect as jest . Mock ) . mockImplementation (
112+ async ( paramName : string ) => {
113+ if ( paramName === 'db-type-param' ) return 'mysql' ;
114+ if ( paramName === 'db-host-param' ) return 'mysql-host' ;
115+ if ( paramName === 'db-port-param' ) return '3306' ;
116+ if ( paramName === 'db-username-param' ) return 'mysql-user' ;
117+ if ( paramName === 'db-password-param' ) return 'mysql-pass' ;
118+ if ( paramName === 'db-name-param' ) return 'mysql-db' ;
119+ throw new Error ( `Unexpected SSM param: ${ paramName } ` ) ;
120+ } ,
121+ ) ;
122+
123+ const databaseModule = require ( '../../config/database' ) ;
124+ const getAppDataSource = databaseModule . getAppDataSource ;
125+ const ds = await getAppDataSource ( ) ;
126+
127+ expect ( MockedDataSource ) . toHaveBeenCalledTimes ( 1 ) ;
128+ const options = ds . options as {
129+ type : string ;
130+ host : string ;
131+ port : number ;
132+ username : string ;
133+ password : string ;
134+ database : string ;
135+ } ;
136+ expect ( options . type ) . toBe ( 'mysql' ) ;
137+ expect ( options . host ) . toBe ( 'mysql-host' ) ;
138+ expect ( options . port ) . toBe ( 3306 ) ;
139+ expect ( options . username ) . toBe ( 'mysql-user' ) ;
140+ expect ( options . password ) . toBe ( 'mysql-pass' ) ;
141+ expect ( options . database ) . toBe ( 'mysql-db' ) ;
142+ } ) ;
143+
144+ it ( 'should throw BaseAppException and log error when SSM parameter retrieval fails' , async ( ) => {
145+ process . env . NODE_ENV = 'development' ;
146+ const ssmError = new Error ( 'SSM error' ) ;
147+
148+ // Force getParameterDirect to reject.
149+ jest . resetModules ( ) ;
150+ const ssmUtil = require ( '../../utils/ssmUtil' ) ;
151+ ( ssmUtil . getParameterDirect as jest . Mock ) . mockRejectedValue ( ssmError ) ;
152+
153+ // Re-import the database module so it picks up the updated SSM mock.
154+ const databaseModule = require ( '../../config/database' ) ;
155+ const getAppDataSource = databaseModule . getAppDataSource ;
91156
92157 await expect ( getAppDataSource ( ) ) . rejects . toThrow (
93158 'Database initialization failed.' ,
94159 ) ;
95160 } ) ;
161+
162+ it ( 'should throw BaseAppException and log error when DataSource initialization fails' , async ( ) => {
163+ process . env . NODE_ENV = 'development' ;
164+
165+ // For SSM calls, provide valid responses.
166+ jest . resetModules ( ) ;
167+ const ssmUtil = require ( '../../utils/ssmUtil' ) ;
168+ ( ssmUtil . getParameterDirect as jest . Mock ) . mockImplementation (
169+ async ( paramName : string ) => {
170+ if ( paramName === 'db-type-param' ) return 'mysql' ;
171+ if ( paramName === 'db-host-param' ) return 'host' ;
172+ if ( paramName === 'db-port-param' ) return '3306' ;
173+ if ( paramName === 'db-username-param' ) return 'user' ;
174+ if ( paramName === 'db-password-param' ) return 'pass' ;
175+ if ( paramName === 'db-name-param' ) return 'db' ;
176+ throw new Error ( `Unexpected SSM param: ${ paramName } ` ) ;
177+ } ,
178+ ) ;
179+
180+ // Force DataSource.initialize() to fail.
181+ jest . doMock ( 'typeorm' , ( ) => ( {
182+ ...jest . requireActual ( 'typeorm' ) ,
183+ DataSource : jest
184+ . fn ( )
185+ . mockImplementation ( ( options : DataSourceOptions ) => ( {
186+ isInitialized : false ,
187+ options,
188+ initialize : jest
189+ . fn ( )
190+ . mockRejectedValue ( new Error ( 'Connection failed' ) ) ,
191+ } ) ) ,
192+ } ) ) ;
193+
194+ const databaseModule = require ( '../../config/database' ) ;
195+ const getAppDataSource = databaseModule . getAppDataSource ;
196+
197+ await expect ( getAppDataSource ( ) ) . rejects . toThrow (
198+ 'Database connection failed' ,
199+ ) ;
200+ } ) ;
201+
202+ it ( 'should reuse existing DataSource instance if already initialized' , async ( ) => {
203+ process . env . NODE_ENV = 'test' ;
204+ // For test environment we don't need error mocks.
205+ const databaseModule = require ( '../../config/database' ) ;
206+ const getAppDataSource = databaseModule . getAppDataSource ;
207+ const ds1 = await getAppDataSource ( ) ;
208+ // Mark the instance as initialized.
209+ Object . defineProperty ( ds1 , 'isInitialized' , { value : true } ) ;
210+ const ds2 = await getAppDataSource ( ) ;
211+ expect ( ds2 ) . toBe ( ds1 ) ;
212+ } ) ;
96213} ) ;
0 commit comments