| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129 |
- <?php
- /** php tests/water_package_delete_test.php:所有测试数据在事务中回滚。 */
- require dirname(__DIR__) . '/vendor/autoload.php';
- \Dotenv\Dotenv::createUnsafeMutable(dirname(__DIR__))->load();
- \support\App::loadAllConfig(['route']);
- date_default_timezone_set('Asia/Shanghai');
- use app\miniapi\service\Catalog;
- use plugin\saiadmin\app\controller\water\WaterController;
- use plugin\saiadmin\app\model\water\WaterAccount;
- use plugin\saiadmin\app\model\water\WaterLedger;
- use plugin\saiadmin\app\model\water\WaterOperator;
- use plugin\saiadmin\app\model\water\WaterOrder;
- use plugin\saiadmin\app\model\water\WaterPackage;
- use plugin\saiadmin\app\model\water\WaterSku;
- use plugin\saiadmin\app\service\WaterBusiness;
- use plugin\saiadmin\exception\ApiException;
- use support\Request;
- $checks = 0;
- function check(bool $condition, string $message): void
- {
- global $checks;
- ++$checks;
- if (!$condition) {
- throw new RuntimeException($message);
- }
- }
- function rejects(callable $callback, string $message): void
- {
- try {
- $callback();
- } catch (ApiException $exception) {
- check(true, $message);
- return;
- }
- throw new RuntimeException($message);
- }
- function packageRequest(array $input): Request
- {
- $body = json_encode($input, JSON_THROW_ON_ERROR);
- return new Request("POST /core/water/packages HTTP/1.1\r\nHost: localhost\r\nContent-Type: application/json\r\nContent-Length: " . strlen($body) . "\r\n\r\n" . $body);
- }
- $service = new WaterBusiness();
- $tag = 'PKG-DELETE-' . bin2hex(random_bytes(4));
- $connection = (new WaterPackage())->getConnection();
- $before = WaterPackage::query()->orderBy('id')->get()->toArray();
- $beforeLedger = WaterLedger::query()->count();
- $beforeOrders = WaterOrder::query()->count();
- $connection->beginTransaction();
- try {
- $enterprise = $service->save('enterprise', ['name' => $tag, 'contact' => '测试', 'phone' => 'TEST', 'address' => '事务回滚地址'], 1)['id'];
- $sku = $service->save('sku', ['name' => $tag, 'liters' => 18.9], 1)['id'];
- $packageInput = ['sku_id' => $sku, 'buckets' => 30, 'price_cents' => 69900];
- $id = $service->save('package', ['name' => $tag . '-delete'] + $packageInput, 1)['id'];
- $keepId = $service->save('package', ['name' => $tag . '-keep'] + $packageInput, 1)['id'];
- $orderId = $service->purchase(['enterprise_id' => $enterprise, 'package_id' => $id, 'reference' => $tag], 1)['id'];
- $order = WaterOrder::query()->findOrFail($orderId)->toArray();
- $account = WaterAccount::query()->where('enterprise_id', $enterprise)->firstOrFail()->toArray();
- $stock = WaterSku::query()->findOrFail($sku)->toArray();
- $otherPackage = WaterPackage::query()->findOrFail($keepId)->toArray();
- $auditCount = WaterLedger::query()->count();
- $reflection = new ReflectionClass(WaterController::class);
- $controller = $reflection->newInstanceWithoutConstructor();
- $adminId = $reflection->getProperty('adminId');
- $adminId->setValue($controller, 0);
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $id]), 'packages'), 'unauthenticated deletion rejected');
- $operator = WaterOperator::query()->insertGetId(['username' => $tag, 'password' => password_hash($tag, PASSWORD_DEFAULT), 'status' => 1]);
- $adminId->setValue($controller, (int) $operator);
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $id]), 'packages'), 'operator without write permission rejected');
- $adminId->setValue($controller, 1);
- foreach ([null, 0, -1, 'invalid', 1.5, []] as $invalidId) {
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $invalidId]), 'packages'), 'invalid package ID rejected');
- }
- $missingId = (int) WaterPackage::query()->max('id') + 100;
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $missingId]), 'packages'), 'missing package rejected');
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $enterprise]), 'enterprises'), 'delete action limited to packages');
- check(WaterPackage::query()->where('id', $id)->exists(), 'rejected requests leave package intact');
- check(WaterLedger::query()->count() === $auditCount, 'rejected requests create no audits');
- $response = json_decode($controller->save(packageRequest(['action' => 'delete', 'id' => (string) $id]), 'packages')->rawBody(), true, 512, JSON_THROW_ON_ERROR);
- check($response['code'] === 200 && $response['data']['id'] === $id, 'JSON delete returns selected ID');
- check(!WaterPackage::query()->where('id', $id)->exists(), 'selected package physically deleted');
- check(WaterPackage::query()->findOrFail($keepId)->toArray() === $otherPackage, 'other package unchanged');
- check(WaterOrder::query()->findOrFail($orderId)->toArray() === $order, 'historical order snapshot retained');
- check(WaterAccount::query()->findOrFail($account['id'])->toArray() === $account, 'enterprise entitlement retained');
- check(WaterSku::query()->findOrFail($sku)->toArray() === $stock, 'inventory unchanged');
- $listing = $service->listing('packages', ['keyword' => $tag]);
- check($listing['total'] === 1 && array_column($listing['data'], 'id') === [$keepId], 'admin listing excludes deleted package');
- $optionsRequest = new Request('GET /core/water/packages/options?type=package&keyword=' . $tag . " HTTP/1.1\r\nHost: localhost\r\n\r\n");
- $options = json_decode($controller->options($optionsRequest, 'packages')->rawBody(), true, 512, JSON_THROW_ON_ERROR);
- check(array_column($options['data'], 'id') === [$keepId], 'package selector excludes deleted package');
- check(array_column((new Catalog())->listing(['keyword' => $tag])['items'], 'id') === [$keepId], 'mini-program catalog excludes deleted package');
- $audit = WaterLedger::query()->where(['kind' => 'archive', 'reference' => 'package:' . $id])->orderByDesc('id')->firstOrFail();
- check($audit->note === '删除套餐:' . $tag . '-delete' && (int) $audit->created_by === 1, 'deletion audit records actor and package');
- check(WaterLedger::query()->count() === $auditCount + 1, 'one deletion audit added');
- rejects(fn () => $controller->save(packageRequest(['action' => 'delete', 'id' => $id]), 'packages'), 'repeated deletion reports missing package');
- check(WaterLedger::query()->count() === $auditCount + 1, 'repeated deletion creates no extra audit');
- } finally {
- $connection->rollBack();
- }
- check(WaterPackage::query()->orderBy('id')->get()->toArray() === $before, 'original package data restored after rollback');
- check(WaterLedger::query()->count() === $beforeLedger && WaterOrder::query()->count() === $beforeOrders, 'audit and order fixtures rolled back');
- check(!WaterOperator::query()->where('username', $tag)->exists() && !WaterSku::query()->where('name', $tag)->exists(), 'operator and inventory fixtures rolled back');
- if (in_array('--http', $argv, true)) {
- $token = \Tinywan\Jwt\JwtToken::generateToken(['id' => 1, 'username' => 'admin', 'plat' => 'saiadmin', 'type' => 'web', 'client' => $tag, 'access_exp' => 120])['access_token'];
- $curl = curl_init('http://127.0.0.1:8787/core/water/packages');
- curl_setopt_array($curl, [
- CURLOPT_POST => true,
- CURLOPT_POSTFIELDS => json_encode(['action' => 'delete', 'id' => 0], JSON_THROW_ON_ERROR),
- CURLOPT_RETURNTRANSFER => true,
- CURLOPT_TIMEOUT => 15,
- CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token],
- ]);
- $body = curl_exec($curl);
- $status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
- curl_close($curl);
- $response = json_decode((string) $body, true, 512, JSON_THROW_ON_ERROR);
- check($status === 200 && ($response['message'] ?? '') === '套餐ID必须是有效整数', 'live HTTP worker has loaded delete action; invalid ID changes no data');
- unset($token);
- }
- echo "PASS: $checks package deletion checks; all fixtures rolled back.\n";
|