Line data Source code
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_partners.c
18 : * @brief tests for the exchangedb functions whose primary table is
19 : * `partners`
20 : * @author Christian Grothoff
21 : *
22 : * Covers #TALER_EXCHANGEDB_insert_partner(). It is the only exported
23 : * function that writes the table; the rows are read back by the purse
24 : * machinery, which is covered in test_purse_merges.c and
25 : * test_purse_deposits.c.
26 : *
27 : * `partners` has no foreign keys. The insert is ON CONFLICT DO NOTHING,
28 : * so re-inserting the same (key, validity period) is a no-op rather than a
29 : * failure.
30 : */
31 : #include "test_common.h"
32 : #include "exchange-database/insert_partner.h"
33 :
34 :
35 : /**
36 : * Build a timestamp from a number of seconds since the epoch.
37 : *
38 : * @param secs seconds since the epoch
39 : * @return the timestamp
40 : */
41 : static struct GNUNET_TIME_Timestamp
42 8 : ts (uint64_t secs)
43 : {
44 8 : struct GNUNET_TIME_Absolute abs = {
45 8 : .abs_value_us = secs * 1000LLU * 1000LLU
46 : };
47 :
48 8 : return GNUNET_TIME_absolute_to_timestamp (abs);
49 : }
50 :
51 :
52 : /**
53 : * Insert a partner exchange.
54 : *
55 : * @param pg the database context
56 : * @param seed seed for the partner's key and signature
57 : * @param from start of the validity period
58 : * @param until end of the validity period
59 : * @param url base URL of the partner
60 : * @return transaction status
61 : */
62 : static enum GNUNET_DB_QueryStatus
63 4 : add_partner (struct TALER_EXCHANGEDB_PostgresContext *pg,
64 : uint32_t seed,
65 : uint64_t from,
66 : uint64_t until,
67 : const char *url)
68 : {
69 : struct TALER_MasterPublicKeyP master_pub;
70 : struct TALER_MasterSignatureP master_sig;
71 4 : struct TALER_Amount wad_fee = TDB_amount ("0.05");
72 :
73 4 : TDB_FILL (master_pub,
74 : seed);
75 4 : TDB_FILL (master_sig,
76 : seed);
77 4 : return TALER_EXCHANGEDB_insert_partner (pg,
78 : &master_pub,
79 : ts (from),
80 : ts (until),
81 : GNUNET_TIME_UNIT_HOURS,
82 : &wad_fee,
83 : url,
84 : &master_sig);
85 : }
86 :
87 :
88 : /**
89 : * Inserting a partner stores exactly the row we asked for.
90 : *
91 : * @param pg the database context
92 : * @return 0 on success
93 : */
94 : static int
95 1 : check_insert (struct TALER_EXCHANGEDB_PostgresContext *pg)
96 : {
97 : struct TALER_MasterPublicKeyP master_pub;
98 1 : struct TALER_Amount wad_fee = TDB_amount ("0.05");
99 : char *hex;
100 :
101 1 : FAILIF (0 != TDB_count (pg,
102 : "FROM partners"));
103 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
104 : add_partner (pg,
105 : 10,
106 : 1600000000,
107 : 1700000000,
108 : "https://partner.example/"));
109 1 : TDB_FILL (master_pub,
110 : 10);
111 1 : hex = TDB_hex (&master_pub,
112 : sizeof (master_pub));
113 1 : FAILIF_C (1 != TDB_count (pg,
114 : "FROM partners"
115 : " WHERE partner_master_pub=decode('%s','hex')"
116 : " AND partner_base_url='https://partner.example/'"
117 : " AND wad_fee=ROW(%llu,%u)::taler_amount"
118 : " AND start_date=%llu"
119 : " AND end_date=%llu",
120 : hex,
121 : (unsigned long long) wad_fee.value,
122 : (unsigned int) wad_fee.fraction,
123 : (unsigned long long) 1600000000000000LLU,
124 : (unsigned long long) 1700000000000000LLU),
125 : GNUNET_free (hex));
126 1 : GNUNET_free (hex);
127 1 : return 0;
128 : }
129 :
130 :
131 : /**
132 : * Re-inserting the identical row is a no-op, while a different validity
133 : * period for the same key is a new row.
134 : *
135 : * @param pg the database context
136 : * @return 0 on success
137 : */
138 : static int
139 1 : check_conflict (struct TALER_EXCHANGEDB_PostgresContext *pg)
140 : {
141 : /* identical to what check_insert() stored */
142 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_NO_RESULTS !=
143 : add_partner (pg,
144 : 10,
145 : 1600000000,
146 : 1700000000,
147 : "https://partner.example/"));
148 1 : FAILIF (1 != TDB_count (pg,
149 : "FROM partners"));
150 : /* a later period for the same partner is a separate row */
151 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
152 : add_partner (pg,
153 : 10,
154 : 1700000000,
155 : 1800000000,
156 : "https://partner.example/"));
157 1 : FAILIF (2 != TDB_count (pg,
158 : "FROM partners"));
159 : /* and so is a different partner */
160 1 : FAILIF (GNUNET_DB_STATUS_SUCCESS_ONE_RESULT !=
161 : add_partner (pg,
162 : 11,
163 : 1600000000,
164 : 1700000000,
165 : "https://other.example/"));
166 1 : FAILIF (3 != TDB_count (pg,
167 : "FROM partners"));
168 1 : return 0;
169 : }
170 :
171 :
172 : /**
173 : * The checks to run, in order.
174 : */
175 : static const struct TDB_Test tests[] = {
176 : { "partners-insert",
177 : &check_insert },
178 : { "partners-conflict",
179 : &check_conflict },
180 : { NULL, NULL }
181 : };
182 :
183 :
184 : int
185 1 : main (int argc,
186 : char *const *argv)
187 : {
188 1 : return TDB_main (argc,
189 : argv,
190 : "test-partners",
191 : "Tests for the exchangedb `partners' table",
192 : tests);
193 : }
194 :
195 :
196 : /* end of test_partners.c */
|