Skip to content

dev_query_test_vectors.erl - GraphQL Query Test Suite

Overview

Purpose: Comprehensive test suite for the ~query@1.0 device's GraphQL implementation
Module: dev_query_test_vectors
Test Framework: EUnit
Coverage: Block queries, transaction queries, ANS-104 queries, combined filters

This module provides a suite of test queries and expected responses for validating the GraphQL query interface. It covers both Arweave-compatible queries (blocks, transactions) and HyperBEAM native message queries.

Dependencies

  • HyperBEAM: hb_cache, hb_message, hb_http, hb_util, hb_json, hb_maps, hb_ao
  • Related: dev_query_graphql, dev_query_arweave
  • Includes: include/hb.hrl
  • Testing: eunit

Test Helper Functions Overview

%% Message Creation Helpers
-spec write_test_message(Opts) -> {ok, Msg}.
-spec write_test_message_with_recipient(Recipient, Opts) -> {ok, Msg}.
 
%% Block Population
-spec get_test_blocks(Node) -> ok.

Test Helper Functions

1. write_test_message/1

-spec write_test_message(Opts) -> {ok, Msg}
    when
        Opts :: map(),
        Msg :: map().

Description: Creates and writes a test message to the cache with standard AO message fields. The message is committed with ANS-104 format.

Message Fields:
  • data-protocol: <<"ao">>
  • variant: <<"ao.N.1">>
  • type: <<"Message">>
  • action: <<"Eval">>
  • data: <<"test data">>
Test Code:
-module(test_dev_query_test_vectors).
-include_lib("eunit/include/eunit.hrl").
 
write_message_test() ->
    Opts = #{
        priv_wallet => hb:wallet(),
        store => hb_test_utils:test_store()
    },
    % Create and write a test message similar to write_test_message/1
    Msg = hb_message:commit(
        #{
            <<"data-protocol">> => <<"ao">>,
            <<"variant">> => <<"ao.N.1">>,
            <<"type">> => <<"Message">>,
            <<"action">> => <<"Eval">>,
            <<"data">> => <<"test data">>
        },
        Opts
    ),
    {ok, _} = hb_cache:write(Msg, Opts),
    ?assert(is_map(Msg)),
    ?assertEqual(<<"Message">>, hb_ao:get(<<"type">>, Msg, Opts)).

2. write_test_message_with_recipient/2

-spec write_test_message_with_recipient(Recipient, Opts) -> {ok, Msg}
    when
        Recipient :: binary(),
        Opts :: map(),
        Msg :: map().

Description: Creates a test message with a specified recipient/target address. Includes additional fields like content-type.

Additional Fields:
  • target: The recipient address
  • content-type: <<"text/plain">>
Test Code:
write_message_with_recipient_test() ->
    Opts = #{
        priv_wallet => hb:wallet(),
        store => hb_test_utils:test_store()
    },
    Recipient = <<"test-recipient-address">>,
    % Create message with recipient similar to write_test_message_with_recipient/2
    Msg = hb_message:commit(
        #{
            <<"data-protocol">> => <<"ao">>,
            <<"variant">> => <<"ao.N.1">>,
            <<"type">> => <<"Message">>,
            <<"target">> => Recipient,
            <<"content-type">> => <<"text/plain">>,
            <<"data">> => <<"test data">>
        },
        Opts
    ),
    {ok, _} = hb_cache:write(Msg, Opts),
    ?assertEqual(Recipient, hb_ao:get(<<"target">>, Msg, Opts)).

3. get_test_blocks/1

-spec get_test_blocks(Node) -> ok
    when
        Node :: binary().

Description: Populates the cache with test blocks by fetching blocks from Arweave via the ~arweave@2.9-pre device. Fetches blocks at heights 1745749 and 1745750.

Test Code:
graphql_query_test() ->
    % Test GraphQL query execution
    Opts = #{store => hb_test_utils:test_store()},
    Context = #{opts => Opts},
    % Simple block query - should return ok even with no results
    Args = #{<<"height">> => #{<<"min">> => 0, <<"max">> => 1}},
    Result = dev_query_graphql:execute(Context, undefined, <<"blocks">>, Args),
    ?assertMatch({ok, _}, Result).

Test Vectors

Block Queries

Simple Block Query by ID

query {
    blocks(
        ids: ["V7yZNKPQLIQfUu8r8-lcEaz4o7idl6LTHn5AHlGIFF8TKfxIe7s_yFxjqan6OW45"]
    ) {
        edges {
            node {
                id
                previous
                height
                timestamp
            }
        }
    }
}
Expected Response:
#{
    <<"data">> := #{
        <<"blocks">> := #{
            <<"edges">> := [
                #{
                    <<"node">> := #{
                        <<"id">> := _,
                        <<"previous">> := _,
                        <<"height">> := 1745749,
                        <<"timestamp">> := 1756866695
                    }
                }
            ]
        }
    }
}

Block Query by Height Range

query {
    blocks(height: {min: 1745749, max: 1745750}) {
        edges {
            node {
                id
                previous
                height
                timestamp
            }
        }
    }
}
Expected Response:
#{
    <<"data">> := #{
        <<"blocks">> := #{
            <<"edges">> := [
                #{<<"node">> := #{<<"height">> := 1745749, ...}},
                #{<<"node">> := #{<<"height">> := 1745750, ...}}
            ]
        }
    }
}

Transaction Queries

Simple ANS-104 Transaction Query

