From 3b4c1968a3d9b5333c31d253f3d1d951b25506e0 Mon Sep 17 00:00:00 2001 From: Rusty Russell Date: Wed, 9 Nov 2022 14:10:57 +1030 Subject: [PATCH] common/test: add unit tests for JSON filtering. Signed-off-by: Rusty Russell --- common/test/run-json_filter.c | 252 +++++++++++++++++++++++ common/test/run-json_stream-filter.c | 287 +++++++++++++++++++++++++++ 2 files changed, 539 insertions(+) create mode 100644 common/test/run-json_filter.c create mode 100644 common/test/run-json_stream-filter.c diff --git a/common/test/run-json_filter.c b/common/test/run-json_filter.c new file mode 100644 index 000000000..5ac68b4fb --- /dev/null +++ b/common/test/run-json_filter.c @@ -0,0 +1,252 @@ +#include "config.h" +#include "../amount.c" +#include "../json_filter.c" +#include "../json_param.c" +#include "../json_parse_simple.c" +#include "../json_stream.c" +#include +#include +#include +#include + +struct command; + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for command_check_only */ +bool command_check_only(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_check_only called!\n"); abort(); } +/* Generated stub for command_fail */ +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "command_fail called!\n"); abort(); } +/* Generated stub for command_set_usage */ +void command_set_usage(struct command *cmd UNNEEDED, const char *usage UNNEEDED) +{ fprintf(stderr, "command_set_usage called!\n"); abort(); } +/* Generated stub for command_usage_only */ +bool command_usage_only(const struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_usage_only called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for json_scan */ +const char *json_scan(const tal_t *ctx UNNEEDED, + const char *buffer UNNEEDED, + const jsmntok_t *tok UNNEEDED, + const char *guide UNNEEDED, + ...) +{ fprintf(stderr, "json_scan called!\n"); abort(); } +/* Generated stub for json_to_channel_id */ +bool json_to_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct channel_id *cid UNNEEDED) +{ fprintf(stderr, "json_to_channel_id called!\n"); abort(); } +/* Generated stub for json_to_millionths */ +bool json_to_millionths(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + u64 *millionths UNNEEDED) +{ fprintf(stderr, "json_to_millionths called!\n"); abort(); } +/* Generated stub for json_to_msat */ +bool json_to_msat(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct amount_msat *msat UNNEEDED) +{ fprintf(stderr, "json_to_msat called!\n"); abort(); } +/* Generated stub for json_to_node_id */ +bool json_to_node_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct node_id *id UNNEEDED) +{ fprintf(stderr, "json_to_node_id called!\n"); abort(); } +/* Generated stub for json_to_number */ +bool json_to_number(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + unsigned int *num UNNEEDED) +{ fprintf(stderr, "json_to_number called!\n"); abort(); } +/* Generated stub for json_to_outpoint */ +bool json_to_outpoint(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_outpoint *op UNNEEDED) +{ fprintf(stderr, "json_to_outpoint called!\n"); abort(); } +/* Generated stub for json_to_pubkey */ +bool json_to_pubkey(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct pubkey *pubkey UNNEEDED) +{ fprintf(stderr, "json_to_pubkey called!\n"); abort(); } +/* Generated stub for json_to_short_channel_id */ +bool json_to_short_channel_id(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct short_channel_id *scid UNNEEDED) +{ fprintf(stderr, "json_to_short_channel_id called!\n"); abort(); } +/* Generated stub for json_to_txid */ +bool json_to_txid(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + struct bitcoin_txid *txid UNNEEDED) +{ fprintf(stderr, "json_to_txid called!\n"); abort(); } +/* Generated stub for json_to_u16 */ +bool json_to_u16(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, + uint16_t *num UNNEEDED) +{ fprintf(stderr, "json_to_u16 called!\n"); abort(); } +/* Generated stub for json_tok_bin_from_hex */ +u8 *json_tok_bin_from_hex(const tal_t *ctx UNNEEDED, const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_tok_bin_from_hex called!\n"); abort(); } +/* Generated stub for lease_rates_fromhex */ +struct lease_rates *lease_rates_fromhex(const tal_t *ctx UNNEEDED, + const char *hexdata UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "lease_rates_fromhex called!\n"); abort(); } +/* Generated stub for segwit_addr_decode */ +int segwit_addr_decode( + int* ver UNNEEDED, + uint8_t* prog UNNEEDED, + size_t* prog_len UNNEEDED, + const char* hrp UNNEEDED, + const char* addr +) +{ fprintf(stderr, "segwit_addr_decode called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for type_to_string_ */ +const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, + union printable_types u UNNEEDED) +{ fprintf(stderr, "type_to_string_ called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +bool deprecated_apis; + +static bool dump_filter(const char *key, struct json_filter *filter, u32 *depth) +{ + for (size_t i = 0; i < *depth; i++) + printf(" "); + printf("%s\n", key); + (*depth)++; + if (filter->filter_array) + dump_filter("[]", filter->filter_array, depth); + else + strmap_iterate(&filter->filter_map, dump_filter, depth); + (*depth)--; + return true; +} + +struct command { + struct json_filter *filter; +}; + +struct json_filter **command_filter_ptr(struct command *cmd) +{ + return &cmd->filter; +} + +int main(int argc, char *argv[]) +{ + jsmntok_t *toks; + bool valid, complete; + const char *str; + jsmn_parser parser; + struct command *cmd; + struct json_stream *js; + u32 depth; + size_t len; + + common_setup(argv[0]); + cmd = tal(tmpctx, struct command); + str = "{\"transactions\": [{\"outputs\": [{\"amount_msat\": true, \"type\": true}]}]}"; + toks = toks_alloc(cmd); + jsmn_init(&parser); + valid = json_parse_input(&parser, &toks, str, strlen(str), &complete); + assert(valid); + assert(complete); + + assert(parse_filter(cmd, "_field", str, toks) == NULL); + assert(cmd->filter); + depth = 0; + dump_filter("[root]", cmd->filter, &depth); + + /* Simulate listtransactions example */ + js = new_json_stream(cmd, cmd, NULL); + json_object_start(js, NULL); + json_object_start(js, "result"); + json_stream_attach_filter(js, cmd->filter); + + json_array_start(js, "transactions"); + for (size_t i = 0; i < 2; i++) { + json_object_start(js, NULL); + json_add_num(js, "blockheight", 1); + json_add_num(js, "txindex", 2); + json_array_start(js, "inputs"); + for (size_t j = 0; j < 5; j++) { + json_object_start(js, NULL); + json_add_u32(js, "index", i+j); + json_add_u32(js, "sequence", i+j+1); + json_object_end(js); + } + json_array_end(js); + + json_array_start(js, "outputs"); + for (size_t j = 0; j < 2; j++) { + json_object_start(js, NULL); + + json_add_u32(js, "index", i+j); + json_add_amount_msat_only(js, "amount_msat", amount_msat(12)); + if (j == 0) + json_add_string(js, "type", "sometype"); + json_add_string(js, "scriptPubKey", "00000000"); + json_object_end(js); + } + json_array_end(js); + json_object_end(js); + } + json_array_end(js); + str = json_stream_detach_filter(tmpctx, js); + assert(!str); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + printf("%.*s\n", (int)len, str); + + common_shutdown(); +} diff --git a/common/test/run-json_stream-filter.c b/common/test/run-json_stream-filter.c new file mode 100644 index 000000000..16f6ea609 --- /dev/null +++ b/common/test/run-json_stream-filter.c @@ -0,0 +1,287 @@ +#include "config.h" +#include "../json_filter.c" +#include "../json_stream.c" +#include +#include +#include +#include +#include +#include + +/* AUTOGENERATED MOCKS START */ +/* Generated stub for amount_asset_is_main */ +bool amount_asset_is_main(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_is_main called!\n"); abort(); } +/* Generated stub for amount_asset_to_sat */ +struct amount_sat amount_asset_to_sat(struct amount_asset *asset UNNEEDED) +{ fprintf(stderr, "amount_asset_to_sat called!\n"); abort(); } +/* Generated stub for amount_msat */ +struct amount_msat amount_msat(u64 millisatoshis UNNEEDED) +{ fprintf(stderr, "amount_msat called!\n"); abort(); } +/* Generated stub for amount_sat */ +struct amount_sat amount_sat(u64 satoshis UNNEEDED) +{ fprintf(stderr, "amount_sat called!\n"); abort(); } +/* Generated stub for amount_sat_add */ + bool amount_sat_add(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_add called!\n"); abort(); } +/* Generated stub for amount_sat_eq */ +bool amount_sat_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_eq called!\n"); abort(); } +/* Generated stub for amount_sat_greater_eq */ +bool amount_sat_greater_eq(struct amount_sat a UNNEEDED, struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_greater_eq called!\n"); abort(); } +/* Generated stub for amount_sat_sub */ + bool amount_sat_sub(struct amount_sat *val UNNEEDED, + struct amount_sat a UNNEEDED, + struct amount_sat b UNNEEDED) +{ fprintf(stderr, "amount_sat_sub called!\n"); abort(); } +/* Generated stub for amount_sat_to_asset */ +struct amount_asset amount_sat_to_asset(struct amount_sat *sat UNNEEDED, const u8 *asset UNNEEDED) +{ fprintf(stderr, "amount_sat_to_asset called!\n"); abort(); } +/* Generated stub for amount_sat_to_msat */ + bool amount_sat_to_msat(struct amount_msat *msat UNNEEDED, + struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "amount_sat_to_msat called!\n"); abort(); } +/* Generated stub for amount_tx_fee */ +struct amount_sat amount_tx_fee(u32 fee_per_kw UNNEEDED, size_t weight UNNEEDED) +{ fprintf(stderr, "amount_tx_fee called!\n"); abort(); } +/* Generated stub for command_fail */ +struct command_result *command_fail(struct command *cmd UNNEEDED, enum jsonrpc_errcode code UNNEEDED, + const char *fmt UNNEEDED, ...) + +{ fprintf(stderr, "command_fail called!\n"); abort(); } +/* Generated stub for command_filter_ptr */ +struct json_filter **command_filter_ptr(struct command *cmd UNNEEDED) +{ fprintf(stderr, "command_filter_ptr called!\n"); abort(); } +/* Generated stub for deprecated_apis */ +bool deprecated_apis; +/* Generated stub for fmt_amount_msat */ +const char *fmt_amount_msat(const tal_t *ctx UNNEEDED, struct amount_msat msat UNNEEDED) +{ fprintf(stderr, "fmt_amount_msat called!\n"); abort(); } +/* Generated stub for fmt_amount_sat */ +const char *fmt_amount_sat(const tal_t *ctx UNNEEDED, struct amount_sat sat UNNEEDED) +{ fprintf(stderr, "fmt_amount_sat called!\n"); abort(); } +/* Generated stub for fmt_wireaddr_without_port */ +char *fmt_wireaddr_without_port(const tal_t *ctx UNNEEDED, const struct wireaddr *a UNNEEDED) +{ fprintf(stderr, "fmt_wireaddr_without_port called!\n"); abort(); } +/* Generated stub for fromwire */ +const u8 *fromwire(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, void *copy UNNEEDED, size_t n UNNEEDED) +{ fprintf(stderr, "fromwire called!\n"); abort(); } +/* Generated stub for fromwire_bool */ +bool fromwire_bool(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_bool called!\n"); abort(); } +/* Generated stub for fromwire_fail */ +void *fromwire_fail(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_fail called!\n"); abort(); } +/* Generated stub for fromwire_secp256k1_ecdsa_signature */ +void fromwire_secp256k1_ecdsa_signature(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, + secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "fromwire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for fromwire_sha256 */ +void fromwire_sha256(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "fromwire_sha256 called!\n"); abort(); } +/* Generated stub for fromwire_tal_arrn */ +u8 *fromwire_tal_arrn(const tal_t *ctx UNNEEDED, + const u8 **cursor UNNEEDED, size_t *max UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_tal_arrn called!\n"); abort(); } +/* Generated stub for fromwire_u32 */ +u32 fromwire_u32(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u32 called!\n"); abort(); } +/* Generated stub for fromwire_u64 */ +u64 fromwire_u64(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u64 called!\n"); abort(); } +/* Generated stub for fromwire_u8 */ +u8 fromwire_u8(const u8 **cursor UNNEEDED, size_t *max UNNEEDED) +{ fprintf(stderr, "fromwire_u8 called!\n"); abort(); } +/* Generated stub for fromwire_u8_array */ +void fromwire_u8_array(const u8 **cursor UNNEEDED, size_t *max UNNEEDED, u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "fromwire_u8_array called!\n"); abort(); } +/* Generated stub for json_next */ +const jsmntok_t *json_next(const jsmntok_t *tok UNNEEDED) +{ fprintf(stderr, "json_next called!\n"); abort(); } +/* Generated stub for json_to_bool */ +bool json_to_bool(const char *buffer UNNEEDED, const jsmntok_t *tok UNNEEDED, bool *b UNNEEDED) +{ fprintf(stderr, "json_to_bool called!\n"); abort(); } +/* Generated stub for json_tok_full */ +const char *json_tok_full(const char *buffer UNNEEDED, const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full called!\n"); abort(); } +/* Generated stub for json_tok_full_len */ +int json_tok_full_len(const jsmntok_t *t UNNEEDED) +{ fprintf(stderr, "json_tok_full_len called!\n"); abort(); } +/* Generated stub for towire */ +void towire(u8 **pptr UNNEEDED, const void *data UNNEEDED, size_t len UNNEEDED) +{ fprintf(stderr, "towire called!\n"); abort(); } +/* Generated stub for towire_bool */ +void towire_bool(u8 **pptr UNNEEDED, bool v UNNEEDED) +{ fprintf(stderr, "towire_bool called!\n"); abort(); } +/* Generated stub for towire_secp256k1_ecdsa_signature */ +void towire_secp256k1_ecdsa_signature(u8 **pptr UNNEEDED, + const secp256k1_ecdsa_signature *signature UNNEEDED) +{ fprintf(stderr, "towire_secp256k1_ecdsa_signature called!\n"); abort(); } +/* Generated stub for towire_sha256 */ +void towire_sha256(u8 **pptr UNNEEDED, const struct sha256 *sha256 UNNEEDED) +{ fprintf(stderr, "towire_sha256 called!\n"); abort(); } +/* Generated stub for towire_u32 */ +void towire_u32(u8 **pptr UNNEEDED, u32 v UNNEEDED) +{ fprintf(stderr, "towire_u32 called!\n"); abort(); } +/* Generated stub for towire_u64 */ +void towire_u64(u8 **pptr UNNEEDED, u64 v UNNEEDED) +{ fprintf(stderr, "towire_u64 called!\n"); abort(); } +/* Generated stub for towire_u8 */ +void towire_u8(u8 **pptr UNNEEDED, u8 v UNNEEDED) +{ fprintf(stderr, "towire_u8 called!\n"); abort(); } +/* Generated stub for towire_u8_array */ +void towire_u8_array(u8 **pptr UNNEEDED, const u8 *arr UNNEEDED, size_t num UNNEEDED) +{ fprintf(stderr, "towire_u8_array called!\n"); abort(); } +/* Generated stub for type_to_string_ */ +const char *type_to_string_(const tal_t *ctx UNNEEDED, const char *typename UNNEEDED, + union printable_types u UNNEEDED) +{ fprintf(stderr, "type_to_string_ called!\n"); abort(); } +/* AUTOGENERATED MOCKS END */ + +int main(int argc, char *argv[]) +{ + struct json_stream *js; + struct json_filter *filter, *subf; + const char *str; + size_t len; + + common_setup(argv[0]); + + /* First with an empty filter. */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + /* Filters assume we start inside an object! */ + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_object_start(js, "message"); + json_add_string(js, "string", "string"); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"message\":{\"string\":\"string\"}}", len) == 0); + + /* Now try a result filter. */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + json_filter_subobj(filter, "result", strlen("result")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_object_start(js, "message"); + json_add_string(js, "string", "string"); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"message\":{\"string\":\"string\"}}", len) == 0); + + /* Now try a result->message->string filter. */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + subf = json_filter_subobj(filter, "result", strlen("result")); + subf = json_filter_subobj(subf, "message", strlen("message")); + json_filter_subobj(subf, "string", strlen("string")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_object_start(js, "message"); + json_add_string(js, "string", "string"); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"message\":{\"string\":\"string\"}}", len) == 0); + + /* Now a sub-filter which doesn't match */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + subf = json_filter_subobj(filter, "result", strlen("result")); + subf = json_filter_subobj(subf, "message", strlen("message")); + json_filter_subobj(subf, "dne", strlen("dne")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_object_start(js, "message"); + json_add_string(js, "string", "string"); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"message\":{}}", len) == 0); + + /* Multple, one of three matchs */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + subf = json_filter_subobj(filter, "result", strlen("result")); + subf = json_filter_subobj(subf, "message", strlen("message")); + json_filter_subobj(subf, "f1", strlen("f1")); + json_filter_subobj(subf, "f3", strlen("f3")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_object_start(js, "message"); + json_add_string(js, "f1", "f1string"); + json_object_start(js, "f2"); + json_add_string(js, "f2sub", "f2string"); + json_object_end(js); + json_add_string(js, "f3", "f3string"); + json_object_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"message\":{\"f1\":\"f1string\",\"f3\":\"f3string\"}}", len) == 0); + + /* Now inside arrays! */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + subf = json_filter_subobj(filter, "result", strlen("result")); + subf = json_filter_subobj(subf, "messages", strlen("messages")); + subf = json_filter_subarr(subf); + json_filter_subobj(subf, "string", strlen("string")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_object_start(js, "result"); + json_array_start(js, "messages"); + json_object_start(js, NULL); + json_add_string(js, "string", "string1"); + json_object_end(js); + json_object_start(js, NULL); + json_add_string(js, "string", "string2"); + json_object_end(js); + json_array_end(js); + json_object_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":{\"messages\":[{\"string\":\"string1\"},{\"string\":\"string2\"}]}", + len) == 0); + + /* Now filter out arrays. */ + js = new_json_stream(tmpctx, NULL, NULL); + filter = json_filter_new(js); + json_filter_subobj(filter, "result", strlen("result")); + json_object_start(js, NULL); + json_stream_attach_filter(js, filter); + + json_add_string(js, "result", "resultstr"); + json_add_string(js, "ignored", "ignoredstr"); + json_array_start(js, "fallbacks"); + json_object_start(js, NULL); + json_add_string(js, "type", "P2PKH"); + json_object_end(js); + json_array_end(js); + + str = json_out_contents(js->jout, &len); + assert(strncmp(str, "{\"result\":\"resultstr\"", len) == 0); + common_shutdown(); +}