add test for emergencies, and associated bugfixes to auditor and auditor report
This commit is contained in:
parent
e3564de010
commit
160a4ef52c
@ -224,15 +224,25 @@ exposure} is the amount of coins in circulation for a particular
|
|||||||
denomination and the maximum loss for the exchange from this type of
|
denomination and the maximum loss for the exchange from this type of
|
||||||
compromise.
|
compromise.
|
||||||
|
|
||||||
{% if (data.emergencies|length() != 0)
|
{% if (data.emergencies|length() != 0) %}
|
||||||
or (data.emergencies_by_count|length() != 0) %}
|
The total risk from emergencies detected by amount is
|
||||||
The total risk from emergencies is
|
{\bf {{ data.emergencies_risk_by_amount }} }.
|
||||||
{\bf {{ data.emergencies_risk_total }} }
|
The total loss from emergencies detected by amount is
|
||||||
|
{\bf {{ data.emergencies_loss }} }.
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
{% if (data.emergencies_by_count|length() != 0) %}
|
||||||
|
The total risk from emergencies detected by counting coins is
|
||||||
|
{\bf {{ data.emergencies_risk_by_count }} }
|
||||||
|
The total loss from emergencies detected by counting coins could be up to
|
||||||
|
{\bf {{ data.emergencies_loss_by_count }} }.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
\subsubsection{Emergencies by counting coins}
|
\subsubsection{Emergencies by counting coins}
|
||||||
|
|
||||||
|
% Table generation tested by testcase #18 in test-auditor.sh
|
||||||
|
|
||||||
{% if data.emergencies_by_count|length() == 0 %}
|
{% if data.emergencies_by_count|length() == 0 %}
|
||||||
{\bf No emergencies detected by counting coins.}
|
{\bf No emergencies detected by counting coins.}
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -251,7 +261,7 @@ The total risk from emergencies is
|
|||||||
\label{table:emergencies_coin_counting}
|
\label{table:emergencies_coin_counting}
|
||||||
\endlastfoot
|
\endlastfoot
|
||||||
{% for item in data.emergencies_by_count %}
|
{% for item in data.emergencies_by_count %}
|
||||||
\multicolumn{4}{l}{ {\tt \truncate{\textwidth}{ {{ item.denompub_hash }} } } } \\
|
\multicolumn{4}{l}{ {\tt \truncate{0.95\textwidth}{ {{ item.denompub_hash }} } } } \\
|
||||||
\nopagebreak
|
\nopagebreak
|
||||||
{{ item.value }} &
|
{{ item.value }} &
|
||||||
{\tiny \begin{tabular}{c}
|
{\tiny \begin{tabular}{c}
|
||||||
@ -264,8 +274,11 @@ The total risk from emergencies is
|
|||||||
\end{longtable}
|
\end{longtable}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
\subsubsection{Emergencies by value deposited}
|
\subsubsection{Emergencies by value deposited}
|
||||||
|
|
||||||
|
% Table generation tested by testcase #18 in test-auditor.sh
|
||||||
|
|
||||||
{% if data.emergencies|length() == 0 %}
|
{% if data.emergencies|length() == 0 %}
|
||||||
{\bf No emergencies by value detected.}
|
{\bf No emergencies by value detected.}
|
||||||
{% else %}
|
{% else %}
|
||||||
@ -284,7 +297,7 @@ The total risk from emergencies is
|
|||||||
\label{table:emergencies}
|
\label{table:emergencies}
|
||||||
\endlastfoot
|
\endlastfoot
|
||||||
{% for item in data.emergencies %}
|
{% for item in data.emergencies %}
|
||||||
\multicolumn{4}{l}{ {\tt \truncate{\textwidth}{ {{ item.denompub_hash }} } } } \\
|
\multicolumn{4}{l}{ {\tt \truncate{0.95\textwidth}{ {{ item.denompub_hash }} } } } \\
|
||||||
\nopagebreak
|
\nopagebreak
|
||||||
{{ item.value }} &
|
{{ item.value }} &
|
||||||
{\tiny \begin{tabular}{c}
|
{\tiny \begin{tabular}{c}
|
||||||
@ -306,32 +319,40 @@ Disagreements imply that either the exchange made a loss (sending out
|
|||||||
too much money), or screwed a customer (and thus at least needs to fix
|
too much money), or screwed a customer (and thus at least needs to fix
|
||||||
the financial damage done to the customer).
|
the financial damage done to the customer).
|
||||||
|
|
||||||
|
% Table generation tested by testcase #18 in test-auditor.sh
|
||||||
|
|
||||||
{% if data.amount_arithmetic_inconsistencies|length() == 0 %}
|
{% if data.amount_arithmetic_inconsistencies|length() == 0 %}
|
||||||
{\bf No arithmetic problems detected.}
|
{\bf No arithmetic problems detected.}
|
||||||
{% else %}
|
{% else %}
|
||||||
\begin{longtable}{p{3.5cm}|l|r|r}
|
\begin{longtable}{p{3.5cm}|r|r|r|c}
|
||||||
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
|
{\bf Operation} & {\bf Row} & {\bf Exchange} & {\bf Auditor} & {\bf P} \\
|
||||||
\hline \hline
|
\hline \hline
|
||||||
\endfirsthead
|
\endfirsthead
|
||||||
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\ \hline \hline
|
{\bf Operation} & {\bf Row} & {\bf Exchange} & {\bf Auditor} & {\bf P} \\ \hline \hline
|
||||||
\endhead
|
\endhead
|
||||||
\hline \hline
|
\hline \hline
|
||||||
{\bf Operation} & {\bf Table row} & \multicolumn{2}{|c|}{ {\bf Exchange}} & \multicolumn{2}{|c}{ {\bf Auditor}} \\
|
{\bf Operation} & {\bf Row} & {\bf Exchange} & {\bf Auditor} & {\bf P} \\
|
||||||
\endfoot
|
\endfoot
|
||||||
\hline \hline
|
\hline \hline
|
||||||
\multicolumn{2}{l|}{ {\bf $\sum$ Deltas (Auditor-Exchange)} } &
|
\multicolumn{2}{l|}{ {\bf $\sum$ Deltas (Auditor-Exchange)} } &
|
||||||
+ {{ data.total_arithmetic_delta_plus }} &
|
+ {{ data.total_arithmetic_delta_plus }} &
|
||||||
- {{ data.total_arithmetic_delta_minus }} \\
|
- {{ data.total_arithmetic_delta_minus }} & \\
|
||||||
\caption{Arithmetic inconsistencies.}
|
\caption{Arithmetic inconsistencies.}
|
||||||
\label{table:amount:arithmetic:inconsistencies}
|
\label{table:amount:arithmetic:inconsistencies}
|
||||||
\endlastfoot
|
\endlastfoot
|
||||||
{% for item in data.amount_arithmetic_inconsistencies %}
|
{% for item in data.amount_arithmetic_inconsistencies %}
|
||||||
{{ item.operation }} &
|
\truncate{3.3cm}{ {\tiny {{ item.operation }} } } &
|
||||||
{{ item.rowid }} &
|
{{ item.rowid }} &
|
||||||
{{ item.exchange }} &
|
{{ item.exchange }} &
|
||||||
{{ item.auditor }} \\ \hline
|
{{ item.auditor }} &
|
||||||
|
{{ item.profitable }} \\ \hline
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
\end{longtable}
|
\end{longtable}
|
||||||
|
|
||||||
|
The {\bf P} colum is set to "1" if the arithmetic problem was be determined to be
|
||||||
|
profitable for the exchange, "-1" if the problem resulted in a net loss for
|
||||||
|
the exchange, and "0" if this is unclear or at least the gain/loss is not
|
||||||
|
easily determined from the amounts and thus not included in the totals.
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
||||||
|
|
||||||
|
@ -268,10 +268,25 @@ static json_int_t number_missed_deposit_confirmations;
|
|||||||
*/
|
*/
|
||||||
static struct TALER_Amount total_missed_deposit_confirmations;
|
static struct TALER_Amount total_missed_deposit_confirmations;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total amount reported in all calls to #report_emergency_by_count().
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount reported_emergency_risk_by_count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Total amount reported in all calls to #report_emergency().
|
* Total amount reported in all calls to #report_emergency().
|
||||||
*/
|
*/
|
||||||
static struct TALER_Amount reported_emergency_sum;
|
static struct TALER_Amount reported_emergency_risk_by_amount;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total amount in losses reported in all calls to #report_emergency().
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount reported_emergency_loss;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Total amount in losses reported in all calls to #report_emergency_by_count().
|
||||||
|
*/
|
||||||
|
static struct TALER_Amount reported_emergency_loss_by_count;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Expected balance in the escrow account.
|
* Expected balance in the escrow account.
|
||||||
@ -419,9 +434,13 @@ report_emergency_by_amount (const struct
|
|||||||
"value",
|
"value",
|
||||||
TALER_JSON_from_amount_nbo (&dki->properties.value)));
|
TALER_JSON_from_amount_nbo (&dki->properties.value)));
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_add (&reported_emergency_sum,
|
TALER_amount_add (&reported_emergency_risk_by_amount,
|
||||||
&reported_emergency_sum,
|
&reported_emergency_risk_by_amount,
|
||||||
risk));
|
risk));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&reported_emergency_loss,
|
||||||
|
&reported_emergency_loss,
|
||||||
|
loss));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -446,6 +465,8 @@ report_emergency_by_count (const struct
|
|||||||
uint64_t num_known,
|
uint64_t num_known,
|
||||||
const struct TALER_Amount *risk)
|
const struct TALER_Amount *risk)
|
||||||
{
|
{
|
||||||
|
struct TALER_Amount denom_value;
|
||||||
|
|
||||||
report (report_emergencies_by_count,
|
report (report_emergencies_by_count,
|
||||||
json_pack ("{s:o, s:I, s:I, s:o, s:o, s:o, s:o}",
|
json_pack ("{s:o, s:I, s:I, s:o, s:o, s:o, s:o}",
|
||||||
"denompub_hash",
|
"denompub_hash",
|
||||||
@ -463,9 +484,17 @@ report_emergency_by_count (const struct
|
|||||||
"value",
|
"value",
|
||||||
TALER_JSON_from_amount_nbo (&dki->properties.value)));
|
TALER_JSON_from_amount_nbo (&dki->properties.value)));
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_add (&reported_emergency_sum,
|
TALER_amount_add (&reported_emergency_risk_by_count,
|
||||||
&reported_emergency_sum,
|
&reported_emergency_risk_by_count,
|
||||||
risk));
|
risk));
|
||||||
|
TALER_amount_ntoh (&denom_value,
|
||||||
|
&dki->properties.value);
|
||||||
|
for (uint64_t i = num_issued; i<num_known; i++)
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_add (&reported_emergency_loss_by_count,
|
||||||
|
&reported_emergency_loss_by_count,
|
||||||
|
&denom_value));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -1650,7 +1679,7 @@ verify_reserve_balance (void *cls,
|
|||||||
ret = GNUNET_SYSERR;
|
ret = GNUNET_SYSERR;
|
||||||
rc->qs = qs;
|
rc->qs = qs;
|
||||||
}
|
}
|
||||||
cleanup:
|
cleanup:
|
||||||
GNUNET_assert (GNUNET_YES ==
|
GNUNET_assert (GNUNET_YES ==
|
||||||
GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
|
GNUNET_CONTAINER_multihashmap_remove (rc->reserves,
|
||||||
key,
|
key,
|
||||||
@ -3859,16 +3888,28 @@ refresh_session_cb (void *cls,
|
|||||||
{
|
{
|
||||||
dso->denom_balance = tmp;
|
dso->denom_balance = tmp;
|
||||||
}
|
}
|
||||||
if (GNUNET_SYSERR ==
|
if (-1 == TALER_amount_cmp (&total_escrow_balance,
|
||||||
TALER_amount_subtract (&total_escrow_balance,
|
amount_with_fee))
|
||||||
&total_escrow_balance,
|
|
||||||
amount_with_fee))
|
|
||||||
{
|
{
|
||||||
/* This should not be possible, unless the AUDITOR
|
/* This can theoretically happen if for example the exchange
|
||||||
has a bug in tracking total balance. */
|
never issued any coins (i.e. escrow balance is zero), but
|
||||||
GNUNET_break (0);
|
accepted a forged coin (i.e. emergency situation after
|
||||||
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
private key compromise). In that case, we cannot even
|
||||||
return GNUNET_SYSERR;
|
subtract the profit we make from the fee from the escrow
|
||||||
|
balance. Tested as part of test-auditor.sh, case #18 */
|
||||||
|
report_amount_arithmetic_inconsistency (
|
||||||
|
"subtracting refresh fee from escrow balance",
|
||||||
|
rowid,
|
||||||
|
&total_escrow_balance,
|
||||||
|
amount_with_fee,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GNUNET_assert (GNUNET_SYSERR !=
|
||||||
|
TALER_amount_subtract (&total_escrow_balance,
|
||||||
|
&total_escrow_balance,
|
||||||
|
amount_with_fee));
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
@ -4030,16 +4071,29 @@ deposit_cb (void *cls,
|
|||||||
{
|
{
|
||||||
ds->denom_balance = tmp;
|
ds->denom_balance = tmp;
|
||||||
}
|
}
|
||||||
if (GNUNET_SYSERR ==
|
|
||||||
TALER_amount_subtract (&total_escrow_balance,
|
if (-1 == TALER_amount_cmp (&total_escrow_balance,
|
||||||
&total_escrow_balance,
|
amount_with_fee))
|
||||||
amount_with_fee))
|
|
||||||
{
|
{
|
||||||
/* This should not be possible, unless the AUDITOR
|
/* This can theoretically happen if for example the exchange
|
||||||
has a bug in tracking total balance. */
|
never issued any coins (i.e. escrow balance is zero), but
|
||||||
GNUNET_break (0);
|
accepted a forged coin (i.e. emergency situation after
|
||||||
cc->qs = GNUNET_DB_STATUS_HARD_ERROR;
|
private key compromise). In that case, we cannot even
|
||||||
return GNUNET_SYSERR;
|
subtract the profit we make from the fee from the escrow
|
||||||
|
balance. Tested as part of test-auditor.sh, case #18 */
|
||||||
|
report_amount_arithmetic_inconsistency (
|
||||||
|
"subtracting deposit fee from escrow balance",
|
||||||
|
rowid,
|
||||||
|
&total_escrow_balance,
|
||||||
|
amount_with_fee,
|
||||||
|
0);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
GNUNET_assert (GNUNET_SYSERR !=
|
||||||
|
TALER_amount_subtract (&total_escrow_balance,
|
||||||
|
&total_escrow_balance,
|
||||||
|
amount_with_fee));
|
||||||
}
|
}
|
||||||
|
|
||||||
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
GNUNET_log (GNUNET_ERROR_TYPE_DEBUG,
|
||||||
@ -5094,7 +5148,16 @@ run (void *cls,
|
|||||||
"Starting audit\n");
|
"Starting audit\n");
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
&reported_emergency_sum));
|
&reported_emergency_loss));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&reported_emergency_risk_by_amount));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&reported_emergency_risk_by_count));
|
||||||
|
GNUNET_assert (GNUNET_OK ==
|
||||||
|
TALER_amount_get_zero (currency,
|
||||||
|
&reported_emergency_loss_by_count));
|
||||||
GNUNET_assert (GNUNET_OK ==
|
GNUNET_assert (GNUNET_OK ==
|
||||||
TALER_amount_get_zero (currency,
|
TALER_amount_get_zero (currency,
|
||||||
&total_escrow_balance));
|
&total_escrow_balance));
|
||||||
@ -5213,7 +5276,8 @@ run (void *cls,
|
|||||||
" s:o, s:o, s:o, s:o, s:o,"
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
" s:o, s:o, s:o, s:o, s:o,"
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
" s:o, s:o, s:o, s:o, s:I,"
|
" s:o, s:o, s:o, s:o, s:I,"
|
||||||
" s:o, s:o, s:o }",
|
" s:o, s:o, s:o, s:o, s:o,"
|
||||||
|
" s:o }",
|
||||||
/* blocks of 5 for easier counting/matching to format string */
|
/* blocks of 5 for easier counting/matching to format string */
|
||||||
/* block */
|
/* block */
|
||||||
"reserve_balance_insufficient_inconsistencies",
|
"reserve_balance_insufficient_inconsistencies",
|
||||||
@ -5248,8 +5312,9 @@ run (void *cls,
|
|||||||
TALER_JSON_from_amount (&income_fee_total),
|
TALER_JSON_from_amount (&income_fee_total),
|
||||||
"emergencies",
|
"emergencies",
|
||||||
report_emergencies,
|
report_emergencies,
|
||||||
"emergencies_risk_total",
|
"emergencies_risk_by_amount",
|
||||||
TALER_JSON_from_amount (&reported_emergency_sum),
|
TALER_JSON_from_amount (
|
||||||
|
&reported_emergency_risk_by_amount),
|
||||||
"reserve_not_closed_inconsistencies",
|
"reserve_not_closed_inconsistencies",
|
||||||
report_reserve_not_closed_inconsistencies,
|
report_reserve_not_closed_inconsistencies,
|
||||||
/* block */
|
/* block */
|
||||||
@ -5309,7 +5374,15 @@ run (void *cls,
|
|||||||
"total_payback_loss",
|
"total_payback_loss",
|
||||||
TALER_JSON_from_amount (&total_payback_loss),
|
TALER_JSON_from_amount (&total_payback_loss),
|
||||||
"emergencies_by_count",
|
"emergencies_by_count",
|
||||||
report_emergencies_by_count
|
report_emergencies_by_count,
|
||||||
|
"emergencies_risk_by_count",
|
||||||
|
TALER_JSON_from_amount (
|
||||||
|
&reported_emergency_risk_by_count),
|
||||||
|
"emergencies_loss",
|
||||||
|
TALER_JSON_from_amount (&reported_emergency_loss),
|
||||||
|
/* block */
|
||||||
|
"emergencies_loss_by_count",
|
||||||
|
TALER_JSON_from_amount (&reported_emergency_loss_by_count)
|
||||||
);
|
);
|
||||||
GNUNET_break (NULL != report);
|
GNUNET_break (NULL != report);
|
||||||
json_dumpf (report,
|
json_dumpf (report,
|
||||||
|
@ -83,7 +83,7 @@ function audit_only () {
|
|||||||
|
|
||||||
# Cleanup to run after the auditor
|
# Cleanup to run after the auditor
|
||||||
function post_audit () {
|
function post_audit () {
|
||||||
kill -9 `jobs -p` >/dev/null 2>/dev/null || true
|
kill -TERM `jobs -p` >/dev/null 2>/dev/null || true
|
||||||
|
|
||||||
echo -n "TeXing ."
|
echo -n "TeXing ."
|
||||||
../../contrib/render.py test-audit.json test-wire-audit.json < ../../contrib/auditor-report.tex.j2 > test-report.tex || exit_fail "Renderer failed"
|
../../contrib/render.py test-audit.json test-wire-audit.json < ../../contrib/auditor-report.tex.j2 > test-report.tex || exit_fail "Renderer failed"
|
||||||
@ -980,6 +980,58 @@ echo "UPDATE app_banktransaction SET date='${OLD_DATE}' WHERE id='${OLD_ID}';" |
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
# Test where we trigger an emergency.
|
||||||
|
function test_18() {
|
||||||
|
echo "===========18: emergency================="
|
||||||
|
|
||||||
|
echo "DELETE FROM reserves_out;" | psql -Aqt $DB
|
||||||
|
|
||||||
|
run_audit
|
||||||
|
|
||||||
|
echo -n "Testing emergency detection... "
|
||||||
|
|
||||||
|
jq -e .reserve_balance_summary_wrong_inconsistencies[0] < test-audit.json > /dev/null || exit_fail "Reserve balance inconsistency not detected"
|
||||||
|
|
||||||
|
jq -e .emergencies[0] < test-audit.json > /dev/null || exit_fail "Emergency not detected"
|
||||||
|
jq -e .emergencies_by_count[0] < test-audit.json > /dev/null || exit_fail "Emergency by count not detected"
|
||||||
|
jq -e .amount_arithmetic_inconsistencies[0] < test-audit.json > /dev/null || exit_fail "Escrow balance calculation impossibility not detected"
|
||||||
|
|
||||||
|
echo PASS
|
||||||
|
|
||||||
|
echo -n "Testing risk/loss calculation... "
|
||||||
|
|
||||||
|
AMOUNT=`jq -r .emergencies_risk_by_amount < test-audit.json`
|
||||||
|
if test "x$AMOUNT" == "xTESTKUDOS:0"
|
||||||
|
then
|
||||||
|
exit_fail "Reported amount wrong: $AMOUNT"
|
||||||
|
fi
|
||||||
|
AMOUNT=`jq -r .emergencies_risk_by_count < test-audit.json`
|
||||||
|
if test "x$AMOUNT" == "xTESTKUDOS:0"
|
||||||
|
then
|
||||||
|
exit_fail "Reported amount wrong: $AMOUNT"
|
||||||
|
fi
|
||||||
|
AMOUNT=`jq -r .emergencies_loss < test-audit.json`
|
||||||
|
if test "x$AMOUNT" == "xTESTKUDOS:0"
|
||||||
|
then
|
||||||
|
exit_fail "Reported amount wrong: $AMOUNT"
|
||||||
|
fi
|
||||||
|
AMOUNT=`jq -r .emergencies_loss_by_count < test-audit.json`
|
||||||
|
if test "x$AMOUNT" == "xTESTKUDOS:0"
|
||||||
|
then
|
||||||
|
exit_fail "Reported amount wrong: $AMOUNT"
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo PASS
|
||||||
|
|
||||||
|
|
||||||
|
# cannot easily undo broad DELETE operation, hence full reload
|
||||||
|
echo -n "Reloading database ..."
|
||||||
|
full_reload
|
||||||
|
echo "DONE"
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# **************************************************
|
# **************************************************
|
||||||
# FIXME: Add more tests here! :-)
|
# FIXME: Add more tests here! :-)
|
||||||
# Specifically:
|
# Specifically:
|
||||||
|
Loading…
Reference in New Issue
Block a user