testing :3
This commit is contained in:
parent
523a64869a
commit
6dc2c1f6db
5 changed files with 110 additions and 60 deletions
|
@ -1,7 +1,7 @@
|
|||
import { identifier, literal, Symbol, ws } from './common.js'
|
||||
import { Node, Edge, KeyValuePair, Label, Name } from './types.js'
|
||||
import { Node, Edge, KeyValuePair, Label, Name, Direction, DirectedEdge } from './types.js'
|
||||
import { curry, is } from '../fn.js'
|
||||
import { many, maybe, map, seq, skip, between, noCaseString, separated, list } from '../parser.js'
|
||||
import { many, maybe, map, seq, skip, between, noCaseString, separated, list, any } from '../parser.js'
|
||||
|
||||
const { Bracket, Colon, Comma, Hyphen } = Symbol
|
||||
|
||||
|
@ -45,8 +45,17 @@ export const edge = map(
|
|||
bracketed(seq(id, maybe(properties)), Bracket.Square)
|
||||
)
|
||||
|
||||
const rightArrow = seq(Hyphen, Bracket.Angle.Right)
|
||||
const relationship = seq(skip(Hyphen), edge, skip(rightArrow), node)
|
||||
const arrowRight = map(() => Direction.Right, seq(Hyphen, Bracket.Angle.Right))
|
||||
const arrowLeft = map(() => Direction.Left, seq(Bracket.Angle.Left, Hyphen))
|
||||
const relationshipRight = map(([edge, direction]) =>
|
||||
DirectedEdge.fromEdge(edge, direction),
|
||||
seq(skip(Hyphen), edge, arrowRight)
|
||||
)
|
||||
const relationshipLeft = map(([direction, edge]) =>
|
||||
DirectedEdge.fromEdge(edge, direction),
|
||||
seq(arrowLeft, edge, skip(Hyphen))
|
||||
)
|
||||
const relationship = seq(any(relationshipRight, relationshipLeft), node)
|
||||
|
||||
const keyword = noCaseString('match')
|
||||
const params = seq(node, many(relationship))
|
||||
|
|
|
@ -30,12 +30,28 @@ export class Node extends GraphObject {
|
|||
}
|
||||
}
|
||||
|
||||
export const Direction = Object.freeze({
|
||||
Left: 0,
|
||||
Right: 1
|
||||
})
|
||||
|
||||
export class Edge extends GraphObject {
|
||||
constructor(name, label, properties) {
|
||||
super(name, label, properties)
|
||||
}
|
||||
}
|
||||
|
||||
export class DirectedEdge extends Edge {
|
||||
constructor(name, label, direction, properties) {
|
||||
super(name, label, properties)
|
||||
this.direction = direction
|
||||
}
|
||||
|
||||
static fromEdge(edge, direction) {
|
||||
return new DirectedEdge(edge.name, edge.label, direction, edge.properties)
|
||||
}
|
||||
}
|
||||
|
||||
export class ReturnValues extends Array {
|
||||
constructor(...args) {
|
||||
super(...args)
|
||||
|
|
|
@ -1,59 +1,83 @@
|
|||
import { describe, it } from 'node:test'
|
||||
import assert from 'node:assert'
|
||||
import { parse } from '../../src/parser.js'
|
||||
import * as Common from '../../src/query/common.js'
|
||||
import assert from '../assert.js'
|
||||
import { Alias, Identifier, Literal, ObjectPath } from '../../src/query/types.js'
|
||||
import { baseValue, literal, value } from '../../src/query/common.js'
|
||||
|
||||
const v = r => r.unwrap()[0][0]
|
||||
describe('common parser library', () => {
|
||||
const literals = [
|
||||
['true', true],
|
||||
['false', false],
|
||||
['5', 5],
|
||||
['+16', 16],
|
||||
['-710', -710],
|
||||
['2.1', 2.1],
|
||||
['+0.001', 0.001],
|
||||
['-22.69', -22.69],
|
||||
['"hellion"', 'hellion'],
|
||||
]
|
||||
|
||||
it('literals should match literal types', () => {
|
||||
const tBool = parse(Common.literal, 'true')
|
||||
const fBool = parse(Common.literal, 'false')
|
||||
const uint = parse(Common.literal, '5')
|
||||
const posInt = parse(Common.literal, '+16')
|
||||
const negInt = parse(Common.literal, '-710')
|
||||
const ufloat = parse(Common.literal, '2.1')
|
||||
const posFloat = parse(Common.literal, '+0.1')
|
||||
const negFloat = parse(Common.literal, '-12.01')
|
||||
const string = parse(Common.literal, '"akjsdfuaio"')
|
||||
|
||||
assert.deepEqual(v(tBool), new Literal(true))
|
||||
assert.deepEqual(v(fBool), new Literal(false))
|
||||
assert.deepEqual(v(uint), new Literal(5))
|
||||
assert.deepEqual(v(posInt), new Literal(16))
|
||||
assert.deepEqual(v(negInt), new Literal(-710))
|
||||
assert.deepEqual(v(ufloat), new Literal(2.1))
|
||||
assert.deepEqual(v(posFloat), new Literal(0.1))
|
||||
assert.deepEqual(v(negFloat), new Literal(-12.01))
|
||||
assert.deepEqual(v(string), new Literal('akjsdfuaio'))
|
||||
literals.forEach(([input, expected]) => {
|
||||
assert.parseOk(literal, input, ([actual]) => {
|
||||
assert.deepEqual(actual, new Literal(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('value should match literals', () => {
|
||||
const bool = parse(Common.baseValue, 'false')
|
||||
const uint = parse(Common.baseValue, '11')
|
||||
const float = parse(Common.baseValue, '0.15')
|
||||
const str = parse(Common.baseValue, '"abc"')
|
||||
|
||||
assert.deepEqual(v(bool), new Literal(false))
|
||||
assert.deepEqual(v(uint), new Literal(11))
|
||||
assert.deepEqual(v(float), new Literal(0.15))
|
||||
assert.deepEqual(v(str), new Literal('abc'))
|
||||
it('baseValue should match literals', () => {
|
||||
literals.forEach(([input, expected]) => {
|
||||
assert.parseOk(baseValue, input, ([actual]) => {
|
||||
assert.deepEqual(actual, new Literal(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('value should match variables', () => {
|
||||
const identifier = parse(Common.baseValue, 'test')
|
||||
const accessor = parse(Common.baseValue, 'test.value')
|
||||
assert.deepEqual(v(identifier), new Identifier('test'))
|
||||
assert.deepEqual(v(accessor), new ObjectPath(new Identifier('test'), new Identifier('value')))
|
||||
it('baseValue should parse identifiers', () => {
|
||||
assert.parseOk(baseValue, 'red', ([actual]) => {
|
||||
assert.deepEqual(actual, new Identifier('red'))
|
||||
})
|
||||
|
||||
it('aliases should work i hope', () => {
|
||||
const noAlias = parse(Common.value, 'test')
|
||||
const aliased1 = parse(Common.value, 'crybaby AS cb')
|
||||
const aliased2 = parse(Common.value, 'property.name AS name')
|
||||
assert.deepEqual(v(noAlias), new Identifier('test'))
|
||||
assert.deepEqual(v(aliased1), new Alias(new Identifier('crybaby'), new Identifier('cb')))
|
||||
assert.deepEqual(v(aliased2), new Alias(new ObjectPath(new Identifier('property'), new Identifier('name')), new Identifier('name')))
|
||||
assert.parseOk(baseValue, 'ginger.snaps', ([actual]) => {
|
||||
assert.deepEqual(actual, new ObjectPath(new Identifier('ginger'), new Identifier('snaps')))
|
||||
})
|
||||
})
|
||||
|
||||
it('value should parse literals', () => {
|
||||
literals.forEach(([input, expected]) => {
|
||||
assert.parseOk(value, input, ([actual]) => {
|
||||
assert.deepEqual(actual, new Literal(expected))
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
it('value should parse identifiers', () => {
|
||||
assert.parseOk(baseValue, 'violet', ([actual]) => {
|
||||
assert.deepEqual(actual, new Identifier('violet'))
|
||||
})
|
||||
|
||||
assert.parseOk(baseValue, 'monster.girl', ([actual]) => {
|
||||
assert.deepEqual(actual, new ObjectPath(new Identifier('monster'), new Identifier('girl')))
|
||||
})
|
||||
})
|
||||
|
||||
it('value should parse aliases', () => {
|
||||
assert.parseOk(value, '19589 AS sybil', ([actual]) => {
|
||||
assert.deepEqual(actual, new Alias(new Literal(19589), new Identifier('sybil')))
|
||||
})
|
||||
|
||||
assert.parseOk(value, '3.14159 AS PI', ([actual]) => {
|
||||
assert.deepEqual(actual, new Alias(new Literal(3.14159), new Identifier('PI')))
|
||||
})
|
||||
|
||||
assert.parseOk(value, 'crybaby AS cb', ([actual]) => {
|
||||
assert.deepEqual(actual, new Alias(new Identifier('crybaby'), new Identifier('cb')))
|
||||
})
|
||||
|
||||
assert.parseOk(value, 'rowan.containment.isBreached AS rawrnEscaped', ([actual]) => {
|
||||
const obj = new ObjectPath(new Identifier('rowan'), new Identifier('containment'), new Identifier('isBreached'))
|
||||
const alias = new Alias(obj, new Identifier('rawrnEscaped'))
|
||||
assert.deepEqual(actual, alias)
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import { describe, it } from 'node:test'
|
||||
import assert from '../assert.js'
|
||||
import { node, edge, statement } from '../../src/query/match.js'
|
||||
import { makeEdge, makeNode } from '../utils.js'
|
||||
import { makeDirectedEdge, makeEdge, makeNode } from '../utils.js'
|
||||
|
||||
describe('node', () => {
|
||||
it('should match a node with a name, label, and properties', () => {
|
||||
|
@ -38,12 +38,12 @@ describe('node', () => {
|
|||
describe('edge', () => {
|
||||
it('should match a relationship with a name, label, properties', () => {
|
||||
assert.parseOk(edge, '[name:Label]', ([actual]) => {
|
||||
const expected = makeEdge('name', 'Label', [])
|
||||
const expected = makeEdge('name', 'Label')
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
|
||||
assert.parseOk(edge, '[:Label]', ([actual]) => {
|
||||
const expected = makeEdge(undefined, 'Label', [])
|
||||
const expected = makeEdge(undefined, 'Label')
|
||||
assert.deepEqual(actual, expected)
|
||||
})
|
||||
|
||||
|
@ -88,7 +88,7 @@ describe('MATCH keyword', () => {
|
|||
assert.parseOk(statement, 'MATCH (:Node)-[:Edge]->(:Node)', actual => {
|
||||
const expected = [
|
||||
makeNode(undefined, 'Node'),
|
||||
makeEdge(undefined, 'Edge'),
|
||||
makeDirectedEdge(undefined, 'Edge', 1),
|
||||
makeNode(undefined, 'Node'),
|
||||
]
|
||||
|
||||
|
@ -98,7 +98,7 @@ describe('MATCH keyword', () => {
|
|||
assert.parseOk(statement, 'MATCH (a:Node)-[e:Edge]->(b:Node)', actual => {
|
||||
const expected = [
|
||||
makeNode('a', 'Node'),
|
||||
makeEdge('e', 'Edge'),
|
||||
makeDirectedEdge('e', 'Edge', 1),
|
||||
makeNode('b', 'Node'),
|
||||
]
|
||||
|
||||
|
@ -108,7 +108,7 @@ describe('MATCH keyword', () => {
|
|||
assert.parseOk(statement, 'MATCH (a:Node { db: 0.7 })-[e:Edge]->(b:Node { db: 0.95 })', actual => {
|
||||
const expected = [
|
||||
makeNode('a', 'Node', [['db', 0.7]]),
|
||||
makeEdge('e', 'Edge'),
|
||||
makeDirectedEdge('e', 'Edge', 1),
|
||||
makeNode('b', 'Node', [['db', 0.95]]),
|
||||
]
|
||||
|
||||
|
@ -118,7 +118,7 @@ describe('MATCH keyword', () => {
|
|||
assert.parseOk(statement, 'MATCH (:Node { db: 0.7 })-[:Edge]->(:Node { db: 0.95 })', actual => {
|
||||
const expected = [
|
||||
makeNode(undefined, 'Node', [['db', 0.7]]),
|
||||
makeEdge(undefined, 'Edge'),
|
||||
makeDirectedEdge(undefined, 'Edge', 1),
|
||||
makeNode(undefined, 'Node', [['db', 0.95]]),
|
||||
]
|
||||
|
||||
|
@ -130,9 +130,9 @@ describe('MATCH keyword', () => {
|
|||
assert.parseOk(statement, 'MATCH (player:Player)-[:Knows]->(a:NPC)-[:Knows]->(b:NPC)', actual => {
|
||||
const expected = [
|
||||
makeNode('player', 'Player'),
|
||||
makeEdge(undefined, 'Knows'),
|
||||
makeDirectedEdge(undefined, 'Knows', 1),
|
||||
makeNode('a', 'NPC'),
|
||||
makeEdge(undefined, 'Knows'),
|
||||
makeDirectedEdge(undefined, 'Knows', 1),
|
||||
makeNode('b', 'NPC'),
|
||||
]
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { KeyValuePair, Name, Label, Identifier, Literal, Edge, Node } from '../src/query/types.js'
|
||||
import { KeyValuePair, Name, Label, Identifier, Literal, Edge, Node, DirectedEdge } from '../src/query/types.js'
|
||||
|
||||
export const keyValuePair = ([k, v]) => new KeyValuePair(new Name(new Identifier(k)), new Literal(v))
|
||||
|
||||
|
@ -9,5 +9,6 @@ export const graphObject = (name, label, props = [], Type = Node) => new Type(
|
|||
)
|
||||
|
||||
export const makeNode = graphObject
|
||||
export const makeEdge = (name, label, props) => graphObject(name, label, props, Edge)
|
||||
export const makeEdge = (name, label, props = []) => graphObject(name, label, props, Edge)
|
||||
export const makeDirectedEdge = (name, label, direction, props = []) => DirectedEdge.fromEdge(makeEdge(name, label, props), direction)
|
||||
|
||||
|
|
Loading…
Reference in a new issue