diff options
Diffstat (limited to 'src/testing/testing_api_cmd_bank_history_credit.c')
-rw-r--r-- | src/testing/testing_api_cmd_bank_history_credit.c | 543 |
1 files changed, 322 insertions, 221 deletions
diff --git a/src/testing/testing_api_cmd_bank_history_credit.c b/src/testing/testing_api_cmd_bank_history_credit.c index c65c84c13..956e6c857 100644 --- a/src/testing/testing_api_cmd_bank_history_credit.c +++ b/src/testing/testing_api_cmd_bank_history_credit.c @@ -83,6 +83,11 @@ struct HistoryState struct TALER_BANK_CreditHistoryHandle *hh; /** + * The interpreter. + */ + struct TALER_TESTING_Interpreter *is; + + /** * Authentication data for the operation. */ struct TALER_BANK_AuthenticationData auth; @@ -144,35 +149,161 @@ 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 *credit_account; + const char *debit_account; + const struct TALER_Amount *amount; + const struct TALER_ReservePublicKeyP *reserve_pub; + const char *exchange_credit_url; + + /** + * The following command allows us to skip over those CMDs + * that do not offer a "row_id" trait. Such skipped CMDs are + * not interesting for building a history. + */ + if ( (GNUNET_OK != + TALER_TESTING_get_trait_bank_row (cmd, + &row_id)) || + (GNUNET_OK != + TALER_TESTING_get_trait_credit_payto_uri (cmd, + &credit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_debit_payto_uri (cmd, + &debit_account)) || + (GNUNET_OK != + TALER_TESTING_get_trait_amount (cmd, + &amount)) || + (GNUNET_OK != + TALER_TESTING_get_trait_reserve_pub (cmd, + &reserve_pub)) || + (GNUNET_OK != + TALER_TESTING_get_trait_exchange_bank_account_url ( + cmd, + &exchange_credit_url)) ) + return; // Not an interesting event + + /** + * Is the interesting event a match with regard to + * the row_id value? If yes, store this condition + * to the state and analyze the next CMDs. + */ + if ( (NULL != ic->row_id_start) && + (*(ic->row_id_start) == *row_id) && + (! ic->ok) ) + { + ic->ok = true; + return; + } + /** + * The interesting event didn't match the wanted + * row_id value, analyze the next CMDs. Note: this + * branch is relevant only when row_id WAS given. + */ + if (! ic->ok) + return; + if (0 != strcasecmp (hs->account_url, + exchange_credit_url)) + return; // Account mismatch + 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].url = GNUNET_strdup (debit_account); + ic->h[ic->pos].details.debit_account_uri = ic->h[ic->pos].url; + ic->h[ic->pos].details.amount = *amount; + ic->h[ic->pos].row_id = *row_id; + ic->h[ic->pos].details.reserve_pub = *reserve_pub; + 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 * @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; - unsigned int start; - unsigned int end; - - 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) { + 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); @@ -182,131 +313,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 transaction (EMPTY)\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; +} - if (hs->num_results > 0) + +/** + * 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; /* _inc_rement */ - start = 0; - end = is->ip - 1; + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - else + mptr = npt + strlen ("payto://"); + bic = strchr (mptr, '/'); + if (NULL == bic) { - inc = -1; - start = is->ip - 1; - end = 0; + GNUNET_break (0); + GNUNET_log (GNUNET_ERROR_TYPE_INFO, + "Invalid payto: %s\n", + npt); + GNUNET_free (npt); + return NULL; } - /** - * ok equals GNUNET_YES whenever a starting row_id - * was provided AND was found among the CMDs, OR no - * starting row was given in the first place. - */ - ok = GNUNET_NO; - if (NULL == row_id_start) - ok = GNUNET_YES; - h = NULL; - total = 0; - GNUNET_array_grow (h, - total, - 4); - pos = 0; - for (unsigned int off = start; off != end + inc; off += inc) + bic++; + iban = strchr (bic, '/'); + if (NULL != iban) { - const struct TALER_TESTING_Command *cmd = &is->commands[off]; - const uint64_t *row_id; - const char **credit_account; - const char **debit_account; - const struct TALER_Amount *amount; - const struct TALER_ReservePublicKeyP *reserve_pub; - const char **exchange_credit_url; - - /** - * The following command allows us to skip over those CMDs - * that do not offer a "row_id" trait. Such skipped CMDs are - * not interesting for building a history. - */ - if ( (GNUNET_OK != - TALER_TESTING_get_trait_bank_row (cmd, - &row_id)) || - (GNUNET_OK != - TALER_TESTING_get_trait_credit_payto_uri (cmd, - &credit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_debit_payto_uri (cmd, - &debit_account)) || - (GNUNET_OK != - TALER_TESTING_get_trait_amount (cmd, - &amount)) || - (GNUNET_OK != - TALER_TESTING_get_trait_reserve_pub (cmd, - &reserve_pub)) || - (GNUNET_OK != - TALER_TESTING_get_trait_exchange_bank_account_url ( - cmd, - &exchange_credit_url)) ) - continue; // Not an interesting event - /** - * Is the interesting event a match with regard to - * the row_id value? If yes, store this condition - * to the state and analyze the next CMDs. - */ - if ( (NULL != row_id_start) && - (*row_id_start == *row_id) && - (GNUNET_NO == ok) ) - { - ok = GNUNET_YES; - continue; - } - /** - * The interesting event didn't match the wanted - * row_id value, analyze the next CMDs. Note: this - * branch is relevant only when row_id WAS given. - */ - if (GNUNET_NO == ok) - continue; - if (0 != strcasecmp (hs->account_url, - *exchange_credit_url)) - continue; // Account mismatch - 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].url = GNUNET_strdup (*debit_account); - h[pos].details.debit_account_uri = h[pos].url; - h[pos].details.amount = *amount; - h[pos].row_id = *row_id; - h[pos].details.reserve_pub = *reserve_pub; - h[pos].details.credit_account_uri = *exchange_credit_url; - pos++; + /* need to remove bic */ + char *n; + + iban++; + GNUNET_asprintf (&n, + "payto://%.*s/%s", + (int) ((bic - mptr) - 1), + mptr, + iban); + GNUNET_free (npt); + npt = n; } - GNUNET_assert (GNUNET_YES == ok); - GNUNET_array_grow (h, - total, - pos); - if (0 == pos) - TALER_LOG_DEBUG ("Empty credit history computed\n"); - *rh = h; - return total; + return npt; } @@ -327,6 +418,9 @@ check_result (struct History *h, unsigned int off, const struct TALER_BANK_CreditDetails *details) { + char *u1; + char *u2; + if (off >= total) { GNUNET_log (GNUNET_ERROR_TYPE_ERROR, @@ -339,25 +433,42 @@ check_result (struct History *h, off); return GNUNET_SYSERR; } + u1 = normalize (h[off].details.debit_account_uri); + if (NULL == u1) + return GNUNET_SYSERR; + u2 = normalize (details->debit_account_uri); + if (NULL == u2) + { + GNUNET_free (u1); + return GNUNET_SYSERR; + } if ( (0 != GNUNET_memcmp (&h[off].details.reserve_pub, &details->reserve_pub)) || (0 != TALER_amount_cmp (&h[off].details.amount, &details->amount)) || - (0 != strcasecmp (h[off].details.debit_account_uri, - details->debit_account_uri)) ) + (0 != strcasecmp (u1, + u2)) ) { GNUNET_break (0); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "expected debit_account_uri: %s\n", - details->debit_account_uri); + "expected debit_account_uri: %s with %s for %s\n", + u1, + TALER_amount2s (&h[off].details.amount), + TALER_B2S (&h[off].details.reserve_pub)); GNUNET_log (GNUNET_ERROR_TYPE_INFO, - "actual debit_account_uri: %s\n", - h[off].details.debit_account_uri); + "actual debit_account_uri: %s with %s for %s\n", + u2, + TALER_amount2s (&details->amount), + TALER_B2S (&details->reserve_pub)); print_expected (h, total, off); + GNUNET_free (u1); + GNUNET_free (u2); return GNUNET_SYSERR; } + GNUNET_free (u1); + GNUNET_free (u2); return GNUNET_OK; } @@ -370,99 +481,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 chr 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_CreditDetails *details, - const json_t *json) + const struct TALER_BANK_CreditHistoryResponse *chr) { - 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 (chr->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<chr->details.ok.details_length; i++) + { + const struct TALER_BANK_CreditDetails *cd = + &chr->details.ok.details[i]; + + /* check current element */ + if (GNUNET_OK != + check_result (hs->h, + hs->total, + hs->results_obtained, + cd)) + { + GNUNET_break (0); + json_dumpf (chr->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) || - (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 ? 1 : 0); - 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) - { + GNUNET_break (0); + goto error; + default: hs->hh = NULL; GNUNET_log (GNUNET_ERROR_TYPE_ERROR, "Unwanted response code from /history/incoming: %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 = true; - hs->hh = NULL; + chr->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, + chr->http_status, + (int) chr->ec, + hs->failed ? 1 : 0); + print_expected (hs->h, + hs->total, + UINT_MAX); + TALER_TESTING_interpreter_fail (is); } @@ -483,6 +581,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) { @@ -503,15 +602,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->total = build_history (hs, &hs->h); - hs->hh = TALER_BANK_credit_history (is->ctx, - &hs->auth, - row_id, - hs->num_results, - GNUNET_TIME_UNIT_ZERO, - &history_cb, - is); + hs->hh = TALER_BANK_credit_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); } @@ -532,7 +632,8 @@ history_cleanup (void *cls, (void) cmd; if (NULL != hs->hh) { - TALER_LOG_WARNING ("/history/incoming did not complete\n"); + TALER_TESTING_command_incomplete (hs->is, + cmd->label); TALER_BANK_credit_history_cancel (hs->hh); } GNUNET_free (hs->account_url); |