@@ -154,6 +154,154 @@ describe('functions', () => {
154154 matches ( { } , matcher )
155155 } ) . toThrow ( )
156156 } )
157+
158+ describe ( 'semver()' , ( ) => {
159+ test ( 'basic >= comparison works' , ( ) => {
160+ // FQL: semver("1.0.1", ">=", "1.0.0")
161+ matcher . ir = `["semver", {"value": "1.0.1"}, {"value": ">="}, {"value": "1.0.0"}]`
162+ expect ( matches ( { } , matcher ) ) . toBe ( true )
163+
164+ // FQL: semver("1.0.0", ">=", "1.0.0")
165+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": ">="}, {"value": "1.0.0"}]`
166+ expect ( matches ( { } , matcher ) ) . toBe ( true )
167+
168+ // FQL: semver("0.9.9", ">=", "1.0.0")
169+ matcher . ir = `["semver", {"value": "0.9.9"}, {"value": ">="}, {"value": "1.0.0"}]`
170+ expect ( matches ( { } , matcher ) ) . toBe ( false )
171+ } )
172+
173+ test ( '> comparison works' , ( ) => {
174+ // FQL: semver("2.0.0", ">", "1.9.9")
175+ matcher . ir = `["semver", {"value": "2.0.0"}, {"value": ">"}, {"value": "1.9.9"}]`
176+ expect ( matches ( { } , matcher ) ) . toBe ( true )
177+
178+ // FQL: semver("1.0.0", ">", "1.0.0")
179+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": ">"}, {"value": "1.0.0"}]`
180+ expect ( matches ( { } , matcher ) ) . toBe ( false )
181+ } )
182+
183+ test ( '< comparison works' , ( ) => {
184+ // FQL: semver("1.5.0", "<", "2.0.0")
185+ matcher . ir = `["semver", {"value": "1.5.0"}, {"value": "<"}, {"value": "2.0.0"}]`
186+ expect ( matches ( { } , matcher ) ) . toBe ( true )
187+
188+ // FQL: semver("2.0.0", "<", "2.0.0")
189+ matcher . ir = `["semver", {"value": "2.0.0"}, {"value": "<"}, {"value": "2.0.0"}]`
190+ expect ( matches ( { } , matcher ) ) . toBe ( false )
191+ } )
192+
193+ test ( '<= comparison works' , ( ) => {
194+ // FQL: semver("1.0.0", "<=", "1.0.0")
195+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": "<="}, {"value": "1.0.0"}]`
196+ expect ( matches ( { } , matcher ) ) . toBe ( true )
197+
198+ // FQL: semver("1.0.1", "<=", "1.0.0")
199+ matcher . ir = `["semver", {"value": "1.0.1"}, {"value": "<="}, {"value": "1.0.0"}]`
200+ expect ( matches ( { } , matcher ) ) . toBe ( false )
201+ } )
202+
203+ test ( '= comparison works' , ( ) => {
204+ // FQL: semver("1.2.3", "=", "1.2.3")
205+ matcher . ir = `["semver", {"value": "1.2.3"}, {"value": "="}, {"value": "1.2.3"}]`
206+ expect ( matches ( { } , matcher ) ) . toBe ( true )
207+
208+ // FQL: semver("1.2.3", "=", "1.2.4")
209+ matcher . ir = `["semver", {"value": "1.2.3"}, {"value": "="}, {"value": "1.2.4"}]`
210+ expect ( matches ( { } , matcher ) ) . toBe ( false )
211+ } )
212+
213+ test ( '!= comparison works' , ( ) => {
214+ // FQL: semver("1.2.3", "!=", "1.2.4")
215+ matcher . ir = `["semver", {"value": "1.2.3"}, {"value": "!="}, {"value": "1.2.4"}]`
216+ expect ( matches ( { } , matcher ) ) . toBe ( true )
217+
218+ // FQL: semver("1.2.3", "!=", "1.2.3")
219+ matcher . ir = `["semver", {"value": "1.2.3"}, {"value": "!="}, {"value": "1.2.3"}]`
220+ expect ( matches ( { } , matcher ) ) . toBe ( false )
221+ } )
222+
223+ test ( 'partial versions work' , ( ) => {
224+ // FQL: semver("2", ">", "1.9.9")
225+ matcher . ir = `["semver", {"value": "2"}, {"value": ">"}, {"value": "1.9.9"}]`
226+ expect ( matches ( { } , matcher ) ) . toBe ( true )
227+
228+ // FQL: semver("1.5", ">", "1.4.9")
229+ matcher . ir = `["semver", {"value": "1.5"}, {"value": ">"}, {"value": "1.4.9"}]`
230+ expect ( matches ( { } , matcher ) ) . toBe ( true )
231+
232+ // FQL: semver("1.5", "=", "1.5.0")
233+ matcher . ir = `["semver", {"value": "1.5"}, {"value": "="}, {"value": "1.5.0"}]`
234+ expect ( matches ( { } , matcher ) ) . toBe ( true )
235+
236+ // FQL: semver("2.0", "=", "2")
237+ matcher . ir = `["semver", {"value": "2.0"}, {"value": "="}, {"value": "2"}]`
238+ expect ( matches ( { } , matcher ) ) . toBe ( true )
239+ } )
240+
241+ test ( 'version precedence works correctly' , ( ) => {
242+ // Major version takes precedence
243+ // FQL: semver("2.0.0", ">", "1.99.99")
244+ matcher . ir = `["semver", {"value": "2.0.0"}, {"value": ">"}, {"value": "1.99.99"}]`
245+ expect ( matches ( { } , matcher ) ) . toBe ( true )
246+
247+ // Minor version takes precedence
248+ // FQL: semver("1.10.0", ">", "1.9.0")
249+ matcher . ir = `["semver", {"value": "1.10.0"}, {"value": ">"}, {"value": "1.9.0"}]`
250+ expect ( matches ( { } , matcher ) ) . toBe ( true )
251+
252+ // Patch version comparison
253+ // FQL: semver("1.0.10", ">", "1.0.9")
254+ matcher . ir = `["semver", {"value": "1.0.10"}, {"value": ">"}, {"value": "1.0.9"}]`
255+ expect ( matches ( { } , matcher ) ) . toBe ( true )
256+ } )
257+
258+ test ( 'null handling works' , ( ) => {
259+ // FQL: semver(null, ">=", "1.0.0")
260+ matcher . ir = `["semver", {"value": null}, {"value": ">="}, {"value": "1.0.0"}]`
261+ expect ( matches ( { } , matcher ) ) . toBe ( false )
262+
263+ // FQL: semver("1.0.0", ">=", null)
264+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": ">="}, {"value": null}]`
265+ expect ( matches ( { } , matcher ) ) . toBe ( false )
266+ } )
267+
268+ test ( 'invalid inputs return false' , ( ) => {
269+ // Invalid version format
270+ // FQL: semver("invalid", ">=", "1.0.0")
271+ matcher . ir = `["semver", {"value": "invalid"}, {"value": ">="}, {"value": "1.0.0"}]`
272+ expect ( matches ( { } , matcher ) ) . toBe ( false )
273+
274+ // Invalid version format
275+ // FQL: semver("1.0.0", ">=", "invalid")
276+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": ">="}, {"value": "invalid"}]`
277+ expect ( matches ( { } , matcher ) ) . toBe ( false )
278+
279+ // Invalid operator
280+ // FQL: semver("1.0.0", "~=", "1.0.0")
281+ matcher . ir = `["semver", {"value": "1.0.0"}, {"value": "~="}, {"value": "1.0.0"}]`
282+ expect ( matches ( { } , matcher ) ) . toBe ( false )
283+
284+ // Too many parts
285+ // FQL: semver("1.2.3.4", ">=", "1.0.0")
286+ matcher . ir = `["semver", {"value": "1.2.3.4"}, {"value": ">="}, {"value": "1.0.0"}]`
287+ expect ( matches ( { } , matcher ) ) . toBe ( false )
288+
289+ // Non-numeric parts
290+ // FQL: semver("1.a.0", ">=", "1.0.0")
291+ matcher . ir = `["semver", {"value": "1.a.0"}, {"value": ">="}, {"value": "1.0.0"}]`
292+ expect ( matches ( { } , matcher ) ) . toBe ( false )
293+ } )
294+
295+ test ( 'works with event properties' , ( ) => {
296+ // FQL: semver(app_version, ">=", "1.0.0")
297+ matcher . ir = `["semver", "app_version", {"value": ">="}, {"value": "1.0.0"}]`
298+ simpleEvent . app_version = '1.5.0'
299+ expect ( matches ( simpleEvent , matcher ) ) . toBe ( true )
300+
301+ simpleEvent . app_version = '0.9.0'
302+ expect ( matches ( simpleEvent , matcher ) ) . toBe ( false )
303+ } )
304+ } )
157305} )
158306
159307describe ( 'arrays' , ( ) => {
0 commit comments