Passed
Pull Request — master (#67)
by
unknown
12:51
created

BillingContext::progressivePriceWithInterval()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 32
Code Lines 15

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 15
c 0
b 0
f 0
dl 0
loc 32
rs 9.7666
cc 2
nc 2
nop 10

How to fix   Many Parameters   

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
/**
3
 * PHP Billing Library
4
 *
5
 * @link      https://github.com/hiqdev/php-billing
6
 * @package   php-billing
7
 * @license   BSD-3-Clause
8
 * @copyright Copyright (c) 2017-2020, HiQDev (http://hiqdev.com/)
9
 */
10
11
namespace hiqdev\php\billing\tests\behat\bootstrap;
12
13
use Behat\Gherkin\Node\TableNode;
14
use BehatExpectException\ExpectException;
15
use DateTimeImmutable;
16
use hiqdev\php\billing\bill\BillInterface;
17
use hiqdev\php\billing\charge\ChargeInterface;
18
use PHPUnit\Framework\Assert;
19
20
class BillingContext extends BaseContext
21
{
22
    use ExpectException {
23
        mayFail as protected;
24
        shouldFail as protected;
25
        assertCaughtExceptionMatches as protected;
26
    }
27
28
    protected $saleTime;
29
30
    protected $bill;
31
32
    protected $charges = [];
33
34
    protected array $progressivePrice = [];
35
36
    /**
37
     * @Given reseller :reseller
38
     */
39
    public function reseller($reseller)
40
    {
41
        $this->builder->buildReseller($reseller);
42
    }
43
44
    /**
45
     * @Given customer :customer
46
     */
47
    public function customer($customer)
48
    {
49
        $this->builder->buildCustomer($customer);
50
    }
51
52
    /**
53
     * @Given manager :manager
54
     */
55
    public function manager($manager)
56
    {
57
        $this->builder->buildManager($manager);
0 ignored issues
show
Bug introduced by
The method buildManager() does not exist on hiqdev\php\billing\tests...tstrap\BuilderInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

57
        $this->builder->/** @scrutinizer ignore-call */ 
58
                        buildManager($manager);

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
58
    }
59
60
    /**
61
     * @Given /^(\S+ )?(\S+) tariff plan (\S+)/
62
     */
63
    public function plan($prefix, $type, $plan)
64
    {
65
        $prefix = strtr($prefix, ' ', '_');
66
        $grouping = $prefix === 'grouping_';
67
        $type = $grouping ? $type : $prefix.$type;
68
        $this->builder->buildPlan($plan, $type, $grouping);
69
    }
70
71
    protected function fullPrice(array $data)
72
    {
73
        if (!empty($data['price'])) {
74
            $data['rate'] = $data['price'];
75
        }
76
        $this->builder->buildPrice($data);
77
    }
78
79
    /**
80
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) for target (.+)$/
81
     */
82
    public function priceWithTarget($type, $price, $currency, $unit, $target)
83
    {
84
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...cy', 'unit', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
85
    }
86
87
    /**
88
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+)$/
89
     */
90
    public function priceWithPrepaid($type, $price, $currency, $unit, $prepaid)
91
    {
92
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...y', 'unit', 'prepaid')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
93
    }
94
95
    /**
96
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+) prepaid (\S+) for target (\S+)$/
97
     */
98
    public function priceWithPrepaidAndTarget($type, $price, $currency, $unit, $prepaid, $target)
99
    {
100
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit', 'prepaid', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact..., 'prepaid', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
101
    }
102
103
    /**
104
     * @Given /price for (\S+) is +(\S+) (\S+) per (\S+)$/
105
     */
106
    public function price($type, $price, $currency, $unit)
107
    {
108
        return $this->fullPrice(compact('type', 'price', 'currency', 'unit'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...', 'currency', 'unit')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
109
    }
110
111
    /**
112
     * @Given /price for (\S+) is +(\S+) (\S+) per 1 (\S+) and (\S+) (\S+) per 2 (\S+) for target (\S+)/
113
     */
114
    public function enumPrice($type, $price, $currency, $unit, $price2, $currency2, $unit2, $target)
115
    {
116
        $sums = [1 => $price, 2 => $price2];
117
118
        return $this->fullPrice(compact('type', 'sums', 'currency', 'unit', 'target'));
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->fullPrice(compact...cy', 'unit', 'target')) targeting hiqdev\php\billing\tests...ingContext::fullPrice() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
119
    }
120
121
    /**
122
     * @Given /progressive price for (\S+) is +(\S+) (\S+) per (\S+) (\S+) (\S+) (\S+)$/
123
     */
124
    public function progressivePrice($type, $price, $currency, $unit, $sign, $quantity, $perUnit): void
125
    {
126
        if (empty($this->progressivePrice[$type])) {
127
            $this->progressivePrice[$type] = [
128
                'price' => $price,
129
                'currency' => $currency,
130
                'unit' => $unit,
131
                'condition' =>[
132
                    [
133
                        'sign_till' => $sign,
134
                        'value_till' => $quantity,
135
                    ],
136
                ] ,
137
            ];
138
        } else {
139
            array_push(
140
                $this->progressivePrice[$type]['condition'],
141
                [
142
                    'sign_till' => $sign,
143
                    'value_till' => $quantity,
144
                ]
145
            );
146
        }
147
    }
148
149
    /**
150
     * @Given /progressive price for (\S+) is +(\S+) (\S+) per (\S+) (\S+) (\S+) (\S+) and (\S+) (\S+) (\S+)$/
151
     */
152
    public function progressivePriceWithInterval(
153
        $type,
154
        $price,
155
        $currency,
156
        $unit,
157
        $signFrom,
158
        $quantityFrom,
159
        $perUnit,
160
        $signTill,
161
        $quantityTill,
162
        $perUnit1
163
    ) {
164
        if (empty($this->progressivePrice[$type])) {
165
            $this->progressivePrice[$type] = [
166
                'price' => $price,
167
                'currency' => $currency,
168
                'unit' => $unit,
169
                'condition' =>[
170
                    [
171
                        'sign_till' => $signFrom,
172
                        'value_till' => $quantityFrom,
173
                    ],
174
                ] ,
175
            ];
176
        } else {
177
            array_push(
178
                $this->progressivePrice[$type]['condition'],
179
                [
180
                    'sign_from' => $signFrom,
181
                    'value_from' => $quantityFrom,
182
                    'sign_till' => $signTill,
183
                    'value_till' => $quantityTill,
184
                ]
185
            );
186
        }
187
    }
188
189
    /**
190
     * @Given /^create progressive price/
191
     */
192
    public function createProgressivePrices()
193
    {
194
        foreach ($this->progressivePrice as $type => $price) {
195
            $this->fullPrice([$type, $price['price'], $price['currency'], $price['unit'], $price['condition']]);
196
        }
197
    }
198
199
    /**
200
     * @Given /^remove and recreate tariff plan (\S+)/
201
     */
202
    public function recreatePlan($plan)
203
    {
204
        $this->builder->recreatePlan($plan);
205
    }
206
207
    /**
208
     * @Given /sale target (\S+) by plan (\S+) at (\S+)/
209
     */
210
    public function sale($target, $plan, $time): void
211
    {
212
        $this->saleTime = $this->prepareTime($time);
213
        $this->builder->buildSale($target, $plan, $this->saleTime);
0 ignored issues
show
Bug introduced by
It seems like $this->saleTime can also be of type null; however, parameter $time of hiqdev\php\billing\tests...rInterface::buildSale() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

213
        $this->builder->buildSale($target, $plan, /** @scrutinizer ignore-type */ $this->saleTime);
Loading history...
214
    }
215
    /**
216
     * @When /^sale close is requested for target "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/
217
     */
218
    public function saleClose(string $target, string $time, ?string $wallTime)
219
    {
220
        throw new PendingException();
0 ignored issues
show
Bug introduced by
The type hiqdev\php\billing\tests...tstrap\PendingException was not found. Maybe you did not declare it correctly or list all dependencies?

The issue could also be caused by a filter entry in the build configuration. If the path has been excluded in your configuration, e.g. excluded_paths: ["lib/*"], you can move it to the dependency path list as follows:

filter:
    dependency_paths: ["lib/*"]

For further information see https://scrutinizer-ci.com/docs/tools/php/php-scrutinizer/#list-dependency-paths

Loading history...
221
    }
222
223
    /**
224
     * @Then /^target "([^"]*)" has exactly (\d+) sale for customer$/
225
     */
226
    public function targetHasExactlyNSaleForCustomer(string $target, string $count)
227
    {
228
        // TODO: implement
229
        // $sales = $this->builder->findSales(['target-name' => $target]);
230
231
        Assert::assertCount($count, $sales);
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sales seems to be never defined.
Loading history...
Bug introduced by
$count of type string is incompatible with the type integer expected by parameter $expectedCount of PHPUnit\Framework\Assert::assertCount(). ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

231
        Assert::assertCount(/** @scrutinizer ignore-type */ $count, $sales);
Loading history...
232
    }
233
234
    /**
235
     * @Given /purchase target (\S+) by plan (\S+) at (\S+)$/
236
     */
237
    public function purchaseTarget(string $target, string $plan, string $time): void
238
    {
239
        $time = $this->prepareTime($time);
240
        $this->builder->buildPurchase($target, $plan, $time);
241
    }
242
243
    /**
244
     * @Given /^purchase target "([^"]*)" by plan "([^"]*)" at "([^"]*)" with the following initial uses:$/
245
     */
246
    public function purchaseTargetWithInitialUses(string $target, string $plan, string $time, TableNode $usesTable): void
247
    {
248
        $time = $this->prepareTime($time);
249
        $uses = array_map(static function (array $row) {
250
            return [
251
                'type' => $row['type'],
252
                'unit' => $row['unit'],
253
                'amount' => $row['amount'],
254
            ];
255
        }, $usesTable->getColumnsHash());
256
257
        $this->mayFail(
258
            fn() => $this->builder->buildPurchase($target, $plan, $time, $uses)
259
        );
260
    }
261
262
    /**
263
     * @Given /resource consumption for (\S+) is +(\S+) (\S+) for target (\S+) at (.+)$/
264
     */
265
    public function setConsumption(string $type, int $amount, string $unit, string $target, string $time): void
266
    {
267
        $time = $this->prepareTime($time);
268
        $this->builder->setConsumption($type, $amount, $unit, $target, $time);
0 ignored issues
show
Bug introduced by
The method setConsumption() does not exist on hiqdev\php\billing\tests...tstrap\BuilderInterface. Since it exists in all sub-types, consider adding an abstract or default implementation to hiqdev\php\billing\tests...tstrap\BuilderInterface. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

268
        $this->builder->/** @scrutinizer ignore-call */ 
269
                        setConsumption($type, $amount, $unit, $target, $time);
Loading history...
269
    }
270
271
    /**
272
     * @Given /perform billing at (\S+)/
273
     */
274
    public function performBilling(string $time): void
275
    {
276
        $this->builder->performBilling($this->prepareTime($time));
277
    }
278
279
    /**
280
     * @Given /action for (\S+) is +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/
281
     */
282
    public function setAction(string $type, int $amount, string $unit, string $target, string $at = null, string $time = null): void
283
    {
284
        $time = $this->prepareTime($time);
285
        $this->builder->setAction($type, $amount, $unit, $target, $time);
0 ignored issues
show
Bug introduced by
It seems like $time can also be of type null; however, parameter $time of hiqdev\php\billing\tests...rInterface::setAction() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

285
        $this->builder->setAction($type, $amount, $unit, $target, /** @scrutinizer ignore-type */ $time);
Loading history...
286
    }
287
288
    /**
289
     * @Given /perform calculation( at (\S+))?/
290
     */
291
    public function performCalculation(string $at = null, string $time = null): array
292
    {
293
        $this->charges = $this->builder->performCalculation($this->prepareTime($time));
0 ignored issues
show
Bug introduced by
It seems like $this->prepareTime($time) can also be of type null; however, parameter $time of hiqdev\php\billing\tests...e::performCalculation() does only seem to accept string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

293
        $this->charges = $this->builder->performCalculation(/** @scrutinizer ignore-type */ $this->prepareTime($time));
Loading history...
294
        return $this->charges;
295
    }
296
297
    /**
298
     * @Given /bill +for (\S+) is +(\S+) (\S+) per (\S+) (\S+) for target (.+?)( +at (.+))?$/
299
     */
300
    public function billWithTime($type, $sum, $currency, $quantity, $unit, $target, $at = null, $time = null)
301
    {
302
        $this->builder->flushEntitiesCacheByType('bill');
303
304
        $quantity = $this->prepareQuantity($quantity);
305
        $sum = $this->prepareSum($sum, $quantity);
306
        $time = $this->prepareTime($time);
307
        $bill = $this->findBill([
308
            'type' => $type,
309
            'target' => $target,
310
            'sum' => "$sum $currency",
311
            'quantity' => "$quantity $unit",
312
            'time' => $time,
313
        ]);
314
        Assert::assertSame($type, $bill->getType()->getName());
315
        Assert::assertSame($target, $bill->getTarget()->getFullName());
316
        Assert::assertEquals(bcmul($sum, 100), $bill->getSum()->getAmount());
317
        Assert::assertSame($currency, $bill->getSum()->getCurrency()->getCode());
318
        Assert::assertEquals((float)$quantity, (float)$bill->getQuantity()->getQuantity());
319
        Assert::assertEquals(strtolower($unit), strtolower($bill->getQuantity()->getUnit()->getName()));
320
        if ($time) {
321
            Assert::assertEquals(new DateTimeImmutable($time), $bill->getTime());
322
        }
323
    }
324
325
    public function findBill(array $params): BillInterface
326
    {
327
        $bills = $this->builder->findBills($params);
328
        $this->bill = reset($bills);
329
        $this->charges = $this->bill->getCharges();
330
331
        return $this->bill;
332
    }
333
334
    /**
335
     * @Given /bills number is (\d+) for (\S+) for target (.+?)( +at (\S+))?$/
336
     */
337
    public function billsNumberWithTime($number, $type, $target, $at = null, $time = null)
338
    {
339
        $count = count($this->builder->findBills(array_filter([
340
            'type' => $type,
341
            'target' => $target,
342
            'time' => $this->prepareTime($time),
343
        ])));
344
345
        Assert::assertEquals($number, $count);
346
    }
347
348
    /**
349
     * @Given /charges number is (\d+)/
350
     */
351
    public function chargesNumber($number)
352
    {
353
        Assert::assertEquals($number, count($this->charges));
354
    }
355
356
    /**
357
     * @Given /charge for (\S+) is +(\S+) (\S+) per (\S+) (\S+)$/
358
     */
359
    public function charge($type, $amount, $currency, $quantity, $unit)
360
    {
361
        $this->chargeWithTarget($type, $amount, $currency, $quantity, $unit, null);
362
    }
363
364
    /**
365
     * @Given /charge for (\S+) is +(\S+) (\S+) per +(\S+) (\S+) +for target (.+?)( +at (\S+))?$/
366
     */
367
    public function chargeWithTarget($type, $amount, $currency, $quantity, $unit, $target, $at = null, $time = null)
368
    {
369
        $quantity = $this->prepareQuantity($quantity);
370
        $amount = $this->prepareSum($amount, $quantity);
371
        $charge = $this->findCharge($type, $target);
372
        Assert::assertNotNull($charge);
373
        Assert::assertSame($type, $charge->getType()->getName());
374
        Assert::assertSame($target, $charge->getTarget()->getFullName());
375
        Assert::assertEquals(bcmul($amount, 100), (int)$charge->getSum()->getAmount());
376
        Assert::assertSame($currency, $charge->getSum()->getCurrency()->getCode());
377
        Assert::assertEquals((float)$quantity, (float)$charge->getUsage()->getQuantity());
378
        Assert::assertEquals(strtolower($unit), strtolower($charge->getUsage()->getUnit()->getName()));
379
    }
380
381
    public function findCharge($type, $target): ?ChargeInterface
382
    {
383
        foreach ($this->charges as $charge) {
384
            if ($charge->getType()->getName() !== $type) {
385
                continue;
386
            }
387
            if ($charge->getTarget()->getFullName() !== $target) {
388
                continue;
389
            }
390
391
            return $charge;
392
        }
393
394
        return null;
395
    }
396
397
    public function getNextCharge(): ChargeInterface
398
    {
399
        $charge = current($this->charges);
400
        next($this->charges);
401
402
        return $charge;
403
    }
404
405
    /**
406
     * @return string|false|null
407
     */
408
    protected function prepareTime(string $time = null)
409
    {
410
        if ($time === null) {
411
            return null;
412
        }
413
414
        if ($time === 'midnight second day of this month') {
415
            return date('Y-m-02');
416
        }
417
        if (strncmp($time, 'pY', 1) === 0) {
418
            return date(substr($time, 1), strtotime('-1 year'));
419
        }
420
        if (strncmp($time, 'Y', 1) === 0) {
421
            return date($time);
422
        }
423
424
        return $time;
425
    }
426
427
    private function prepareQuantity($quantity)
428
    {
429
        if ($quantity[0] === 's') {
430
            return $this->getSaleQuantity();
431
        }
432
433
        return $quantity;
434
    }
435
436
    private function prepareSum($sum, $quantity)
437
    {
438
        if ($sum[0] === 's') {
439
            $sum = round(substr($sum, 1) * $quantity*100)/100;
440
        }
441
442
        return $sum;
443
    }
444
445
    public function getSaleQuantity()
446
    {
447
        return $this->days2quantity(new DateTimeImmutable($this->saleTime));
448
    }
449
450
    private function days2quantity(DateTimeImmutable $from)
451
    {
452
        $till = new DateTimeImmutable('first day of next month midnight');
453
        $diff = $from->diff($till);
454
        if ($diff->m) {
455
            return 1;
456
        }
457
458
        return $diff->d/date('t');
459
    }
460
461
    /**
462
     * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)"$/
463
     */
464
    public function tariffPlanChangeIsRequestedForTarget(string $target, string $planName, string $date)
465
    {
466
        $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date)));
467
    }
468
469
    /**
470
     * @When /^tariff plan change is requested for target "([^"]*)" to plan "([^"]*)" at "([^"]*)", assuming current time is "([^"]*)"$/
471
     */
472
    public function tariffPlanChangeIsRequestedForTargetAtSpecificTime(string $target, string $planName, string $date, ?string $wallTime = null)
473
    {
474
        $this->mayFail(fn () => $this->builder->targetChangePlan($target, $planName, $this->prepareTime($date), $this->prepareTime($wallTime)));
475
    }
476
477
    /**
478
     * @Then /^target "([^"]*)" is sold to customer by plan "([^"]*)" since "([^"]*)"(?: till "([^"]*)")?$/
479
     */
480
    public function targetIsSoldToCustomerByPlanSinceTill(string $target, string $planName, string $saleDate, ?string $saleCloseDate = null)
481
    {
482
        $sales = $this->builder->findHistoricalSales([
483
            'target' => $target,
484
        ]);
485
486
        $saleDateTime = new DateTimeImmutable('@' . strtotime($saleDate));
487
        $saleCloseDateTime = new DateTimeImmutable('@' . ($saleCloseDate ? strtotime($saleCloseDate) : time()));
488
489
        foreach ($sales as $sale) {
490
            /** @noinspection PhpBooleanCanBeSimplifiedInspection */
491
            $saleExists = true
492
                && str_contains($sale->getPlan()->getName(), $planName)
493
                && $sale->getTime()->format(DATE_ATOM) === $saleDateTime->format(DATE_ATOM)
494
                && (
495
                    ($saleCloseDate === null && $sale->getCloseTime() === null)
496
                    ||
497
                    ($saleCloseDate !== null && $sale->getCloseTime()->format(DATE_ATOM) === $saleCloseDateTime->format(DATE_ATOM))
498
                );
499
500
            if ($saleExists) {
501
                return;
502
            }
503
        }
504
505
        Assert::fail('Requested sale does not exist');
506
    }
507
508
    /**
509
     * @Then /^target "([^"]*)" has exactly (\d+) sales for customer$/
510
     */
511
    public function targetHasExactlySalesForCustomer(string $target, int $count)
512
    {
513
        $sales = $this->builder->findHistoricalSales([
514
            'target' => $target,
515
        ]);
516
517
        Assert::assertCount($count, $sales);
518
    }
519
520
    /**
521
     * @Then /^caught error is "([^"]*)"$/
522
     */
523
    public function caughtErrorIs(string $errorMessage): void
524
    {
525
        $this->assertCaughtExceptionMatches(\Throwable::class, $errorMessage);
526
    }
527
528
    /**
529
     * @Given /^target "([^"]*)"$/
530
     */
531
    public function target(string $target)
532
    {
533
        $this->builder->buildTarget($target);
534
    }
535
536
    /**
537
     * @Then /^flush entities cache$/
538
     */
539
    public function flushEntitiesCache()
540
    {
541
        $this->builder->flushEntitiesCache();
542
    }
543
544
    /**
545
     * @Given /^target "([^"]*)" has the following uses:$/
546
     */
547
    public function targetHasTheFollowingUses(string $target, TableNode $usesTable)
548
    {
549
        foreach ($usesTable->getColumnsHash() as $row) {
550
            $uses = $this->builder->findUsage($row['time'], $target, $row['type']);
551
            Assert::assertCount(1, $uses);
552
553
            $use = reset($uses);
554
            Assert::assertSame(
555
                $row['unit'], $use['unit'],
556
                sprintf('Exptected unit to be %s, got %s instead', $row['unit'], $use['unit'])
557
            );
558
            Assert::assertEquals(
559
                $row['amount'], $use['total'],
560
                sprintf('Exptected total to be %s, got %s instead', $row['amount'], $use['total'])
561
            );
562
        }
563
    }
564
}
565