From 6dc2c1f6dba3c1cabf45e9acab24b47b6b897aa6 Mon Sep 17 00:00:00 2001 From: kitsunecafe Date: Fri, 15 Nov 2024 00:36:19 -0600 Subject: [PATCH] testing :3 --- src/query/match.js | 17 ++++-- src/query/types.js | 16 ++++++ tests/query/common.test.js | 114 ++++++++++++++++++++++--------------- tests/query/match.test.js | 18 +++--- tests/utils.js | 5 +- 5 files changed, 110 insertions(+), 60 deletions(-) diff --git a/src/query/match.js b/src/query/match.js index 71211f2..f9e7eb9 100644 --- a/src/query/match.js +++ b/src/query/match.js @@ -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)) diff --git a/src/query/types.js b/src/query/types.js index c2057a3..b672123 100644 --- a/src/query/types.js +++ b/src/query/types.js @@ -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) diff --git a/tests/query/common.test.js b/tests/query/common.test.js index 28b6f27..8a5ee8a 100644 --- a/tests/query/common.test.js +++ b/tests/query/common.test.js @@ -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')) + }) + + assert.parseOk(baseValue, 'ginger.snaps', ([actual]) => { + assert.deepEqual(actual, new ObjectPath(new Identifier('ginger'), new Identifier('snaps'))) + }) }) - 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'))) + 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) + }) }) }) diff --git a/tests/query/match.test.js b/tests/query/match.test.js index aad9ddf..1da9446 100644 --- a/tests/query/match.test.js +++ b/tests/query/match.test.js @@ -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'), ] diff --git a/tests/utils.js b/tests/utils.js index 836c96e..1839b8e 100644 --- a/tests/utils.js +++ b/tests/utils.js @@ -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)