create(); $user = User::factory()->for($tenant)->create(); $p = Project::factory()->for($tenant)->create(['region_mask' => 1]); $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'update_regions', 'ids' => [$p->id], 'add' => 6, // биты 2+4 = Северо-Западный + Южный 'remove' => 1, // бит 1 = Центральный ]) ->assertOk() ->assertJsonStructure(['updated', 'skipped', 'warnings']); }); it('rejects unknown action', function () { $user = User::factory()->create(); $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'nuke_everything', 'ids' => [1], ]) ->assertStatus(422) ->assertJsonValidationErrors(['action']); }); it('rejects update_limit with both delta and replace', function () { $user = User::factory()->create(); $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'update_limit', 'ids' => [1], 'delta' => 50, 'replace' => 500, ]) ->assertStatus(422); }); it('rejects empty ids without scope', function () { $user = User::factory()->create(); $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'pause', ]) ->assertStatus(422); }); it('accepts empty scope.filter as valid scope (all projects)', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->for($tenant)->create(); $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'pause', 'scope' => ['filter' => []], ]) ->assertOk(); }); it('applies update_regions add and remove correctly', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->for($tenant)->create(); $p1 = Project::factory()->for($tenant)->create(['region_mask' => 3]); // 1+2 $p2 = Project::factory()->for($tenant)->create(['region_mask' => 5]); // 1+4 $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'update_regions', 'ids' => [$p1->id, $p2->id], 'add' => 16, // 16 = Приволжский 'remove' => 1, // 1 = Центральный ]) ->assertOk() ->assertJson(['updated' => 2, 'skipped' => [], 'warnings' => []]); expect($p1->fresh()->region_mask)->toBe((3 | 16) & ~1); // = 18 expect($p2->fresh()->region_mask)->toBe((5 | 16) & ~1); // = 20 }); it('applies update_days add and remove correctly', function () { $tenant = Tenant::factory()->create(); $user = User::factory()->for($tenant)->create(); $p = Project::factory()->for($tenant)->create(['delivery_days_mask' => 31]); // Пн-Пт $this->actingAs($user) ->postJson('/api/projects/bulk', [ 'action' => 'update_days', 'ids' => [$p->id], 'add' => 96, // 32+64 = Сб+Вс 'remove' => 1, // Пн ]) ->assertOk() ->assertJson(['updated' => 1, 'skipped' => [], 'warnings' => []]); expect($p->fresh()->delivery_days_mask)->toBe((31 | 96) & ~1); // = 126 });