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">>
-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.
target: The recipient addresscontent-type:<<"text/plain">>
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.
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
}
}
}
}#{
<<"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
}
}
}
}#{
<<"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
}
}
}
}
}{
"owners": ["owner-address-here"]
}#{
<<"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
}
}
}#{
<<"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
}
}
}#{
<<"data">> := #{
<<"transaction">> := null
}
}Transaction with Anchor
query($id: ID!) {
transaction(id: $id) {
data {
size
type
}
anchor
}
}#{
<<"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
| Category | Tests |
|---|---|
| Block by ID | simple_blocks_query_test |
| Block by Height | block_by_height_query_test |
| ANS-104 Transactions | simple_ans104_query_test |
| Transaction Tags | transactions_query_tags_test |
| Transaction Owners | transactions_query_owners_test |
| Transaction IDs | transactions_query_ids_test |
| Combined Filters | transactions_query_combined_test |
| Single Transaction | transaction_query_by_id_test |
| Full Transaction | transaction_query_full_test |
| Not Found | transaction_query_not_found_test |
| With Anchor | transaction_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
- Test Isolation: Each test creates its own store to prevent interference
- Block Heights: Tests use specific Arweave mainnet block heights (1745749-1745750)
- LMDB Store: Tests use
hb_store_lmdbfor reliable key-value storage - Message IDs: Tests verify exact ID matching between written and queried messages
- ANS-104 Format: All test messages use ANS-104 commitment format
- Owner Verification: Tests verify owner address and public key retrieval
- Recipient Support: Tests cover target/recipient field handling
- Anchor Support: Tests verify anchor encoding and retrieval
- Null Handling: Tests verify proper null response for non-existent IDs
- Tag Filtering: Tests cover single and multiple tag filters