@@ -3,12 +3,13 @@ import { strict as assert } from 'node:assert'; // use strict equality everywhe
33// Future TODO: before this API becomes set in stone/offered publicly, we should change the results of these methods to be just the output data rather than duplicating input values.
44
55// Reusable testing data
6+ const SAMPLE_SIZE = 8000
67const FPCoreFormula = '(FPCore (x) (- (sqrt (+ x 1)) (sqrt x)))'
78const FPCoreFormula2 = '(FPCore (x) (- (sqrt (+ x 1))))'
89const eval_sample = [ [ [ 1 ] , - 1.4142135623730951 ] ]
910
1011// improve endpoint
11- const improveResponse = await callHerbie ( `/improve?formula=${ encodeURIComponent ( FPCoreFormula2 ) } ` , { method : 'GET' } )
12+ const improveResponse = await fetch ( makeEndpoint ( `/improve?formula=${ encodeURIComponent ( FPCoreFormula2 ) } ` ) , { method : 'GET' } )
1213assert . equal ( improveResponse . status , 200 )
1314let redirect = improveResponse . url . split ( "/" )
1415const jobID = redirect [ 3 ] . split ( "." ) [ 0 ]
@@ -18,18 +19,18 @@ const jobID = redirect[3].split(".")[0]
1819// assert.equal(improveHTML.length, improveHTMLexpectedCount, `HTML response character count should be ${improveHTMLexpectedCount} unless HTML changes.`)
1920
2021// timeline
21- const timelineRSP = await callHerbie ( `/timeline/${ jobID } ` , { method : 'GET' } )
22+ const timelineRSP = await fetch ( makeEndpoint ( `/timeline/${ jobID } ` ) , { method : 'GET' } )
2223assert . equal ( timelineRSP . status , 201 )
2324const timeline = await timelineRSP . json ( )
2425assert . equal ( timeline . length > 0 , true )
2526
2627// Test with a likely missing job-id
27- const badTimelineRSP = await callHerbie ( ` /timeline/42069` , { method : 'GET' } )
28+ const badTimelineRSP = await fetch ( makeEndpoint ( " /timeline/42069" ) , { method : 'GET' } )
2829assert . equal ( badTimelineRSP . status , 404 )
2930
3031// improve-start endpoint
3132const URIencodedBody = "formula=" + encodeURIComponent ( FPCoreFormula )
32- const startResponse = await callHerbie ( `/improve- start` , {
33+ const startResponse = await fetch ( makeEndpoint ( "/api/ start/improve" ) , {
3334 method : 'POST' ,
3435 headers : {
3536 'Content-Type' : 'application/x-www-form-urlencoded' ,
@@ -38,122 +39,164 @@ const startResponse = await callHerbie(`/improve-start`, {
3839} )
3940const testResult = ( startResponse . status == 201 || startResponse . status == 202 )
4041assert . equal ( testResult , true )
41- const path = startResponse . headers . get ( "location" )
42+ const improveResultPath = startResponse . headers . get ( "location" )
43+ let counter = 0
44+ let cap = 100
45+ // Check status endpoint
46+ let checkStatus = await fetch ( makeEndpoint ( improveResultPath ) , { method : 'GET' } )
47+ /*
48+ This is testing if the /api/start/improve test at the beginning has been completed. The cap and counter is a sort of timeout for the test. Ends up being 10 seconds max.
49+ */
50+ while ( checkStatus . status != 201 && counter < cap ) {
51+ counter += 1
52+ checkStatus = await fetch ( makeEndpoint ( improveResultPath ) , { method : 'GET' } )
53+ await new Promise ( r => setTimeout ( r , 100 ) ) ; // ms
54+ }
55+ assert . equal ( checkStatus . statusText , 'Job complete' )
4256
4357// up endpoint
44- const up = await callHerbie ( "/up" , { method : 'GET' } )
58+ const up = await fetch ( makeEndpoint ( "/up" ) , { method : 'GET' } )
4559assert . equal ( 'Up' , up . statusText )
4660// TODO how do I test down state?
4761
4862// Sample endpoint
49- const sampleRSP = await callHerbie ( "/api/sample" , { method : 'POST' , body : JSON . stringify ( { formula : FPCoreFormula2 , seed : 5 } ) } )
63+ const sampleBody = {
64+ method : 'POST' ,
65+ body : JSON . stringify ( { formula : FPCoreFormula2 , seed : 5 } )
66+ }
67+ const sampleRSP = await fetch ( makeEndpoint ( "/api/sample" ) , sampleBody )
68+ const sampleAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/sample" , sampleBody )
5069const jid = sampleRSP . headers . get ( "x-herbie-job-id" )
5170assert . notEqual ( jid , null )
52-
5371const sample = await sampleRSP . json ( )
72+ assertIdAndPath ( sampleAsyncResult )
73+ assert . ok ( sampleAsyncResult . points )
74+ assert . equal ( sampleAsyncResult . points . length , SAMPLE_SIZE )
5475assertIdAndPath ( sample )
55-
56- const SAMPLE_SIZE = 8000
5776assert . ok ( sample . points )
58- const points = sample . points
59- assert . equal ( points . length , SAMPLE_SIZE , `sample size should be ${ SAMPLE_SIZE } ` )
77+ assert . equal ( sample . points . length , SAMPLE_SIZE , `sample size should be ${ SAMPLE_SIZE } ` )
6078
61- const sample2RPS = await callHerbie ( "/api/sample" , { method : 'POST' , body : JSON . stringify ( { formula : FPCoreFormula2 , seed : 5 } ) } )
79+ // Make second call to test that results are the same
80+ const sample2RPS = await fetch ( makeEndpoint ( "/api/sample" ) , sampleBody )
6281const jid2 = sample2RPS . headers . get ( "x-herbie-job-id" )
6382assert . notEqual ( jid2 , null )
6483const sample2 = await sample2RPS . json ( )
65- const points2 = sample2 . points
6684assertIdAndPath ( sample2 )
67- assert . deepEqual ( points [ 1 ] , points2 [ 1 ] )
85+ assert . deepEqual ( sample . points [ 1 ] , sample2 . points [ 1 ] )
86+
87+ //Explanations endpoint
88+ const explainBody = {
89+ method : 'POST' ,
90+ body : JSON . stringify ( {
91+ formula : FPCoreFormula , sample : sample2 . points
92+ } )
93+ }
94+ const explain = await ( await fetch ( makeEndpoint ( "/api/explanations" ) , explainBody ) ) . json ( )
95+ assertIdAndPath ( explain )
96+ assert . equal ( explain . explanation . length > 0 , true , 'explanation should not be empty' ) ;
97+ const explainAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/explanations" , explainBody )
98+ assertIdAndPath ( explainAsyncResult )
99+ assert . equal ( explainAsyncResult . explanation . length > 0 , true , 'explanation should not be empty' ) ;
68100
69101// Analyze endpoint
70- const errors = await callHerbie ( "/api/analyze" , {
102+ const errorsBody = {
71103 method : 'POST' , body : JSON . stringify ( {
72104 formula : FPCoreFormula , sample : [ [ [
73105 14.97651307489794
74106 ] , 0.12711304680349078 ] ]
75107 } )
76- } )
108+ }
109+ const errors = await ( await fetch ( makeEndpoint ( "/api/analyze" ) , errorsBody ) ) . json ( )
77110assertIdAndPath ( errors )
78111assert . deepEqual ( errors . points , [ [ [ 14.97651307489794 ] , "2.3" ] ] )
112+ const analyzeAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/analyze" , errorsBody )
113+ assertIdAndPath ( analyzeAsyncResult )
114+ assert . deepEqual ( analyzeAsyncResult . points , [ [ [ 14.97651307489794 ] , "2.3" ] ] )
115+
116+ // Exacts endpoint
117+ const exactsBody = {
118+ method : 'POST' , body : JSON . stringify ( {
119+ formula : FPCoreFormula2 , sample : eval_sample
120+ } )
121+ }
122+ const exacts = await ( await fetch ( makeEndpoint ( "/api/exacts" ) , exactsBody ) ) . json ( )
123+ assertIdAndPath ( exacts )
124+ assert . deepEqual ( exacts . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
125+ const exactsAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/exacts" , exactsBody )
126+ assertIdAndPath ( exactsAsyncResult )
127+ assert . deepEqual ( exactsAsyncResult . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
128+
129+ // Calculate endpoint
130+ const calculateBody = {
131+ method : 'POST' , body : JSON . stringify ( {
132+ formula : FPCoreFormula2 , sample : eval_sample
133+ } )
134+ }
135+ const calculate = await ( await fetch ( makeEndpoint ( "/api/calculate" ) , calculateBody ) ) . json ( )
136+ assertIdAndPath ( calculate )
137+ assert . deepEqual ( calculate . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
138+ const calculateAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/calculate" , calculateBody )
139+ assertIdAndPath ( calculateAsyncResult )
140+ assert . deepEqual ( calculateAsyncResult . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
79141
80142// Local error endpoint
81- const localError = await callHerbie ( "/api/localerror" , {
143+ const localErrorBody = {
82144 method : 'POST' , body : JSON . stringify ( {
83145 formula : FPCoreFormula , sample : sample2 . points
84146 } )
85- } )
147+ }
148+ const localError = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , localErrorBody ) ) . json ( )
86149assertIdAndPath ( localError )
87150assert . equal ( localError . tree [ 'avg-error' ] > 0 , true )
151+ const localErrorAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/localerror" , localErrorBody )
152+ assertIdAndPath ( localErrorAsyncResult )
153+ assert . equal ( localErrorAsyncResult . tree [ 'avg-error' ] > 0 , true )
88154
89- const json1 = JSON . stringify ( {
90- formula : FPCoreFormula , sample : [ [ [ 2.852044568544089e-150 ] , 1e+308 ] ] , seed : 5
91- } )
92- const json2 = JSON . stringify ( {
93- formula : FPCoreFormula , sample : [ [ [ 1.5223342548065899e-15 ] , 1e+308 ] ] , seed : 5
94- } )
95- const localError1 = await callHerbie ( "/api/localerror" , {
96- method : 'POST' , body : json1
97- } )
98- const localError2 = await callHerbie ( "/api/localerror" , {
99- method : 'POST' , body : json2
100- } )
155+ const localError1 = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , {
156+ method : 'POST' , body : JSON . stringify ( {
157+ formula : FPCoreFormula , sample : [ [ [ 2.852044568544089e-150 ] , 1e+308 ] ] , seed : 5
158+ } )
159+ } ) ) . json ( )
160+ const localError2 = await ( await fetch ( makeEndpoint ( "/api/localerror" ) , {
161+ method : 'POST' , body : JSON . stringify ( {
162+ formula : FPCoreFormula , sample : [ [ [ 1.5223342548065899e-15 ] , 1e+308 ] ] , seed : 5
163+ } )
164+ } ) ) . json ( )
101165// Test that different sample points produce different job ids ensuring that different results are served for these inputs.
102166assert . notEqual ( localError1 . job , localError2 . job )
103167
104168// Alternatives endpoint
105-
106- const alternatives = await callHerbie ( "/api/alternatives" , {
169+ const altBody = {
107170 method : 'POST' , body : JSON . stringify ( {
108171 formula : FPCoreFormula , sample : [ [ [
109172 14.97651307489794
110173 ] , 0.12711304680349078 ] ]
111174 } )
112- } )
175+ }
176+ const alternatives = await ( await fetch ( makeEndpoint ( "/api/alternatives" ) , altBody ) ) . json ( )
113177assertIdAndPath ( alternatives )
114178assert . equal ( Array . isArray ( alternatives . alternatives ) , true )
115-
116- //Explanations endpoint
117- const sampleExp = ( await ( await fetch ( 'http://127.0.0.1:8000/api/sample' , { method : 'POST' , body : JSON . stringify ( { formula : FPCoreFormula2 , seed : 5 } ) } ) ) . json ( ) )
118- const explain = await callHerbie ( "/api/explanations" , {
119- method : 'POST' , body : JSON . stringify ( {
120- formula : FPCoreFormula , sample : sampleExp . points
121- } )
122- } )
123- assertIdAndPath ( explain )
124- assert . equal ( explain . explanation . length > 0 , true , 'explanation should not be empty' ) ;
125- // Exacts endpoint
126- const exacts = await callHerbie ( "/api/exacts" , {
127- method : 'POST' , body : JSON . stringify ( {
128- formula : FPCoreFormula2 , sample : eval_sample
129- } )
130- } )
131- assertIdAndPath ( exacts )
132- assert . deepEqual ( exacts . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
133-
134- // Calculate endpoint
135- const calculate = await callHerbie ( "/api/calculate" , {
136- method : 'POST' , body : JSON . stringify ( {
137- formula : FPCoreFormula2 , sample : eval_sample
138- } )
139- } )
140- assertIdAndPath ( calculate )
141- assert . deepEqual ( calculate . points , [ [ [ 1 ] , - 1.4142135623730951 ] ] )
179+ const alternativesAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/alternatives" , altBody )
180+ assertIdAndPath ( alternativesAsyncResult )
181+ assert . equal ( Array . isArray ( alternativesAsyncResult . alternatives ) , true )
142182
143183// Cost endpoint
144- const cost = await callHerbie ( "/api/cost" , {
184+ const costBody = {
145185 method : 'POST' , body : JSON . stringify ( {
146186 formula : FPCoreFormula2 , sample : eval_sample
147187 } )
148- } )
188+ }
189+ const cost = await ( await fetch ( makeEndpoint ( "/api/cost" ) , costBody ) ) . json ( )
149190assertIdAndPath ( cost )
150191assert . equal ( cost . cost > 0 , true )
192+ const costAsyncResult = await callAsyncAndWaitJSONResult ( "/api/start/cost" , costBody )
193+ assertIdAndPath ( costAsyncResult )
194+ assert . equal ( costAsyncResult . cost > 0 , true )
151195
152- // // MathJS endpoint
153- const mathjs = await callHerbie ( "/api/mathjs" , {
154- method : 'POST' ,
155- body : JSON . stringify ( { formula : FPCoreFormula } )
156- } )
196+ // MathJS endpoint
197+ const mathjs = await ( await fetch ( makeEndpoint ( "/api/mathjs" ) , {
198+ method : 'POST' , body : JSON . stringify ( { formula : FPCoreFormula } )
199+ } ) ) . json ( )
157200assert . equal ( mathjs . mathjs , "sqrt(x + 1.0) - sqrt(x)" )
158201
159202// Translate endpoint
@@ -170,52 +213,46 @@ const expectedExpressions = {
170213}
171214
172215for ( const e in expectedExpressions ) {
173- const translatedExpr = await callHerbie ( "/api/translate" , {
216+ const translatedExpr = await ( await fetch ( makeEndpoint ( "/api/translate" ) , {
174217 method : 'POST' , body : JSON . stringify (
175218 { formula : FPCoreFormula , language : e } )
176- } )
219+ } ) ) . json ( )
177220
178221 assert . equal ( translatedExpr . result , expectedExpressions [ e ] )
179222}
180223
181- let counter = 0
182- let cap = 100
183- // Check status endpoint
184- let checkStatus = await callHerbie ( path , { method : 'GET' } )
185- /*
186- This is testing if the /improve-start test at the beginning has been completed. The cap and counter is a sort of timeout for the test. Ends up being 10 seconds max.
187- */
188- while ( checkStatus . status != 201 && counter < cap ) {
189- counter += 1
190- checkStatus = await callHerbie ( path , { method : 'GET' } )
191- await new Promise ( r => setTimeout ( r , 100 ) ) ; // ms
192- }
193- assert . equal ( checkStatus . statusText , 'Job complete' )
194-
195224// Results.json endpoint
196- const jsonResults = await callHerbie ( "/results.json" , { method : 'GET' } )
225+ const jsonResults = await ( await fetch ( makeEndpoint ( "/results.json" ) , { method : 'GET' } ) ) . json ( )
197226
198227// Basic test that checks that there are the two results after the above test.
199228// TODO add a way to reset the results.json file?
200229assert . equal ( jsonResults . tests . length , 2 )
201230
202- async function callHerbie ( endPoint , body ) {
203- const url = new URL ( `http://127.0.0.1:8000${ endPoint } ` )
204- const pathname = url . pathname
205- const rsp = await fetch ( url , body )
206- if ( pathname == "/improve" ||
207- pathname == "/improve-start" ||
208- pathname . includes ( "check-status" ) ||
209- pathname . includes ( "timeline" ) ||
210- pathname . includes ( "sample" ) ||
211- pathname == "/up" ) {
212- return rsp
213- } else {
214- return rsp . json ( )
215- }
231+ // Helper Functions
232+ function makeEndpoint ( endpoint ) {
233+ return new URL ( `http://127.0.0.1:8000${ endpoint } ` )
216234}
217235
218236function assertIdAndPath ( json ) {
219237 assert . equal ( json . job . length > 0 , true )
220238 assert . equal ( json . path . includes ( "." ) , true )
239+ }
240+
241+ async function callAsyncAndWaitJSONResult ( endpoint , body ) {
242+ let counter = 0
243+ let cap = 100
244+ // Check status endpoint
245+ let jobInfo = await fetch ( makeEndpoint ( endpoint ) , body )
246+ /*
247+ The cap and counter is a sort of timeout for the test. Ends up being 10 seconds max.
248+ */
249+ const jobJSON = await jobInfo . json ( )
250+ const checkStatus = await fetch ( makeEndpoint ( `/check-status/${ jobJSON . job } ` ) , { method : 'GET' } )
251+ while ( checkStatus . status != 201 && counter < cap ) {
252+ counter += 1
253+ checkStatus = await fetch ( makeEndpoint ( `/check-status/${ jobJSON . job } ` ) , { method : 'GET' } )
254+ await new Promise ( r => setTimeout ( r , 100 ) ) ; // ms
255+ }
256+ const result = await fetch ( makeEndpoint ( `/api/result/${ jobJSON . job } ` ) , { method : 'GET' } )
257+ return await result . json ( )
221258}
0 commit comments