diff options
Diffstat (limited to 'src/testing/testing_api_cmd_bank_history_debit.c')
-rw-r--r-- | src/testing/testing_api_cmd_bank_history_debit.c | 530 |
1 files changed, 316 insertions, 214 deletions
diff --git a/src/testing/testing_api_cmd_bank_history_debit.c b/src/testing/testing_api_cmd_bank_history_debit.c index cb3f68097..1cb7320fa 100644 --- a/src/testing/testing_api_cmd_bank_history_debit.c +++ b/src/testing/testing_api_cmd_bank_history_debit.c @@ -92,6 +92,11 @@ struct HistoryState struct TALER_BANK_DebitHistoryHandle *hh; /** + * Our interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** * Expected number of results (= rows). */ uint64_t results_obtained; @@ -147,38 +152,155 @@ print_expected (struct History *h, /** + * Closure for command_cb(). + */ +struct IteratorContext +{ + /** + * Array of history items to return. + */ + struct History *h; + + /** + * Set to the row ID from where on we should actually process history items, + * or NULL if we should process all of them. + */ + const uint64_t *row_id_start; + + /** + * History state we are working on. + */ + struct HistoryState *hs; + + /** + * Current length of the @e h array. + */ + unsigned int total; + + /** + * Current write position in @e h array. + */ + unsigned int pos; + + /** + * Ok equals True whenever a starting row_id was provided AND was found + * among the CMDs, OR no starting row was given in the first place. + */ + bool ok; + +}; + + +/** + * Helper function of build_history() that expands + * the history for each relevant command encountered. + * + * @param[in,out] cls our `struct IteratorContext` + * @param cmd a command to process + */ +static void +command_cb (void *cls, + const struct TALER_TESTING_Command *cmd) +{ + struct IteratorContext *ic = cls; + struct HistoryState *hs = ic->hs; + + const uint64_t *row_id; + const char *debit_account; + const char *credit_account; + const struct TALER_Amount *amount; + const struct TALER_WireTransferIdentifierRawP *wtid; + const char *exchange_base_url; + + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Checking if command %s is relevant for debit history\n", + cmd->label); + if ( (GNUNET_OK != + TALER_TESTING_get_trait_bank_row (cmd, + &row_id)) || + (GNUNET_OK != + TALER_TESTING_get_trait_debit_payto_uri (cmd, + &debit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_credit_payto_uri (cmd, + &credit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_amount (cmd, + &amount)) || + (GNUNET_OK != + TALER_TESTING_get_trait_wtid (cmd, + &wtid)) || + (GNUNET_OK != + TALER_TESTING_get_trait_exchange_url (cmd, + &exchange_base_url)) ) + return; /* not an event we care about */ + /* Seek "/history/outgoing" starting row. */ + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Command %s is relevant for debit history!\n", + cmd->label); + if ( (NULL != ic->row_id_start) && + (*(ic->row_id_start) == *row_id) && + (! ic->ok) ) + { + /* Until here, nothing counted. */ + ic->ok = true; + return; + } + /* when 'start' was _not_ given, then ok == GNUNET_YES */ + if (! ic->ok) + return; /* skip until we find the marker */ + if (ic->total >= GNUNET_MAX (hs->num_results, + -hs->num_results) ) + { + TALER_LOG_DEBUG ("Hit history limit\n"); + return; + } + TALER_LOG_INFO ("Found history: %s->%s for account %s\n", + debit_account, + credit_account, + hs->account_url); + /* found matching record, make sure we have room */ + if (ic->pos == ic->total) + GNUNET_array_grow (ic->h, + ic->total, + ic->pos * 2); + ic->h[ic->pos].c_url = GNUNET_strdup (credit_account); + ic->h[ic->pos].d_url = GNUNET_strdup (debit_account); + ic->h[ic->pos].details.credit_account_uri = ic->h[ic->pos].c_url; + ic->h[ic->pos].details.amount = *amount; + ic->h[ic->pos].row_id = *row_id; + ic->h[ic->pos].details.wtid = *wtid; + ic->h[ic->pos].details.exchange_base_url = exchange_base_url; + ic->pos++; +} + + +/** * This function constructs the list of history elements that * interest the account number of the caller. It has two main * loops: the first to figure out how many history elements have * to be allocated, and the second to actually populate every * element. * - * @param is interpreter state (supposedly having the - * current CMD pointing at a "history" CMD). + * @param hs history state command context * @param[out] rh history array to initialize. * @return number of entries in @a rh. */ static unsigned int -build_history (struct TALER_TESTING_Interpreter *is, +build_history (struct HistoryState *hs, struct History **rh) { - struct HistoryState *hs = is->commands[is->ip].cls; - unsigned int total; - unsigned int pos; - struct History *h; - const struct TALER_TESTING_Command *add_incoming_cmd; - int inc; - int start; - int end; - /* #GNUNET_YES whenever either no 'start' value was given for the history - * query, or the given value is found in the list of all the CMDs. */ - int ok; - const uint64_t *row_id_start = NULL; + struct TALER_TESTING_Interpreter *is = hs->is; + struct IteratorContext ic = { + .hs = hs + }; if (NULL != hs->start_row_reference) { - TALER_LOG_INFO - ("`%s': start row given via reference `%s'\n", + const struct TALER_TESTING_Command *add_incoming_cmd; + + TALER_LOG_INFO ( + "`%s': start row given via reference `%s'\n", TALER_TESTING_interpreter_get_current_label (is), hs->start_row_reference); add_incoming_cmd = TALER_TESTING_interpreter_lookup_command ( @@ -187,124 +309,91 @@ build_history (struct TALER_TESTING_Interpreter *is, GNUNET_assert (NULL != add_incoming_cmd); GNUNET_assert (GNUNET_OK == TALER_TESTING_get_trait_row (add_incoming_cmd, - &row_id_start)); + &ic.row_id_start)); } + ic.ok = false; + if (NULL == ic.row_id_start) + ic.ok = true; + GNUNET_array_grow (ic.h, + ic.total, + 4); GNUNET_assert (0 != hs->num_results); - if (0 == is->ip) - { - TALER_LOG_DEBUG ("Checking history at first CMD..\n"); - *rh = NULL; - return 0; - } + TALER_TESTING_iterate (is, + hs->num_results > 0, + &command_cb, + &ic); + GNUNET_assert (ic.ok); + GNUNET_array_grow (ic.h, + ic.total, + ic.pos); + if (0 == ic.pos) + TALER_LOG_DEBUG ("Empty credit history computed\n"); + *rh = ic.h; + return ic.pos; +} - /* AKA 'delta' */ - if (hs->num_results > 0) - { - inc = 1; /* _inc_rement: go forwards */ - start = 0; - end = is->ip; - } + +/** + * Normalize IBAN-based payto URI in @a in. + * + * @param in input payto://-URI to normalize + * @return normalized IBAN for the test + */ +static char * +normalize (const char *in) +{ + char *npt; + const char *q = strchr (in, + '?'); + const char *mptr; + const char *bic; + const char *iban; + + if (NULL == q) + npt = GNUNET_strdup (in); else + npt = GNUNET_strndup (in, + q - in); + if (0 != strncasecmp (npt, + "payto://", + strlen ("payto://"))) { - inc = -1; /* decrement: we go backwards */ - start = is->ip - 1; - end = -1; /* range is exclusive, do look at 0! */ + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - h = NULL; - total = 0; - GNUNET_array_grow (h, - total, - 4); - pos = 0; - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checking commands %u to %u for debit history\n", - start, - end); - for (int off = start; off != end; off += inc) + mptr = npt + strlen ("payto://"); + bic = strchr (mptr, '/'); + if (NULL == bic) { - const struct TALER_TESTING_Command *cmd = &is->commands[off]; - const uint64_t *row_id; - const char **debit_account; - const char **credit_account; - const struct TALER_Amount *amount; - const struct TALER_WireTransferIdentifierRawP *wtid; - const char **exchange_base_url; - - GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Checking if command %s is relevant for debit history\n", - cmd->label); - if ( (GNUNET_OK != - TALER_TESTING_get_trait_bank_row (cmd, - &row_id)) || - (GNUNET_OK != - TALER_TESTING_get_trait_debit_payto_uri (cmd, - &debit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_credit_payto_uri (cmd, - &credit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount (cmd, - &amount)) || - (GNUNET_OK != - TALER_TESTING_get_trait_wtid (cmd, - &wtid)) || - (GNUNET_OK != - TALER_TESTING_get_trait_exchange_url (cmd, - &exchange_base_url)) ) - continue; /* not an event we care about */ - /* Seek "/history/outgoing" starting row. */ + GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "Command %s is relevant for debit history!\n", - cmd->label); - if ( (NULL != row_id_start) && - (*row_id_start == *row_id) && - (GNUNET_NO == ok) ) - { - /* Until here, nothing counted. */ - ok = GNUNET_YES; - continue; - } - /* when 'start' was _not_ given, then ok == GNUNET_YES */ - if (GNUNET_NO == ok) - continue; /* skip until we find the marker */ - if (total >= GNUNET_MAX (hs->num_results, - -hs->num_results) ) - { - TALER_LOG_DEBUG ("Hit history limit\n"); - break; - } - TALER_LOG_INFO ("Found history: %s->%s for account %s\n", - *debit_account, - *credit_account, - hs->account_url); - /* found matching record, make sure we have room */ - if (pos == total) - GNUNET_array_grow (h, - total, - pos * 2); - h[pos].c_url = GNUNET_strdup (*credit_account); - h[pos].d_url = GNUNET_strdup (*debit_account); - h[pos].details.credit_account_uri = h[pos].c_url; - h[pos].details.debit_account_uri = h[pos].d_url; - h[pos].details.amount = *amount; - h[pos].row_id = *row_id; - h[pos].details.wtid = *wtid; - h[pos].details.exchange_base_url = *exchange_base_url; - pos++; + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - GNUNET_assert (GNUNET_YES == ok); - GNUNET_array_grow (h, - total, - pos); - if (0 == pos) - TALER_LOG_DEBUG ("Empty debit history computed\n"); - *rh = h; - return total; + bic++; + iban = strchr (bic, '/'); + if (NULL != iban) + { + /* need to remove bic */ + char *n; + + iban++; + GNUNET_asprintf (&n, + "payto://%.*s/%s", + (int) ((bic - mptr) - 1), + mptr, + iban); + GNUNET_free (npt); + npt = n; + } + return npt; } @@ -325,6 +414,9 @@ check_result (struct History *h, unsigned int off, const struct TALER_BANK_DebitDetails *details) { + char *u1; + char *u2; + if (off >= total) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -337,14 +429,33 @@ check_result (struct History *h, off); return GNUNET_SYSERR; } + u1 = normalize (h[off].details.credit_account_uri); + if (NULL == u1) + return GNUNET_SYSERR; + u2 = normalize (details->credit_account_uri); + if (NULL == u2) + { + GNUNET_free (u1); + return GNUNET_SYSERR; + } if ( (0 != GNUNET_memcmp (&h[off].details.wtid, &details->wtid)) || (0 != TALER_amount_cmp (&h[off].details.amount, &details->amount)) || - (0 != strcasecmp (h[off].details.credit_account_uri, - details->credit_account_uri)) ) + (0 != strcasecmp (u1, + u2)) ) { GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "expected debit_account_uri: %s with %s for %s\n", + u1, + TALER_amount2s (&h[off].details.amount), + TALER_B2S (&h[off].details.wtid)); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "actual debit_account_uri: %s with %s for %s\n", + u2, + TALER_amount2s (&details->amount), + TALER_B2S (&details->wtid)); print_expected (h, total, off); @@ -362,99 +473,86 @@ check_result (struct History *h, * finally check it against what the bank returned. * * @param cls closure. - * @param http_status HTTP response code, #MHD_HTTP_OK (200) - * for successful status request 0 if the bank's reply is - * bogus (fails to follow the protocol), - * #MHD_HTTP_NO_CONTENT if there are no more results; on - * success the last callback is always of this status - * (even if `abs(num_results)` were already returned). - * @param ec taler status code. - * @param row_id monotonically increasing counter corresponding to - * the transaction. - * @param details details about the wire transfer. - * @param json detailed response from the HTTPD, or NULL if - * reply was not in JSON. - * @return #GNUNET_OK to continue, #GNUNET_SYSERR to abort iteration + * @param dhr http response details */ -static enum GNUNET_GenericReturnValue +static void history_cb (void *cls, - unsigned int http_status, - enum TALER_ErrorCode ec, - uint64_t row_id, - const struct TALER_BANK_DebitDetails *details, - const json_t *json) + const struct TALER_BANK_DebitHistoryResponse *dhr) { - struct TALER_TESTING_Interpreter *is = cls; - struct HistoryState *hs = is->commands[is->ip].cls; + struct HistoryState *hs = cls; + struct TALER_TESTING_Interpreter *is = hs->is; - (void) row_id; - if (NULL == details) + hs->hh = NULL; + switch (dhr->http_status) { - hs->hh = NULL; - if ( (MHD_HTTP_NOT_FOUND == http_status) && - (0 == hs->total) ) + case 0: + GNUNET_break (0); + goto error; + case MHD_HTTP_OK: + for (unsigned int i = 0; i<dhr->details.ok.details_length; i++) + { + const struct TALER_BANK_DebitDetails *dd = + &dhr->details.ok.details[i]; + + /* check current element */ + if (GNUNET_OK != + check_result (hs->h, + hs->total, + hs->results_obtained, + dd)) + { + GNUNET_break (0); + json_dumpf (dhr->response, + stderr, + JSON_COMPACT); + hs->failed = true; + hs->hh = NULL; + TALER_TESTING_interpreter_fail (is); + return; + } + hs->results_obtained++; + } + TALER_TESTING_interpreter_next (is); + return; + case MHD_HTTP_NO_CONTENT: + if (0 == hs->total) { /* not found is OK for empty history */ TALER_TESTING_interpreter_next (is); - return GNUNET_OK; + return; } - if ( (hs->results_obtained != hs->total) || - (GNUNET_YES == hs->failed) || - (MHD_HTTP_NO_CONTENT != http_status) ) + GNUNET_break (0); + goto error; + case MHD_HTTP_NOT_FOUND: + if (0 == hs->total) { - GNUNET_break (0); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Expected history of length %u, got %llu;" - " HTTP status code: %u/%d, failed: %d\n", - hs->total, - (unsigned long long) hs->results_obtained, - http_status, - (int) ec, - hs->failed); - print_expected (hs->h, - hs->total, - UINT_MAX); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; + /* not found is OK for empty history */ + TALER_TESTING_interpreter_next (is); + return; } - TALER_TESTING_interpreter_next (is); - return GNUNET_OK; - } - if (MHD_HTTP_OK != http_status) - { - hs->hh = NULL; - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Unwanted response code from /history/outgoing: %u\n", - http_status); - TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; - } - - /* check current element */ - if (GNUNET_OK != - check_result (hs->h, - hs->total, - hs->results_obtained, - details)) - { - char *acc; - GNUNET_break (0); - acc = json_dumps (json, - JSON_COMPACT); - GNUNET_log (GNUNET_ERROR_TYPE_ERROR, - "Result %u was `%s'\n", - (unsigned int) hs->results_obtained++, - acc); - if (NULL != acc) - free (acc); - hs->failed = GNUNET_YES; + goto error; + default: hs->hh = NULL; + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Unwanted response code from /history/incoming: %u\n", + dhr->http_status); TALER_TESTING_interpreter_fail (is); - return GNUNET_SYSERR; + return; } - hs->results_obtained++; - return GNUNET_OK; +error: + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Expected history of length %u, got %llu;" + " HTTP status code: %u/%d, failed: %d\n", + hs->total, + (unsigned long long) hs->results_obtained, + dhr->http_status, + (int) dhr->ec, + hs->failed ? 1 : 0); + print_expected (hs->h, + hs->total, + UINT_MAX); + TALER_TESTING_interpreter_fail (is); } @@ -475,6 +573,7 @@ history_run (void *cls, const uint64_t *row_ptr; (void) cmd; + hs->is = is; /* Get row_id from trait. */ if (NULL != hs->start_row_reference) { @@ -495,14 +594,16 @@ history_run (void *cls, TALER_LOG_DEBUG ("row id (from trait) is %llu\n", (unsigned long long) row_id); } - hs->total = build_history (is, &hs->h); - hs->hh = TALER_BANK_debit_history (is->ctx, - &hs->auth, - row_id, - hs->num_results, - GNUNET_TIME_UNIT_ZERO, - &history_cb, - is); + hs->total = build_history (hs, + &hs->h); + hs->hh = TALER_BANK_debit_history ( + TALER_TESTING_interpreter_get_context (is), + &hs->auth, + row_id, + hs->num_results, + GNUNET_TIME_UNIT_ZERO, + &history_cb, + hs); GNUNET_assert (NULL != hs->hh); } @@ -523,7 +624,8 @@ history_cleanup (void *cls, (void) cmd; if (NULL != hs->hh) { - TALER_LOG_WARNING ("/history/outgoing did not complete\n"); + TALER_TESTING_command_incomplete (hs->is, + cmd->label); TALER_BANK_debit_history_cancel (hs->hh); } for (unsigned int off = 0; off<hs->total; off++) |