query($owners: [String!]) {
    transactions(
        tags: [
            {name: "type", values: ["Message"]},
            {name: "variant", values: ["ao.N.1"]}
        ],
        owners: $owners
    ) {
        edges {
            node {
                id
                tags {
                    name
                    value
                }
            }
        }
    }
}
Variables:
{
    "owners": ["owner-address-here"]
}
Expected Response:
#{
    <<"data">> := #{
        <<"transactions">> := #{
            <<"edges">> := [
                #{
                    <<"node">> := #{
                        <<"id">> := ExpectedID,
                        <<"tags">> := [#{<<"name">> := _, <<"value">> := _}|_]
                    }
                }
            ]
        }
    }
}

Transaction Query with Tags Filter

query {
    transactions(
        tags: [
            {name: "type", values: ["Message"]},
            {name: "variant", values: ["ao.N.1"]}
        ]
    ) {
        edges {
            node {
                id
                tags {
                    name
                    value
                }
            }
        }
    }
}

Transaction Query by Owner

query($owners: [String!]) {
    transactions(owners: $owners) {
        edges {
            node {
                id
                tags {
                    name
                    value
                }
            }
        }
    }
}

Transaction Query by IDs

query($ids: [ID!]) {
    transactions(ids: $ids) {
        edges {
            node {
                id
                tags {
                    name
                    value
                }
            }
        }
    }
}

Combined Transaction Query

query($owners: [String!], $ids: [ID!]) {
    transactions(
        owners: $owners,
        ids: $ids,
        tags: [{name: "type", values: ["Message"]}]
    ) {
        edges {
            node {
                id
                tags {
                    name
                    value
                }
            }
        }
    }
}

Single Transaction Queries

Transaction by ID

query($id: ID!) {
    transaction(id: $id) {
        id
        tags {
            name
            value
        }
    }
}

Full Transaction Query

query($id: ID!) {
    transaction(id: $id) {
        id
        anchor
        signature
        recipient
        owner {
            address
            key
        }
        tags {
            name
            value
        }
        data {
            size
            type
        }
    }
}
Expected Response:
#{
    <<"data">> := #{
        <<"transaction">> := #{
            <<"id">> := ExpectedID,
            <<"recipient">> := AliceAddress,
            <<"anchor">> := <<"">>,
            <<"owner">> := #{
                <<"address">> := SenderAddress,
                <<"key">> := SenderPubKey
            },
            <<"data">> := #{
                <<"size">> := <<"9">>,
                <<"type">> := <<"text/plain">>
            },
            <<"tags">> := [#{<<"name">> := _, <<"value">> := _}|_]
        }
    }
}

Transaction Not Found

query($id: ID!) {
    transaction(id: $id) {
        id
        tags {
            name
            value
        }
    }
}
Expected Response (non-existent ID):
#{
    <<"data">> := #{
        <<"transaction">> := null
    }
}

Transaction with Anchor

query($id: ID!) {
    transaction(id: $id) {
        data {
            size
            type
        }
        anchor
    }
}
Expected Response:
#{
    <<"data">> := #{
        <<"transaction">> := #{
            <<"anchor">> := EncodedAnchor
        }
    }
}

Common Patterns

%% Set up test environment
Opts = #{
    priv_wallet => hb:wallet(),
    store => [hb_test_utils:test_store(hb_store_lmdb)]
},
Node = hb_http_server:start_node(Opts).
 
%% Write test data
{ok, WrittenMsg} = dev_query_test_vectors:write_test_message(Opts),
ExpectedID = hb_message:id(WrittenMsg, all, Opts).
 
%% Verify message was cached
{ok, [_]} = hb_cache:match(#{<<"type">> => <<"Message">>}, Opts).
 
%% Execute GraphQL query
Query = <<"query($id: ID!) { transaction(id: $id) { id } }">>,
Res = dev_query_graphql:test_query(Node, Query, #{<<"id">> => ExpectedID}, Opts).
 
%% Verify response structure
?assertMatch(
    #{<<"data">> := #{<<"transaction">> := #{<<"id">> := ExpectedID}}},
    Res
).

Test Coverage Summary

CategoryTests
Block by IDsimple_blocks_query_test
Block by Heightblock_by_height_query_test
ANS-104 Transactionssimple_ans104_query_test
Transaction Tagstransactions_query_tags_test
Transaction Ownerstransactions_query_owners_test
Transaction IDstransactions_query_ids_test
Combined Filterstransactions_query_combined_test
Single Transactiontransaction_query_by_id_test
Full Transactiontransaction_query_full_test
Not Foundtransaction_query_not_found_test
With Anchortransaction_query_with_anchor_test

References

  • GraphQL Interface - dev_query_graphql.erl
  • Arweave Queries - dev_query_arweave.erl
  • Cache System - hb_cache.erl
  • Message Format - hb_message.erl
  • ANS-104 - ar_bundles.erl

Notes

  1. Test Isolation: Each test creates its own store to prevent interference
  2. Block Heights: Tests use specific Arweave mainnet block heights (1745749-1745750)
  3. LMDB Store: Tests use hb_store_lmdb for reliable key-value storage
  4. Message IDs: Tests verify exact ID matching between written and queried messages
  5. ANS-104 Format: All test messages use ANS-104 commitment format
  6. Owner Verification: Tests verify owner address and public key retrieval
  7. Recipient Support: Tests cover target/recipient field handling
  8. Anchor Support: Tests verify anchor encoding and retrieval
  9. Null Handling: Tests verify proper null response for non-existent IDs
  10. Tag Filtering: Tests cover single and multiple tag filters