DBIO-PostgreSQL-Age

 view release on metacpan or  search on metacpan

t/22-cypher-async.t  view on Meta::CPAN

  isa_ok $f, 'Future', 'cypher_async returns a Future';

  my $rows = $f->get;
  is_deeply $rows, [ { name => '"alice"' }, { name => '"bob"' } ],
    'resolves an arrayref of hashrefs keyed by column (sync {Slice=>{}} shape), raw by default';

  # The captured SQL+bind must be EXACTLY the shared sync builder's output --
  # RAW, un-transformed. Same sub => identical strings by construction; a
  # copied/forked builder would drift and fail here.
  my ($sync_sql, $sync_bind)
    = DBIO::PostgreSQL::Age::Storage::_cypher_sql_bind($a, @args);
  is $a->{cap}[0]{sql}, $sync_sql,
    'cypher_async hands the transport the RAW sync _cypher_sql_bind SQL (no shaping in the layer)';
  is_deeply $a->{cap}[0]{bind}, $sync_bind,
    'cypher_async bind == sync _cypher_sql_bind bind (JSON-encoded params)';
  like $a->{cap}[0]{sql}, qr/\$\$, \?\) AS \(name agtype\)/,
    'param slot is a raw "?" placeholder ($N shaping is the transport concern); column declared agtype';

  # '?'-seam body-survival: the Cypher body ($$...$$) and the $name reference are
  # NOT SQL placeholders and must reach the transport intact.
  like $a->{cap}[0]{sql}, qr/\$\$\nMATCH \(n \{name: \$name\}\) RETURN n\.name\n\$\$/,
    q{the '$$...$$' cypher body and the '$name' reference survive untouched into the transport};
}

# --- cypher_async: no-params path -------------------------------------------
{
  my $a = FakeAgeAsync->new;
  $a->{rows} = [ [ '"x"', '"y"' ] ];

  my @args = ('g', 'RETURN a, b', [qw(person friend)]);
  my $rows = $a->cypher_async(@args)->get;
  is_deeply $rows, [ { person => '"x"', friend => '"y"' } ],
    'multi-column no-param query zips columns in declared order';

  my ($sync_sql, $sync_bind)
    = DBIO::PostgreSQL::Age::Storage::_cypher_sql_bind($a, @args);
  is $a->{cap}[0]{sql}, $sync_sql, 'no-param SQL matches the raw sync builder';
  is_deeply $a->{cap}[0]{bind}, [], 'no-param query carries no bind';
  unlike $a->{cap}[0]{sql}, qr/\?/,  'no "?" placeholder emitted without params';
  unlike $a->{cap}[0]{sql}, qr/\$\d/, 'no $N appears -- the layer never shapes placeholders';
}

# --- cypher_async: auto_decode parity ---------------------------------------
{
  my $a = FakeAgeAsync->new;
  my @raw = ( [ '"alice"', '30' ], [ '{"id":1,"label":"Person"}::vertex', 'null' ] );
  $a->{rows} = [ map { [ @$_ ] } @raw ];

  my $rows = $a->cypher_async(
    'g', 'MATCH (n) RETURN n.name, n.age', [qw(name age)], undef, { auto_decode => 1 },
  )->get;

  # Expected = the SAME pure decode_agtype applied cell-by-cell.
  my @expected = map {
    { name => DBIO::PostgreSQL::Age::Storage::decode_agtype($a, $_->[0]),
      age  => DBIO::PostgreSQL::Age::Storage::decode_agtype($a, $_->[1]) }
  } @raw;

  is_deeply $rows, \@expected,
    'auto_decode applies decode_agtype to every cell, inside the Future chain';
  is $rows->[0]{name}, 'alice', '... quoted string decoded';
  is $rows->[0]{age}, 30, '... integer decoded to a number';
  is ref($rows->[1]{name}), 'HASH', '... vertex decoded to a hashref';
  is $rows->[1]{age}, undef, '... null decoded to undef';
}

# --- create_graph_async / drop_graph_async: mirror the sync graph SQL --------
{
  my $a = FakeAgeAsync->new;

  $a->create_graph_async('social')->get;
  is $a->{cap}[0]{sql}, 'SELECT * FROM ag_catalog.create_graph(?)',
    'create_graph_async runs ag_catalog.create_graph with a raw "?" (transport shapes it)';
  is_deeply $a->{cap}[0]{bind}, ['social'], 'create_graph_async binds the graph name';

  $a->{cap} = [];
  $a->drop_graph_async('social')->get;
  is $a->{cap}[0]{sql}, 'SELECT * FROM ag_catalog.drop_graph(?, ?)',
    'drop_graph_async runs ag_catalog.drop_graph with raw "?" placeholders';
  is_deeply $a->{cap}[0]{bind}, ['social', 0], 'drop_graph_async defaults cascade to 0';

  $a->{cap} = [];
  $a->drop_graph_async('social', 1)->get;
  is_deeply $a->{cap}[0]{bind}, ['social', 1], 'drop_graph_async passes a true cascade as 1';
}

done_testing;



( run in 0.825 second using v1.01-cache-2.11-cpan-600a1bdf6e4 )