11import { describe , test , expect , beforeAll , afterAll } from '@jest/globals' ;
22import ExperimentClient from '../src/tracking/ExperimentClient' ;
33import { ApiError } from '../src/utils/apiError' ;
4- import { Experiment } from '../src/utils/interface' ;
4+ import {
5+ createTestExperiment ,
6+ deleteTestExperiments ,
7+ experimentProperties ,
8+ ExpSearchResults ,
9+ TRACKING_SERVER_URI ,
10+ } from './testUtils' ;
511
612describe ( 'ExperimentClient' , ( ) => {
713let experimentClient :ExperimentClient ;
8- let experimentId :string ;
9- let experimentName :string ;
1014const testIds :string [ ] = [ ] ;
1115
1216beforeAll ( async ( ) => {
1317// Add a small delay to ensure MLflow is fully ready
1418await new Promise ( ( resolve ) => setTimeout ( resolve , 2000 ) ) ;
15- experimentClient = new ExperimentClient ( 'http://127.0.0.1:5002' ) ;
16-
17- // Generate the experiment ID to be used generically in later tests
18- const timestamp = Date . now ( ) ;
19- experimentName = `Testing${ timestamp } ` ;
20- experimentId = await experimentClient . createExperiment ( experimentName ) ;
21- testIds . push ( experimentId ) ;
19+ experimentClient = new ExperimentClient ( TRACKING_SERVER_URI ) ;
2220} ) ;
2321
2422describe ( 'createExperiment' , ( ) => {
2523test ( 'should create an experiment and return the experiment ID' , async ( ) => {
26- const timestamp = Date . now ( ) ;
27- const testExperimentId = await experimentClient . createExperiment (
28- `Test experiment${ timestamp } `
29- ) ;
24+ const testExperimentId :string = await createTestExperiment ( ) ;
3025testIds . push ( testExperimentId ) ;
3126expect ( typeof testExperimentId ) . toBe ( 'string' ) ;
3227expect ( testExperimentId ) . toBeTruthy ( ) ;
@@ -44,6 +39,8 @@ describe('ExperimentClient', () => {
4439} ) ;
4540
4641test ( 'should throw error if name is already in use' , async ( ) => {
42+ const experimentName :string = `Test experiment${ Date . now ( ) } ` ;
43+ testIds . push ( await experimentClient . createExperiment ( experimentName ) ) ;
4744await expect (
4845experimentClient . createExperiment ( experimentName )
4946) . rejects . toThrow ( ApiError ) ;
@@ -53,18 +50,15 @@ describe('ExperimentClient', () => {
5350describe ( 'searchExperiment' , ( ) => {
5451beforeAll ( async ( ) => {
5552for ( let i = 0 ; i < 5 ; i ++ ) {
56- const num = Math . random ( ) . toString ( ) . slice ( 2 , 11 ) ;
57- const name = `Search test${ num } ` ;
58- const search = await experimentClient . createExperiment ( name ) ;
53+ const search : string = await experimentClient . createExperiment (
54+ `Search test${ Date . now ( ) } `
55+ ) ;
5956testIds . push ( search ) ;
6057}
6158} ) ;
6259
6360test ( 'should return valid search results' , async ( ) => {
64- const results :{
65- experiments ?:Experiment [ ] ;
66- next_page_token ?:string ;
67- } = await experimentClient . searchExperiment (
61+ const results :ExpSearchResults = await experimentClient . searchExperiment (
6862"name LIKE 'Search test%'" ,
69634
7064) ;
@@ -73,26 +67,22 @@ describe('ExperimentClient', () => {
7367expect ( results . next_page_token ) . toBeDefined ( ) ;
7468expect ( results . experiments ) . toHaveLength ( 4 ) ;
7569results . experiments ?. forEach ( ( result ) => {
76- expect ( result ) . toHaveProperty ( 'experiment_id' ) ;
77- expect ( result ) . toHaveProperty ( 'name' ) ;
78- expect ( result ) . toHaveProperty ( 'artifact_location' ) ;
79- expect ( result ) . toHaveProperty ( 'lifecycle_stage' ) ;
80- expect ( result ) . toHaveProperty ( 'last_update_time' ) ;
81- expect ( result ) . toHaveProperty ( 'creation_time' ) ;
70+ for ( const property of experimentProperties ) {
71+ expect ( result ) . toHaveProperty ( property ) ;
72+ }
8273} ) ;
8374expect ( typeof results . next_page_token ) . toBe ( 'string' ) ;
8475} ) ;
8576} ) ;
8677
8778describe ( 'getExperiment' , ( ) => {
8879test ( 'should return experiment information' , async ( ) => {
89- const experiment = await experimentClient . getExperiment ( experimentId ) ;
90- expect ( experiment ) . toHaveProperty ( 'experiment_id' ) ;
91- expect ( experiment ) . toHaveProperty ( 'name' ) ;
92- expect ( experiment ) . toHaveProperty ( 'artifact_location' ) ;
93- expect ( experiment ) . toHaveProperty ( 'lifecycle_stage' ) ;
94- expect ( experiment ) . toHaveProperty ( 'last_update_time' ) ;
95- expect ( experiment ) . toHaveProperty ( 'creation_time' ) ;
80+ const expId :string = await createTestExperiment ( ) ;
81+ const experiment = await experimentClient . getExperiment ( expId ) ;
82+ testIds . push ( expId ) ;
83+ for ( const property of experimentProperties ) {
84+ expect ( experiment ) . toHaveProperty ( property ) ;
85+ }
9686} ) ;
9787
9888test ( 'should throw error if experiment ID is missing' , async ( ) => {
@@ -103,54 +93,49 @@ describe('ExperimentClient', () => {
10393
10494describe ( 'getExperimentByName' , ( ) => {
10595test ( 'should return experiment information' , async ( ) => {
106- const experiment = await experimentClient . getExperimentByName ( experimentName ) ;
107- expect ( experiment ) . toHaveProperty ( 'experiment_id' ) ;
108- expect ( experiment ) . toHaveProperty ( 'name' ) ;
109- expect ( experiment ) . toHaveProperty ( 'artifact_location' ) ;
110- expect ( experiment ) . toHaveProperty ( 'lifecycle_stage' ) ;
111- expect ( experiment ) . toHaveProperty ( 'last_update_time' ) ;
112- expect ( experiment ) . toHaveProperty ( 'creation_time' ) ;
96+ const name :string = `Test experiment${ Date . now ( ) } ` ;
97+ testIds . push ( await experimentClient . createExperiment ( name ) ) ;
98+ const experiment = await experimentClient . getExperimentByName ( name ) ;
99+ for ( const property of experimentProperties ) {
100+ expect ( experiment ) . toHaveProperty ( property ) ;
101+ }
113102} ) ;
114103
115104test ( 'should throw error if experiment name is missing' , async ( ) => {
116105//@ts -expect-error: testing for missing arguments
117- await expect ( experimentClient . getExperimentByName ( ) ) . rejects . toThrow ( ApiError ) ;
106+ await expect ( experimentClient . getExperimentByName ( ) ) . rejects . toThrow (
107+ ApiError
108+ ) ;
118109} ) ;
119110} ) ;
120111
121112describe ( 'deleteExperiment' , ( ) => {
122113test ( 'should delete an experiment' , async ( ) => {
123- const num = Math . random ( ) . toString ( ) . slice ( 2 , 11 ) ;
124- const name = `Test experiment${ num } ` ;
125- const idToDelete = await experimentClient . createExperiment ( name ) ;
114+ const name :string = `Test experiment${ Date . now ( ) } ` ;
115+ const idToDelete :string = await experimentClient . createExperiment ( name ) ;
126116await experimentClient . deleteExperiment ( idToDelete ) ;
127- const results :{
128- experiments ?:Experiment [ ] ;
129- next_page_token ?:string ;
130- } = await experimentClient . searchExperiment (
131- `name LIKE '${ idToDelete } '` ,
117+ const results :ExpSearchResults = await experimentClient . searchExperiment (
118+ `name LIKE '${ name } '` ,
1321194
133120) ;
134121expect ( results ) . toEqual ( { } ) ;
135122} ) ;
136123
137124test ( 'should throw error if invalid experiment ID is passed in' , async ( ) => {
138- await expect ( experimentClient . deleteExperiment ( 'invalidExperimentId' ) ) . rejects . toThrow ( ApiError ) ;
125+ await expect (
126+ experimentClient . deleteExperiment ( 'invalidExperimentId' )
127+ ) . rejects . toThrow ( ApiError ) ;
139128} ) ;
140129} ) ;
141130
142131describe ( 'restoreExperiment' , ( ) => {
143132test ( 'should restore a deleted experiment' , async ( ) => {
144- const num = Math . random ( ) . toString ( ) . slice ( 2 , 11 ) ;
145- const name = `Test experiment${ num } ` ;
146- const idToDelete = await experimentClient . createExperiment ( name ) ;
133+ const name :string = `Test experiment${ Date . now ( ) } ` ;
134+ const idToDelete :string = await experimentClient . createExperiment ( name ) ;
147135testIds . push ( idToDelete ) ;
148136await experimentClient . deleteExperiment ( idToDelete ) ;
149137await experimentClient . restoreExperiment ( idToDelete ) ;
150- const results :{
151- experiments ?:Experiment [ ] ;
152- next_page_token ?:string ;
153- } = await experimentClient . searchExperiment (
138+ const results :ExpSearchResults = await experimentClient . searchExperiment (
154139`name LIKE '${ name } '` ,
1551404
156141) ;
@@ -159,22 +144,20 @@ describe('ExperimentClient', () => {
159144} ) ;
160145
161146test ( 'should throw error if invalid experiment ID is passed in' , async ( ) => {
162- await expect ( experimentClient . restoreExperiment ( 'invalidExperimentId' ) ) . rejects . toThrow ( ApiError ) ;
147+ await expect (
148+ experimentClient . restoreExperiment ( 'invalidExperimentId' )
149+ ) . rejects . toThrow ( ApiError ) ;
163150} ) ;
164151} ) ;
165152
166153describe ( 'updateExperiment' , ( ) => {
167- test ( 'should update an experiment\'s name' , async ( ) => {
168- const num = Math . random ( ) . toString ( ) . slice ( 2 , 11 ) ;
169- const name = `Test experiment${ num } ` ;
170- const exp = await experimentClient . createExperiment ( name ) ;
154+ test ( "should update an experiment's name" , async ( ) => {
155+ const name :string = `Test experiment${ Date . now ( ) } ` ;
156+ const exp :string = await experimentClient . createExperiment ( name ) ;
171157testIds . push ( exp ) ;
172- const updatedName = `${ name } _UPDATE`
158+ const updatedName : string = `${ name } _UPDATE` ;
173159await experimentClient . updateExperiment ( exp , updatedName ) ;
174- const results :{
175- experiments ?:Experiment [ ] ;
176- next_page_token ?:string ;
177- } = await experimentClient . searchExperiment (
160+ const results :ExpSearchResults = await experimentClient . searchExperiment (
178161`name LIKE '${ updatedName } '` ,
1791624
180163) ;
@@ -184,22 +167,24 @@ describe('ExperimentClient', () => {
184167} ) ;
185168
186169test ( 'should throw error if invalid experiment ID is passed in' , async ( ) => {
187- await expect ( experimentClient . updateExperiment ( 'invalidExperimentId' , 'invalidExperimentIdUpdate' ) ) . rejects . toThrow ( ApiError ) ;
170+ await expect (
171+ experimentClient . updateExperiment (
172+ 'invalidExperimentId' ,
173+ 'invalidExperimentIdUpdate'
174+ )
175+ ) . rejects . toThrow ( ApiError ) ;
188176} ) ;
189177} ) ;
190178
191179describe ( 'setExperimentTag' , ( ) => {
192180test ( 'should set a tag on an experiment' , async ( ) => {
193- const num = Math . random ( ) . toString ( ) . slice ( 2 , 11 ) ;
194- const name = `Test experiment${ num } ` ;
195- const exp = await experimentClient . createExperiment ( name ) ;
181+ const date : number = Date . now ( ) ;
182+ const name : string = `Test experiment${ date } ` ;
183+ const exp : string = await experimentClient . createExperiment ( name ) ;
196184testIds . push ( exp ) ;
197- await experimentClient . setExperimentTag ( exp , 'tag1' , `value${ num } ` ) ;
198- const results :{
199- experiments ?:Experiment [ ] ;
200- next_page_token ?:string ;
201- } = await experimentClient . searchExperiment (
202- `tags.tag1 = "value${ num } "` ,
185+ await experimentClient . setExperimentTag ( exp , 'tag1' , `value${ date } ` ) ;
186+ const results :ExpSearchResults = await experimentClient . searchExperiment (
187+ `tags.tag1 = "value${ date } "` ,
2031884
204189) ;
205190expect ( results . experiments ) . toBeDefined ( ) ;
@@ -208,16 +193,15 @@ describe('ExperimentClient', () => {
208193} ) ;
209194
210195test ( 'should throw error if invalid experiment ID is passed in' , async ( ) => {
211- await expect ( experimentClient . setExperimentTag ( 'invalidExperimentId' , 'tag1' , 'value1' ) ) . rejects . toThrow ( ApiError ) ;
196+ await expect (
197+ experimentClient . setExperimentTag (
198+ 'invalidExperimentId' ,
199+ 'tag1' ,
200+ 'value1'
201+ )
202+ ) . rejects . toThrow ( ApiError ) ;
212203} ) ;
213204} ) ;
214205
215- afterAll ( async ( ) => {
216- while ( testIds . length > 0 ) {
217- const id = testIds . pop ( ) ;
218- if ( id ) {
219- await experimentClient . deleteExperiment ( id ) ;
220- }
221- }
222- } ) ;
206+ afterAll ( async ( ) => await deleteTestExperiments ( testIds ) ) ;
223207} ) ;