test_legitimization_measures.c (27725B)
1 /* 2 This file is part of TALER 3 Copyright (C) 2026 Taler Systems SA 4 5 TALER is free software; you can redistribute it and/or modify it under the 6 terms of the GNU General Public License as published by the Free Software 7 Foundation; either version 3, or (at your option) any later version. 8 9 TALER is distributed in the hope that it will be useful, but WITHOUT ANY 10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 A PARTICULAR PURPOSE. See the GNU General Public License for more details. 12 13 You should have received a copy of the GNU General Public License along with 14 TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/> 15 */ 16 /** 17 * @file exchangedb/test_legitimization_measures.c 18 * @brief tests for the exchangedb functions whose primary table is 19 * `legitimization_measures` 20 * @author Christian Grothoff 21 * 22 * Covers #TALER_EXCHANGEDB_do_trigger_kyc_rule_for_account(), 23 * #TALER_EXCHANGEDB_insert_active_legitimization_measure(), 24 * #TALER_EXCHANGEDB_insert_successor_measure(), 25 * #TALER_EXCHANGEDB_get_pending_legitimization(), 26 * #TALER_EXCHANGEDB_get_active_legitimization(), 27 * #TALER_EXCHANGEDB_get_completed_legitimization(), 28 * #TALER_EXCHANGEDB_get_kyc_status_by_token(), 29 * #TALER_EXCHANGEDB_get_open_legitimization_measure(), 30 * #TALER_EXCHANGEDB_get_legitimization_requirement_by_row() and 31 * #TALER_EXCHANGEDB_iterate_aml_measures(). 32 * 33 * A row of `legitimization_measures` is one set of KYC measures the 34 * exchange put on an account. It is keyed by the account's access token, 35 * so `kyc_targets` has to exist first -- do_trigger_kyc_rule_for_account() 36 * creates it when it has to. Only one measure set per account is active 37 * at a time: triggering a different set finishes the previous one. 38 */ 39 #include "test_common.h" 40 #include "exchange-database/do_trigger_kyc_rule_for_account.h" 41 #include "exchange-database/get_active_legitimization.h" 42 #include "exchange-database/get_completed_legitimization.h" 43 #include "exchange-database/get_kyc_status_by_token.h" 44 #include "exchange-database/get_legitimization_requirement_by_row.h" 45 #include "exchange-database/get_open_legitimization_measure.h" 46 #include "exchange-database/get_pending_legitimization.h" 47 #include "exchange-database/insert_active_legitimization_measure.h" 48 #include "exchange-database/insert_legitimization_process.h" 49 #include "exchange-database/insert_successor_measure.h" 50 #include "exchange-database/iterate_aml_measures.h" 51 52 53 /** 54 * Account the checks put measures on. 55 */ 56 static struct TDB_Account account; 57 58 59 /** 60 * Row of the measure set that check_trigger() created. 61 */ 62 static uint64_t measure_row; 63 64 65 /** 66 * Key the account may use to authorise KYC work on itself. 67 */ 68 static union TALER_AccountPublicKeyP account_pub; 69 70 71 /** 72 * Build a timestamp from a number of seconds since the epoch. 73 * 74 * @param secs seconds since the epoch 75 * @return the timestamp 76 */ 77 static struct GNUNET_TIME_Timestamp 78 ts (uint64_t secs) 79 { 80 struct GNUNET_TIME_Absolute abs = { 81 .abs_value_us = secs * 1000LLU * 1000LLU 82 }; 83 84 return GNUNET_TIME_absolute_to_timestamp (abs); 85 } 86 87 88 /** 89 * Build a measure set naming @a check. 90 * 91 * @param check name of the check to demand 92 * @return the measure set, to be freed with json_decref() 93 */ 94 static json_t * 95 make_measures (const char *check) 96 { 97 json_t *j; 98 99 j = GNUNET_JSON_PACK ( 100 GNUNET_JSON_pack_array_steal ( 101 "measures", 102 json_pack ("[{s:s,s:s,s:s}]", 103 "check_name", 104 check, 105 "prog_name", 106 "skip", 107 "context", 108 "none")), 109 GNUNET_JSON_pack_bool ("is_and_combinator", 110 true)); 111 GNUNET_assert (NULL != j); 112 return j; 113 } 114 115 116 /** 117 * Read the access token the database generated for our account. 118 * 119 * @param pg the database context 120 * @param[out] access_token set to the account's access token 121 */ 122 static void 123 get_access_token (struct TALER_EXCHANGEDB_PostgresContext *pg, 124 struct TALER_AccountAccessTokenP *access_token) 125 { 126 struct GNUNET_PQ_QueryParam params[] = { 127 GNUNET_PQ_query_param_auto_from_type (&account.h_normalized), 128 GNUNET_PQ_query_param_end 129 }; 130 struct GNUNET_PQ_ResultSpec rs[] = { 131 GNUNET_PQ_result_spec_auto_from_type ("access_token", 132 access_token), 133 GNUNET_PQ_result_spec_end 134 }; 135 136 GNUNET_assert (GNUNET_OK == 137 GNUNET_PQ_prepare_anon (pg->conn, 138 "SELECT access_token" 139 " FROM kyc_targets" 140 " WHERE h_normalized_payto=$1;")); 141 GNUNET_assert (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT == 142 GNUNET_PQ_eval_prepared_singleton_select (pg->conn, 143 "", 144 params, 145 rs)); 146 } 147 148 149 /** 150 * Closure for #measure_cb(). 151 */ 152 struct MeasureContext 153 { 154 /** 155 * How many measures did the callback see? 156 */ 157 unsigned int total; 158 159 /** 160 * Row we are looking for, 0 to match nothing. 161 */ 162 uint64_t measure_row; 163 164 /** 165 * How many times did we see it? 166 */ 167 unsigned int matched; 168 169 /** 170 * Was it reported as finished? 171 */ 172 bool is_finished; 173 174 /** 175 * Account it was reported for. 176 */ 177 struct TALER_NormalizedPaytoHashP h_payto; 178 }; 179 180 181 /** 182 * Callback for #TALER_EXCHANGEDB_iterate_aml_measures(). 183 * 184 * @param cls a `struct MeasureContext *` 185 * @param h_payto account the measure applies to 186 * @param start_time when the measure was imposed 187 * @param jmeasures the measures 188 * @param is_finished whether the measure was completed 189 * @param measure_serial_id row of the measure 190 */ 191 static void 192 measure_cb (void *cls, 193 struct TALER_NormalizedPaytoHashP *h_payto, 194 struct GNUNET_TIME_Absolute start_time, 195 const json_t *jmeasures, 196 bool is_finished, 197 uint64_t measure_serial_id) 198 { 199 struct MeasureContext *ctx = cls; 200 201 (void) start_time; 202 (void) jmeasures; 203 ctx->total++; 204 if ( (0 != ctx->measure_row) && 205 (measure_serial_id == ctx->measure_row) ) 206 { 207 ctx->matched++; 208 ctx->is_finished = is_finished; 209 ctx->h_payto = *h_payto; 210 } 211 } 212 213 214 /** 215 * Nothing is known while the table is empty. 216 * 217 * @param pg the database context 218 * @return 0 on success 219 */ 220 static int 221 check_empty (struct TALER_EXCHANGEDB_PostgresContext *pg) 222 { 223 struct TALER_NormalizedPaytoHashP h_payto; 224 struct TALER_AccountAccessTokenP access_token; 225 struct GNUNET_TIME_Absolute start_time; 226 struct MeasureContext ctx = { 0 }; 227 json_t *jmeasures = NULL; 228 uint64_t row; 229 bool is_finished; 230 bool is_wallet; 231 232 TDB_FILL (h_payto, 233 1); 234 TDB_FILL (access_token, 235 1); 236 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 237 TALER_EXCHANGEDB_get_pending_legitimization (pg, 238 1, 239 &access_token, 240 &h_payto, 241 &jmeasures, 242 &is_finished, 243 &is_wallet)); 244 FAILIF (NULL != jmeasures); 245 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 246 TALER_EXCHANGEDB_get_kyc_status_by_token (pg, 247 &access_token, 248 &row, 249 &jmeasures)); 250 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 251 TALER_EXCHANGEDB_get_open_legitimization_measure (pg, 252 &h_payto, 253 &row, 254 &start_time)); 255 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 256 TALER_EXCHANGEDB_iterate_aml_measures (pg, 257 NULL, 258 TALER_EXCHANGE_YNA_ALL, 259 0, 260 10, 261 &measure_cb, 262 &ctx)); 263 FAILIF (0 != ctx.total); 264 return 0; 265 } 266 267 268 /** 269 * Triggering a rule creates the account and the measure set. 270 * 271 * @param pg the database context 272 * @return 0 on success 273 */ 274 static int 275 check_trigger (struct TALER_EXCHANGEDB_PostgresContext *pg) 276 { 277 struct TALER_NormalizedPayto npayto; 278 struct TALER_FullPayto payto; 279 struct TALER_NormalizedPaytoHashP h_payto; 280 struct TALER_AccountAccessTokenP access_token; 281 struct TALER_AccountAccessTokenP got_token; 282 struct TALER_NormalizedPaytoHashP got_payto; 283 json_t *jmeasures = make_measures ("verify-id"); 284 json_t *got = NULL; 285 uint64_t row = 0; 286 bool bad_kyc_auth = false; 287 bool is_finished = true; 288 bool is_wallet = true; 289 290 payto.full_payto = (char *) 291 "payto://x-taler-bank/localhost/lm?receiver-name=LM"; 292 TALER_full_payto_hash (payto, 293 &account.h_full); 294 npayto = TALER_payto_normalize (payto); 295 GNUNET_assert (NULL != npayto.normalized_payto); 296 TALER_normalized_payto_hash (npayto, 297 &h_payto); 298 GNUNET_free (npayto.normalized_payto); 299 account.payto.full_payto = GNUNET_strdup (payto.full_payto); 300 account.h_normalized = h_payto; 301 302 TDB_FILL (account_pub, 303 42); 304 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 305 TALER_EXCHANGEDB_do_trigger_kyc_rule_for_account ( 306 pg, 307 payto, 308 &h_payto, 309 &account_pub, 310 NULL, 311 jmeasures, 312 1, 313 &row, 314 &bad_kyc_auth), 315 json_decref (jmeasures)); 316 FAILIF_C (0 == row, 317 json_decref (jmeasures)); 318 /* The account was not known, so there was no key to match against; the 319 function reports that even though it just set one. */ 320 FAILIF_C (! bad_kyc_auth, 321 json_decref (jmeasures)); 322 measure_row = row; 323 FAILIF_C (1 != TDB_count (pg, 324 "FROM legitimization_measures"), 325 json_decref (jmeasures)); 326 /* the account was created along the way */ 327 FAILIF_C (1 != TDB_count (pg, 328 "FROM kyc_targets"), 329 json_decref (jmeasures)); 330 FAILIF_C (1 != TDB_count (pg, 331 "FROM wire_targets"), 332 json_decref (jmeasures)); 333 334 /* the measure set can be read back by its row... */ 335 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 336 TALER_EXCHANGEDB_get_pending_legitimization (pg, 337 row, 338 &got_token, 339 &got_payto, 340 &got, 341 &is_finished, 342 &is_wallet), 343 json_decref (jmeasures)); 344 FAILIF_C (0 != GNUNET_memcmp (&got_payto, 345 &h_payto), 346 json_decref (got); json_decref (jmeasures)); 347 FAILIF_C (is_finished, 348 json_decref (got); json_decref (jmeasures)); 349 FAILIF_C (is_wallet, 350 json_decref (got); json_decref (jmeasures)); 351 FAILIF_C (1 != json_equal (got, 352 jmeasures), 353 json_decref (got); json_decref (jmeasures)); 354 json_decref (got); 355 got = NULL; 356 357 /* ...and by the account's access token */ 358 get_access_token (pg, 359 &access_token); 360 FAILIF_C (0 != GNUNET_memcmp (&got_token, 361 &access_token), 362 json_decref (jmeasures)); 363 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 364 TALER_EXCHANGEDB_get_kyc_status_by_token (pg, 365 &access_token, 366 &row, 367 &got), 368 json_decref (jmeasures)); 369 FAILIF_C (row != measure_row, 370 json_decref (got); json_decref (jmeasures)); 371 json_decref (got); 372 373 /* triggering the very same measure set again keeps the one row */ 374 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 375 TALER_EXCHANGEDB_do_trigger_kyc_rule_for_account ( 376 pg, 377 payto, 378 &h_payto, 379 NULL, 380 NULL, 381 jmeasures, 382 1, 383 &row, 384 &bad_kyc_auth), 385 json_decref (jmeasures)); 386 FAILIF_C (row != measure_row, 387 json_decref (jmeasures)); 388 FAILIF_C (1 != TDB_count (pg, 389 "FROM legitimization_measures"), 390 json_decref (jmeasures)); 391 json_decref (jmeasures); 392 return 0; 393 } 394 395 396 /** 397 * A different measure set supersedes the previous one, which is marked 398 * finished. 399 * 400 * @param pg the database context 401 * @return 0 on success 402 */ 403 static int 404 check_supersede (struct TALER_EXCHANGEDB_PostgresContext *pg) 405 { 406 json_t *jmeasures = make_measures ("verify-address"); 407 uint64_t row = 0; 408 bool bad_kyc_auth = false; 409 410 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 411 TALER_EXCHANGEDB_do_trigger_kyc_rule_for_account ( 412 pg, 413 account.payto, 414 &account.h_normalized, 415 NULL, 416 NULL, 417 jmeasures, 418 2, 419 &row, 420 &bad_kyc_auth), 421 json_decref (jmeasures)); 422 json_decref (jmeasures); 423 FAILIF (row == measure_row); 424 FAILIF (2 != TDB_count (pg, 425 "FROM legitimization_measures")); 426 FAILIF (1 != TDB_count (pg, 427 "FROM legitimization_measures" 428 " WHERE is_finished")); 429 measure_row = row; 430 return 0; 431 } 432 433 434 /** 435 * The account's rule requirement and its open measure are reported. 436 * 437 * @param pg the database context 438 * @return 0 on success 439 */ 440 static int 441 check_requirement (struct TALER_EXCHANGEDB_PostgresContext *pg) 442 { 443 struct TALER_AccountAccessTokenP access_token; 444 struct TALER_AccountAccessTokenP got_token; 445 struct GNUNET_TIME_Absolute start_time; 446 enum GNUNET_GenericReturnValue is_wallet; 447 json_t *jrules = NULL; 448 uint64_t rule_gen = 42; 449 uint64_t row = 0; 450 bool aml_review = true; 451 bool kyc_required = false; 452 453 get_access_token (pg, 454 &access_token); 455 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 456 TALER_EXCHANGEDB_get_legitimization_requirement_by_row ( 457 pg, 458 &account.h_normalized, 459 &account_pub, 460 &is_wallet, 461 &got_token, 462 &rule_gen, 463 &jrules, 464 &aml_review, 465 &kyc_required)); 466 FAILIF_C (0 != GNUNET_memcmp (&got_token, 467 &access_token), 468 json_decref (jrules)); 469 FAILIF_C (GNUNET_NO != is_wallet, 470 json_decref (jrules)); 471 /* no AML decision has been taken for the account */ 472 FAILIF_C (aml_review, 473 json_decref (jrules)); 474 FAILIF_C (0 != rule_gen, 475 json_decref (jrules)); 476 /* ...but a measure is pending, so KYC is required */ 477 FAILIF_C (! kyc_required, 478 json_decref (jrules)); 479 json_decref (jrules); 480 481 /* the exchange's own account of why it is holding back */ 482 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 483 TALER_EXCHANGEDB_get_open_legitimization_measure ( 484 pg, 485 &account.h_normalized, 486 &row, 487 &start_time)); 488 /* the oldest undecided measure is the one from check_trigger() */ 489 FAILIF (0 == row); 490 FAILIF (GNUNET_TIME_absolute_is_zero (start_time)); 491 492 /* an account nobody put a measure on has none */ 493 { 494 struct TALER_NormalizedPaytoHashP other; 495 496 TDB_FILL (other, 497 98); 498 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 499 TALER_EXCHANGEDB_get_open_legitimization_measure (pg, 500 &other, 501 &row, 502 &start_time)); 503 } 504 return 0; 505 } 506 507 508 /** 509 * A legitimization process ties a measure set to the check it is 510 * satisfying. 511 * 512 * @param pg the database context 513 * @return 0 on success 514 */ 515 static int 516 check_process (struct TALER_EXCHANGEDB_PostgresContext *pg) 517 { 518 struct TALER_AccountAccessTokenP access_token; 519 struct TALER_NormalizedPaytoHashP h_payto; 520 json_t *jmeasures = NULL; 521 size_t attributes_len = 42; 522 void *attributes = NULL; 523 uint64_t process_row = 0; 524 uint32_t measure_index = 99; 525 bool is_finished = true; 526 bool is_wallet = true; 527 528 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 529 TALER_EXCHANGEDB_insert_legitimization_process ( 530 pg, 531 &account.h_normalized, 532 0, 533 measure_row, 534 "test-provider", 535 "provider-account-1", 536 "provider-legi-1", 537 &process_row)); 538 FAILIF (0 == process_row); 539 540 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 541 TALER_EXCHANGEDB_get_active_legitimization (pg, 542 process_row, 543 &measure_index, 544 &jmeasures)); 545 FAILIF_C (0 != measure_index, 546 json_decref (jmeasures)); 547 FAILIF_C (NULL == jmeasures, 548 json_decref (jmeasures)); 549 json_decref (jmeasures); 550 jmeasures = NULL; 551 552 /* a process that does not exist has no active measure */ 553 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 554 TALER_EXCHANGEDB_get_active_legitimization (pg, 555 process_row + 1000, 556 &measure_index, 557 &jmeasures)); 558 559 /* the completed view reaches the same measure through the process */ 560 FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 561 TALER_EXCHANGEDB_get_completed_legitimization (pg, 562 measure_row, 563 0, 564 &access_token, 565 &h_payto, 566 &is_wallet, 567 &jmeasures, 568 &is_finished, 569 &attributes_len, 570 &attributes)); 571 FAILIF_C (0 != GNUNET_memcmp (&h_payto, 572 &account.h_normalized), 573 json_decref (jmeasures); GNUNET_free (attributes)); 574 FAILIF_C (is_finished, 575 json_decref (jmeasures); GNUNET_free (attributes)); 576 /* no attributes have been collected yet */ 577 FAILIF_C (NULL != attributes, 578 json_decref (jmeasures); GNUNET_free (attributes)); 579 json_decref (jmeasures); 580 jmeasures = NULL; 581 582 /* a measure index no process is working on is not found */ 583 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 584 TALER_EXCHANGEDB_get_completed_legitimization (pg, 585 measure_row, 586 7, 587 &access_token, 588 &h_payto, 589 &is_wallet, 590 &jmeasures, 591 &is_finished, 592 &attributes_len, 593 &attributes)); 594 return 0; 595 } 596 597 598 /** 599 * A measure can also be created directly against an access token. 600 * 601 * @param pg the database context 602 * @return 0 on success 603 */ 604 static int 605 check_insert_active (struct TALER_EXCHANGEDB_PostgresContext *pg) 606 { 607 struct TALER_AccountAccessTokenP access_token; 608 json_t *jmeasures = make_measures ("verify-phone"); 609 uint64_t row = 0; 610 611 get_access_token (pg, 612 &access_token); 613 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 614 TALER_EXCHANGEDB_insert_active_legitimization_measure ( 615 pg, 616 &access_token, 617 jmeasures, 618 &row), 619 json_decref (jmeasures)); 620 json_decref (jmeasures); 621 FAILIF (0 == row); 622 FAILIF (3 != TDB_count (pg, 623 "FROM legitimization_measures")); 624 /* it becomes the one active measure for the account */ 625 FAILIF (1 != TDB_count (pg, 626 "FROM legitimization_measures" 627 " WHERE NOT is_finished")); 628 measure_row = row; 629 630 return 0; 631 } 632 633 634 /** 635 * A successor measure replaces what is active for the account. 636 * 637 * @param pg the database context 638 * @return 0 on success 639 */ 640 static int 641 check_successor (struct TALER_EXCHANGEDB_PostgresContext *pg) 642 { 643 struct GNUNET_TIME_Timestamp last_date; 644 json_t *jmeasures = make_measures ("verify-income"); 645 bool unknown_account = true; 646 647 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 648 TALER_EXCHANGEDB_insert_successor_measure ( 649 pg, 650 &account.h_normalized, 651 ts (1600000000), 652 "verify-income", 653 jmeasures, 654 &unknown_account, 655 &last_date), 656 json_decref (jmeasures)); 657 FAILIF_C (unknown_account, 658 json_decref (jmeasures)); 659 FAILIF_C (4 != TDB_count (pg, 660 "FROM legitimization_measures"), 661 json_decref (jmeasures)); 662 663 /* an older decision is ignored */ 664 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 665 TALER_EXCHANGEDB_insert_successor_measure ( 666 pg, 667 &account.h_normalized, 668 ts (1500000000), 669 "verify-income", 670 jmeasures, 671 &unknown_account, 672 &last_date), 673 json_decref (jmeasures)); 674 FAILIF_C (GNUNET_TIME_timestamp_cmp (last_date, 675 !=, 676 ts (1600000000)), 677 json_decref (jmeasures)); 678 FAILIF_C (4 != TDB_count (pg, 679 "FROM legitimization_measures"), 680 json_decref (jmeasures)); 681 682 /* an account the exchange never saw is reported as unknown */ 683 { 684 struct TALER_NormalizedPaytoHashP other; 685 686 TDB_FILL (other, 687 95); 688 unknown_account = false; 689 FAILIF_C (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT != 690 TALER_EXCHANGEDB_insert_successor_measure ( 691 pg, 692 &other, 693 ts (1600000000), 694 "verify-income", 695 jmeasures, 696 &unknown_account, 697 &last_date), 698 json_decref (jmeasures)); 699 FAILIF_C (! unknown_account, 700 json_decref (jmeasures)); 701 } 702 json_decref (jmeasures); 703 return 0; 704 } 705 706 707 /** 708 * The measure listing reports the account's measures, filtered. 709 * 710 * @param pg the database context 711 * @return 0 on success 712 */ 713 static int 714 check_iterate (struct TALER_EXCHANGEDB_PostgresContext *pg) 715 { 716 struct MeasureContext ctx; 717 unsigned int all; 718 719 memset (&ctx, 720 0, 721 sizeof (ctx)); 722 FAILIF (0 >= 723 TALER_EXCHANGEDB_iterate_aml_measures (pg, 724 &account.h_normalized, 725 TALER_EXCHANGE_YNA_ALL, 726 0, 727 100, 728 &measure_cb, 729 &ctx)); 730 all = ctx.total; 731 FAILIF (0 == all); 732 733 /* the account filter matters */ 734 { 735 struct TALER_NormalizedPaytoHashP other; 736 737 TDB_FILL (other, 738 94); 739 memset (&ctx, 740 0, 741 sizeof (ctx)); 742 FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS != 743 TALER_EXCHANGEDB_iterate_aml_measures (pg, 744 &other, 745 TALER_EXCHANGE_YNA_ALL, 746 0, 747 100, 748 &measure_cb, 749 &ctx)); 750 FAILIF (0 != ctx.total); 751 } 752 753 /* only one measure is still open */ 754 memset (&ctx, 755 0, 756 sizeof (ctx)); 757 FAILIF (0 >= 758 TALER_EXCHANGEDB_iterate_aml_measures (pg, 759 &account.h_normalized, 760 TALER_EXCHANGE_YNA_YES, 761 0, 762 100, 763 &measure_cb, 764 &ctx)); 765 FAILIF (1 != ctx.total); 766 767 /* the finished ones make up the rest */ 768 memset (&ctx, 769 0, 770 sizeof (ctx)); 771 FAILIF (0 >= 772 TALER_EXCHANGEDB_iterate_aml_measures (pg, 773 &account.h_normalized, 774 TALER_EXCHANGE_YNA_NO, 775 0, 776 100, 777 &measure_cb, 778 &ctx)); 779 FAILIF (all - 1 != ctx.total); 780 781 /* the limit caps the result set */ 782 memset (&ctx, 783 0, 784 sizeof (ctx)); 785 FAILIF (1 != 786 TALER_EXCHANGEDB_iterate_aml_measures (pg, 787 NULL, 788 TALER_EXCHANGE_YNA_ALL, 789 0, 790 1, 791 &measure_cb, 792 &ctx)); 793 FAILIF (1 != ctx.total); 794 return 0; 795 } 796 797 798 /** 799 * The checks to run, in order. 800 */ 801 static const struct TDB_Test tests[] = { 802 { "legitimization-measures-empty", 803 &check_empty }, 804 { "legitimization-measures-trigger", 805 &check_trigger }, 806 { "legitimization-measures-supersede", 807 &check_supersede }, 808 { "legitimization-measures-requirement", 809 &check_requirement }, 810 { "legitimization-measures-process", 811 &check_process }, 812 { "legitimization-measures-insert-active", 813 &check_insert_active }, 814 { "legitimization-measures-successor", 815 &check_successor }, 816 { "legitimization-measures-iterate", 817 &check_iterate }, 818 { NULL, NULL } 819 }; 820 821 822 int 823 main (int argc, 824 char *const *argv) 825 { 826 int ret; 827 828 ret = TDB_main (argc, 829 argv, 830 "test-legitimization-measures", 831 "Tests for the exchangedb `legitimization_measures' table", 832 tests); 833 TDB_account_free (&account); 834 return ret; 835 } 836 837 838 /* end of test_legitimization_measures.c */