def test_links(): f1 = Mock(return_value=[1]) f2 = Mock(return_value=[2]) f3 = Mock(return_value=[['boners']]) f4 = Mock(return_value=[['julio']]) graph = Graph([ Node('a', [ Field('d', None, f3), Field('e', None, f4), ]), Root([ Link('b', Sequence[TypeRef['a']], f1, requires=None), Link('c', Sequence[TypeRef['a']], f2, requires=None), ]), ]) result = execute(graph, build([Q.b[Q.d], Q.c[Q.e]])) check_result(result, {'b': [{'d': 'boners'}], 'c': [{'e': 'julio'}]}) assert result.index == {'a': {1: {'d': 'boners'}, 2: {'e': 'julio'}}} f1.assert_called_once_with() f2.assert_called_once_with() with reqs_eq_patcher(): f3.assert_called_once_with([query.Field('d')], [1]) f4.assert_called_once_with([query.Field('e')], [2])
def testTypeRef(self): self.assertDumps( Edge(None, [ Edge('Foo', [ Field('a', StringType, noop), ]), Edge('Bar', [ Field('b', StringType, noop), Link('c', None, 'Foo', noop, to_list=False), ]), Edge('Baz', [ Field('d', StringType, noop), Link('e', None, 'Foo', noop, to_list=True), ]), ]), """ type Foo Record :a String type Bar Record :b String :c Foo type Baz Record :d String :e List Foo """, )
def test_type_ref(): assert_dumps( Graph([ Node('xeric', [ Field('derrida', String, _), ]), Node('amb', [ Field('loor', String, _), Link('cressy', TypeRef['xeric'], _, requires=None), ]), Node('offeree', [ Field('abila', String, _), Link('ferber', Sequence[TypeRef['xeric']], _, requires=None), ]), ]), """ type xeric Record :derrida String type amb Record :loor String :cressy xeric type offeree Record :abila String :ferber List xeric """, )
def test_links(): fb = Mock(return_value=[1]) fc = Mock(return_value=[2]) fi = Mock(return_value=[3]) fd = Mock(return_value=[['boners']]) fe = Mock(return_value=[['julio']]) graph = Graph([ Node('a', [ Field('d', None, fd), Field('e', None, fe), ]), Root([ Field('i', None, fi), Link('b', Sequence[TypeRef['a']], fb, requires=None), Link('c', Sequence[TypeRef['a']], fc, requires='i'), ]), ]) result = execute(graph, build([Q.b[Q.d], Q.c[Q.e]])) check_result(result, {'b': [{'d': 'boners'}], 'c': [{'e': 'julio'}]}) fi.assert_called_once_with([q.Field('i')]) fb.assert_called_once_with() fc.assert_called_once_with(3) fd.assert_called_once_with([q.Field('d')], [1]) fe.assert_called_once_with([q.Field('e')], [2])
def test_link_optional(value, expected): graph = Graph([ Node('Bar', [ Field('baz', Integer, _), ]), Node('Foo', [ Link('bar', Optional[TypeRef['Bar']], _, requires=None), ]), Root([ Link('foo', Optional[TypeRef['Foo']], _, requires=None), ]), ]) query = build([ Q.foo[Q.bar[Q.baz, ], ], ]) index = Index() index[ROOT.node][ROOT.ident].update({ 'foo': Reference('Foo', 1), }) index['Foo'][1].update({ 'bar': value, }) index['Bar'][2].update({'baz': 42}) result = Proxy(index, ROOT, query) assert Denormalize(graph, result).process(query) == { 'foo': { 'bar': expected, }, }
def test_conflicting_fields(): x_data = {'xN': {'a': 42}} @listify def x_fields(fields, ids): for i in ids: yield ['{}-{}'.format(x_data[i][f.name], f.options['k']) for f in fields] graph = Graph([ Node('X', [ Field('a', None, x_fields, options=[Option('k', Integer)]), ]), Root([ Link('x1', TypeRef['X'], lambda: 'xN', requires=None), Link('x2', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x1', q.Node([q.Field('a', options={'k': 1})])), q.Link('x2', q.Node([q.Field('a', options={'k': 2})])), ])) check_result(result, { 'x1': {'a': '42-1'}, 'x2': {'a': '42-2'}, })
def test_typename(): graph = Graph([ Node('Bar', [ Field('baz', Integer, _), ]), Node('Foo', [ Link('bar', Sequence[TypeRef['Bar']], _, requires=None), ]), Root([ Link('foo', TypeRef['Foo'], _, requires=None), ]), ]) query = read(""" query { __typename foo { __typename bar { __typename baz } } } """) index = Index() index[ROOT.node][ROOT.ident].update({ 'foo': Reference('Foo', 1), }) index['Foo'][1].update({ 'bar': [Reference('Bar', 2), Reference('Bar', 3)], }) index['Bar'][2].update({ 'baz': 42 }) index['Bar'][3].update({ 'baz': 43 }) result = Proxy(index, ROOT, query) assert DenormalizeGraphQL(graph, result, 'Query').process(query) == { '__typename': 'Query', 'foo': { '__typename': 'Foo', 'bar': [ { '__typename': 'Bar', 'baz': 42, }, { '__typename': 'Bar', 'baz': 43, }, ], }, }
def test_introspection_query(): graph = Graph([ Node('flexed', [ Field('yari', Boolean, _noop, options=[ Option('membuka', Sequence[String], default=['frayed']), Option('modist', Optional[Integer], default=None, description='callow'), ]), ]), Node('decian', [ Field('dogme', Integer, _noop), Link('clarkia', Sequence[TypeRef['flexed']], _noop, requires=None), ]), Root([ Field('_cowered', String, _noop), Field('entero', Float, _noop), Link('toma', Sequence[TypeRef['decian']], _noop, requires=None), ]), ]) assert introspect(graph) == _schema([ _type('flexed', 'OBJECT', fields=[ _field('yari', _non_null(_BOOL), args=[ _ival('membuka', _seq_of(_STR), defaultValue='["frayed"]'), _ival('modist', _INT, defaultValue='null', description='callow'), ]), ]), _type('decian', 'OBJECT', fields=[ _field('dogme', _non_null(_INT)), _field('clarkia', _seq_of(_obj('flexed'))), ]), _type('Query', 'OBJECT', fields=[ _field('entero', _non_null(_FLOAT)), _field('toma', _seq_of(_obj('decian'))), ]), ])
def test_root_link_alias(): data = { 'xN': {'a': 1, 'b': 2}, } @listify def x_fields(fields, ids): for i in ids: yield [data[i][f.name] for f in fields] graph = Graph([ Node('X', [ Field('a', None, x_fields), Field('b', None, x_fields), ]), Root([ Link('x', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x', q.Node([q.Field('a')]), alias='x1'), q.Link('x', q.Node([q.Field('b')]), alias='x2'), ])) check_result(result, { 'x1': {'a': 1}, 'x2': {'b': 2}, })
def test_link_requires_missing_field(): check_errors( [ Node('foo', []), Node('bar', [ Link('link1', Sequence[TypeRef['foo']], _link_func, requires='missing1'), ]), Root([ Link('link2', Sequence[TypeRef['foo']], _link_func, requires='missing2'), ]), ], ['Link "link2" requires missing field "missing2" in the "root" node', 'Link "link1" requires missing field "missing1" in the "bar" node'], )
def test_complex_field(): engine = Engine(SyncExecutor()) def get_a(fields, ids): return [[{'s': 'bar'} for _ in fields] for _ in ids] ll_graph = Graph([ Node('Foo', [ Field('a', Record[{ 's': String }], get_a), ]), ]) foo_sg = SubGraph(ll_graph, 'Foo') hl_graph = Graph([ Node('Foo', [ Field('a', Record[{ 's': String }], foo_sg), ]), Root([ Link('foo', TypeRef['Foo'], lambda: 1, requires=None), ]), ]) result = engine.execute(hl_graph, build([Q.foo[Q.a[Q.s]]])) check_result(result, {'foo': {'a': {'s': 'bar'}}})
def test_node_complex_fields(): f1 = Mock(return_value=[1]) f2 = Mock(return_value=[[{'f': 'marshes'}]]) f3 = Mock(return_value=[[{'g': 'colline'}]]) f4 = Mock(return_value=[[[{'h': 'magi'}]]]) graph = Graph([ Node('a', [ Field('b', Optional[Record[{'f': Integer}]], f2), Field('c', Record[{'g': Integer}], f3), Field('d', Sequence[Record[{'h': Integer}]], f4), ]), Root([ Link('e', Sequence[TypeRef['a']], f1, requires=None), ]), ]) check_result( execute(graph, build([Q.e[Q.b[Q.f], Q.c[Q.g], Q.d[Q.h]]])), {'e': [{'b': {'f': 'marshes'}, 'c': {'g': 'colline'}, 'd': [{'h': 'magi'}]}]}, ) f1.assert_called_once_with() f2.assert_called_once_with( [q.Link('b', q.Node([q.Field('f')]))], [1], ) f3.assert_called_once_with( [q.Link('c', q.Node([q.Field('g')]))], [1], ) f4.assert_called_once_with( [q.Link('d', q.Node([q.Field('h')]))], [1], )
def test_pass_context_link(): f1 = pass_context(Mock(return_value=[1])) f2 = Mock(return_value=[['boners']]) graph = Graph([ Node('a', [ Field('b', None, f2), ]), Root([ Link('c', Sequence[TypeRef['a']], f1, requires=None), ]), ]) result = execute(graph, build([Q.c[Q.b]]), {'fibs': 'dossil'}) check_result(result, {'c': [{'b': 'boners'}]}) assert result.index == {'a': {1: {'b': 'boners'}}} f1.assert_called_once_with(ANY) with reqs_eq_patcher(): f2.assert_called_once_with([query.Field('b')], [1]) ctx = f1.call_args[0][0] assert isinstance(ctx, Context) assert ctx['fibs'] == 'dossil' with pytest.raises(KeyError) as err: _ = ctx['invalid'] # noqa err.match('is not specified in the query context')
def testDocs(): assert_dumps( Graph([ Node('switzer', [ Field('beatch', String, _, description="attribute beatch"), ], description="switzer description"), Node('trine', [ Field('propels', Optional[String], _, description="attribute propels"), Link('cardura', TypeRef['switzer'], _, requires=None, description="link cardura to switzer"), ], description="trine description"), Node('packrat', [ Field('pikes', String, _, description="attribute pikes"), Link('albus', Sequence[TypeRef['switzer']], _, requires=None, description="link albus to switzer"), ], description="packrat description"), ]), """ type switzer ; switzer description Record :beatch String ; attribute beatch type trine ; trine description Record :propels ; attribute propels Option String :cardura switzer ; link cardura to switzer type packrat ; packrat description Record :pikes String ; attribute pikes :albus ; link albus to switzer List switzer """, )
def test_conflicting_links(): data = { 'yA': {'a': 1, 'b': 2}, 'yB': {'a': 3, 'b': 4}, 'yC': {'a': 5, 'b': 6}, } x2y = {'xN': ['yA', 'yB', 'yC']} @listify def y_fields(fields, ids): for i in ids: yield [data[i][f.name] for f in fields] @listify def x_to_y_link(ids, options): for i in ids: yield [y for y in x2y[i] if y not in options['exclude']] graph = Graph([ Node('Y', [ Field('a', None, y_fields), Field('b', None, y_fields), ]), Node('X', [ Field('id', None, id_field), Link('y', Sequence[TypeRef['Y']], x_to_y_link, requires='id', options=[Option('exclude', None)]), ]), Root([ Link('x1', TypeRef['X'], lambda: 'xN', requires=None), Link('x2', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x1', q.Node([ q.Link('y', q.Node([q.Field('a')]), options={'exclude': ['yA']}), ])), q.Link('x2', q.Node([ q.Link('y', q.Node([q.Field('b')]), options={'exclude': ['yC']}), ])), ])) check_result(result, { 'x1': {'y': [{'a': 3}, {'a': 5}]}, 'x2': {'y': [{'b': 2}, {'b': 4}]}, })
def testDocs(self): self.assertDumps( Edge(None, [ Edge('Foo', [ Field('a', StringType, noop, doc="Attribute a"), ], doc="Some Foo explanation"), Edge('Bar', [ Field('b', StringType, noop, doc="Attribute b"), Link('c', None, 'Foo', noop, to_list=False, doc="Link c to Foo"), ], doc="Some Bar explanation"), Edge('Baz', [ Field('d', StringType, noop, doc="Attribute d"), Link('e', None, 'Foo', noop, to_list=True, doc="Link e to Foo"), ], doc="Some Baz explanation"), ]), """ type Foo ; Some Foo explanation Record :a String ; Attribute a type Bar ; Some Bar explanation Record :b String ; Attribute b :c Foo ; Link c to Foo type Baz ; Some Baz explanation Record :d String ; Attribute d :e ; Link e to Foo List Foo """, )
def test_distinct_by_name_links(): graph = Graph([ Node('X', [ Field('a', None, None), ]), Root([ Link('x1', TypeRef['X'], None, requires=None), Link('x2', TypeRef['X'], None, requires=None), ]), ]) errors = validate(graph, q.Node([ q.Link('x1', q.Node([q.Field('a')])), q.Link('x2', q.Node([q.Field('a')]), alias='x1'), ])) assert errors == [ 'Found distinct fields with the same resulting name "x1" for the ' 'node "root"' ]
def test_link_missing_node(): check_errors( [ Node('bar', [ Link('link', Sequence[TypeRef['missing']], _link_func, requires=None), ]), ], ['Link "bar.link" points to the missing node "missing"'], )
def test_process_ordered_node(): ordering = [] def f1(fields): names = tuple(f.name for f in fields) ordering.append(names) return names def f2(fields): return f1(fields) def f3(): ordering.append('x1') return 'x1' @listify def f4(fields, ids): for i in ids: yield ['{}-e'.format(i) for _ in fields] graph = Graph([ Node('X', [ Field('e', None, f4), ]), Root([ Field('a', None, f1), Field('b', None, f1), Field('c', None, f2), Field('d', None, f2), Link('x', TypeRef['X'], f3, requires=None), ]), ]) query = q.Node([ q.Field('d'), q.Field('b'), q.Field('a'), q.Link('x', q.Node([ q.Field('e'), ])), q.Field('c'), ], ordered=True) engine = Engine(SyncExecutor()) result = engine.execute(graph, query) check_result(result, { 'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd', 'x': { 'e': 'x1-e', }, }) assert ordering == [('d',), ('b', 'a'), 'x1', ('c',)]
async def test_simple_async(graph_name, sample_count): async def x_fields(fields, ids): return [[42 for _ in fields] for _ in ids] async def root_fields(fields): return [1 for _ in fields] async def x_link(): return 2 ll_graph = Graph([ Node('X', [ Field('id', None, x_fields), ]), ]) x_sg = SubGraph(ll_graph, 'X') hl_graph = Graph([ Node('X', [ Field('id', None, x_sg), ]), Root([ Field('a', None, root_fields), Link('x', TypeRef['X'], x_link, requires=None), ]), ]) hl_graph = apply(hl_graph, [AsyncGraphMetrics(graph_name)]) query = q.Node([ q.Field('a'), q.Link('x', q.Node([ q.Field('id'), ])), ]) engine = Engine(AsyncIOExecutor()) assert sample_count('Root', 'a') is None assert sample_count('Root', 'x') is None assert sample_count('X', 'id') is None result = await engine.execute(hl_graph, query) check_result(result, { 'a': 1, 'x': { 'id': 42, }, }) assert sample_count('Root', 'a') == 1.0 assert sample_count('Root', 'x') == 1.0 assert sample_count('X', 'id') == 1.0
def test_link_sequence(): graph = Graph([ Node('Bar', [ Field('baz', Integer, _), ]), Node('Foo', [ Link('bar', Sequence[TypeRef['Bar']], _, requires=None), ]), Root([ Link('foo', Sequence[TypeRef['Foo']], _, requires=None), ]), ]) query = build([ Q.foo[Q.bar[Q.baz, ], ], ]) index = Index() index[ROOT.node][ROOT.ident].update({ 'foo': [Reference('Foo', 1)], }) index['Foo'][1].update({ 'bar': [Reference('Bar', 2), Reference('Bar', 3)], }) index['Bar'][2].update({'baz': 42}) index['Bar'][3].update({'baz': 43}) result = Proxy(index, ROOT, query) assert Denormalize(graph, result).process(query) == { 'foo': [ { 'bar': [ { 'baz': 42 }, { 'baz': 43 }, ], }, ], }
def query_func(queue, task_set, edge, fields, ids): this_link = Link(THIS_LINK_NAME, None, sub_edge_name, None, True) reqs = merge(reqs_map[f.name] for f in fields) # FIXME: implement proper way to handle "this" value # and query other possible data from sub_root pattern = reqs.fields['this'].edge procs = [procs_map[f.name] for f in fields] query = Query(queue, task_set, sub_root, None) query._process_link(sub_root, this_link, pattern, None, ids) return _create_result_proc(query, fn_env, edge, fields, procs, ids)
def test_node_link_alias(): data = { 'yN': {'a': 1, 'b': 2}, } x2y = {'xN': 'yN'} @listify def y_fields(fields, ids): for i in ids: yield [data[i][f.name] for f in fields] graph = Graph([ Node('Y', [ Field('a', None, y_fields), Field('b', None, y_fields), ]), Node('X', [ Field('id', None, id_field), Link('y', TypeRef['Y'], lambda ids: [x2y[i] for i in ids], requires='id'), ]), Root([ Link('x', TypeRef['X'], lambda: 'xN', requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x', q.Node([ q.Link('y', q.Node([q.Field('a')]), alias='y1'), q.Link('y', q.Node([q.Field('b')]), alias='y2'), ])), ])) check_result(result, { 'x': { 'y1': {'a': 1}, 'y2': {'b': 2}, } })
def test_link_option_missing(): graph = Graph([ Node('slices', [ Field('papeete', None, Mock()), ]), Root([ Link('eclairs', Sequence[TypeRef['slices']], Mock(), requires=None, options=[Option('nocks', None)]), ]), ]) with pytest.raises(TypeError) as err: execute(graph, build([Q.eclairs[Q.papeete]])) err.match(r'^Required option "nocks" for Link\(\'eclairs\', ' r'(.*) was not provided$')
def test_node_link_without_requirements(): f1 = Mock(return_value=[1]) f2 = Mock(return_value=[2]) f3 = Mock(return_value=[['arnhild']]) graph = Graph([ Node('a', [ Field('c', None, f3), ]), Node('b', [ Link('d', Sequence[TypeRef['a']], f2, requires=None), ]), Root([ Link('e', Sequence[TypeRef['b']], f1, requires=None), ]), ]) result = execute(graph, build([Q.e[Q.d[Q.c]]])) check_result(result, {'e': [{'d': [{'c': 'arnhild'}]}]}) f1.assert_called_once_with() f2.assert_called_once_with() f3.assert_called_once_with([q.Field('c')], [2])
def test_link_contain_invalid_types(): check_errors( [ Node('foo', []), Node('bar', [ Field('id', None, _fields_func), Link('baz', Sequence[TypeRef['foo']], _link_func, requires='id', options=[Option('size', None), 1]), ]), ], [('Invalid types provided as link "bar.baz" options: {!r}' .format(int))], )
def test_node_link_many_func_result_validation(value): with pytest.raises(TypeError) as err: execute( Graph([ Node('a', [ Field('b', None, Mock(return_value=[[1], [2]])) ]), Node('c', [ Field('d', None, Mock(return_value=[[3], [4]])), Link('e', Sequence[TypeRef['a']], Mock(return_value=value), requires='d'), ]), Root([ Link('f', Sequence[TypeRef['c']], Mock(return_value=[1, 2]), requires=None), ]), ]), build([Q.f[Q.e[Q.b]]]), ) err.match(re.escape( "Can't store link values, node: 'c', link: 'e', expected: " "list (len: 2) of lists, returned: {!r}".format(value) ))
def test_falsy_link_result(): x_fields = Mock(return_value=[[42]]) graph = Graph([ Node('X', [ Field('a', None, x_fields), ]), Root([ Link('x', TypeRef['X'], lambda: 0, requires=None), ]), ]) result = execute(graph, q.Node([ q.Link('x', q.Node([q.Field('a')])), ])) assert denormalize(graph, result) == {'x': {'a': 42}} x_fields.assert_called_once_with([q.Field('a')], [0])
def test_node_link_without_requirements(): f1 = Mock(return_value=[1]) f2 = Mock(return_value=[2]) f3 = Mock(return_value=[['arnhild']]) graph = Graph([ Node('a', [ Field('c', None, f3), ]), Node('b', [ Link('d', Sequence[TypeRef['a']], f2, requires=None), ]), Root([ Link('e', Sequence[TypeRef['b']], f1, requires=None), ]), ]) result = execute(graph, build([Q.e[Q.d[Q.c]]])) check_result(result, {'e': [{'d': [{'c': 'arnhild'}]}]}) assert result.index == { 'a': { 2: { 'c': 'arnhild' } }, 'b': { 1: { 'd': [result.ref('a', 2)] } }, } f1.assert_called_once_with() f2.assert_called_once_with() with reqs_eq_patcher(): f3.assert_called_once_with([query.Field('c')], [2])
def test_link_option_valid(option, args, result): f1 = Mock(return_value=[1]) f2 = Mock(return_value=[['aunder']]) graph = Graph([ Node('a', [ Field('c', None, f2), ]), Root([ Link('b', Sequence[TypeRef['a']], f1, requires=None, options=[option]), ]), ]) check_result(execute(graph, build([Q.b(**args)[Q.c]])), {'b': [{'c': 'aunder'}]}) f1.assert_called_once_with(result) f2.assert_called_once_with([q.Field('c')], [1])