fix: stabilize tests and spec plan artifacts
This commit is contained in:
parent
629bbef6e9
commit
70fd5d2e68
3
.github/agents/copilot-instructions.md
vendored
3
.github/agents/copilot-instructions.md
vendored
@ -3,6 +3,8 @@ # TenantAtlas Development Guidelines
|
||||
Auto-generated from all feature plans. Last updated: 2025-12-22
|
||||
|
||||
## Active Technologies
|
||||
- PHP 8.4.15 + Laravel 12, Filament v4, Livewire v3 (feat/005-bulk-operations)
|
||||
- PostgreSQL (app), SQLite in-memory (tests) (feat/005-bulk-operations)
|
||||
|
||||
- PHP 8.4.15 (feat/005-bulk-operations)
|
||||
|
||||
@ -22,6 +24,7 @@ ## Code Style
|
||||
PHP 8.4.15: Follow standard conventions
|
||||
|
||||
## Recent Changes
|
||||
- feat/005-bulk-operations: Added PHP 8.4.15 + Laravel 12, Filament v4, Livewire v3
|
||||
|
||||
- feat/005-bulk-operations: Added PHP 8.4.15
|
||||
|
||||
|
||||
12
specs/005-bulk-operations/contracts/openapi.yaml
Normal file
12
specs/005-bulk-operations/contracts/openapi.yaml
Normal file
@ -0,0 +1,12 @@
|
||||
openapi: 3.0.3
|
||||
info:
|
||||
title: TenantPilot - Bulk Operations (Feature 005)
|
||||
version: 0.0.0
|
||||
description: |
|
||||
This feature is implemented via Filament/Livewire actions inside the admin panel.
|
||||
No public, stable HTTP API endpoints are introduced specifically for bulk operations.
|
||||
|
||||
This OpenAPI document is intentionally minimal.
|
||||
servers: []
|
||||
paths: {}
|
||||
components: {}
|
||||
@ -1,82 +1,38 @@
|
||||
# Implementation Plan: Feature 005 - Bulk Operations
|
||||
|
||||
**Branch**: `feat/005-bulk-operations` | **Date**: 2025-12-22 | **Spec**: [spec.md](./spec.md)
|
||||
**Branch**: `feat/005-bulk-operations` | **Date**: 2025-12-25 | **Spec**: [spec.md](./spec.md)
|
||||
**Input**: Feature specification from `/specs/005-bulk-operations/spec.md`
|
||||
|
||||
**Note**: This template is filled in by the `/speckit.plan` command. See `.specify/templates/commands/plan.md` for the execution workflow.
|
||||
|
||||
## Summary
|
||||
|
||||
Enable efficient bulk operations (delete, export, prune) across TenantPilot's main resources (Policies, Policy Versions, Backup Sets, Restore Runs) with safety gates, progress tracking, and comprehensive audit logging. Technical approach: Filament bulk actions + Laravel Queue jobs with chunked processing + BulkOperationRun tracking model + Livewire polling for progress updates.
|
||||
Add consistent bulk actions (delete/export/restore/prune/sync where applicable) across TenantPilot's primary admin resources (Policies, Policy Versions, Backup Sets, Restore Runs). Bulk operations create a tracking record, enforce permissions, support type-to-confirm for large destructive changes, and run asynchronously via queue for larger selections with progress tracking.
|
||||
|
||||
## Technical Context
|
||||
|
||||
|
||||
**Language/Version**: PHP 8.4.15
|
||||
**Framework**: Laravel 12
|
||||
**Primary Dependencies**:
|
||||
- Filament v4 (admin panel + bulk actions)
|
||||
- Livewire v3 (reactive UI + polling)
|
||||
- Laravel Queue (async job processing)
|
||||
- PostgreSQL (JSONB for tracking)
|
||||
|
||||
**Storage**: PostgreSQL with JSONB fields for:
|
||||
- `bulk_operation_runs.item_ids` (array of resource IDs)
|
||||
- `bulk_operation_runs.failures` (per-item error details)
|
||||
- Existing audit logs (metadata column)
|
||||
|
||||
**Testing**: Pest v4 (unit, feature, browser tests)
|
||||
**Target Platform**: Web (Dokploy deployment)
|
||||
**Project Type**: Web application (Filament admin panel)
|
||||
|
||||
**Performance Goals**:
|
||||
- Process 100 items in <2 minutes (queued)
|
||||
- Handle up to 500 items per operation without timeout
|
||||
- Progress notifications update every 5-10 seconds
|
||||
|
||||
**Constraints**:
|
||||
- Queue jobs MUST process in chunks of 10-20 items (memory efficiency)
|
||||
- Progress tracking requires explicit polling (not automatic in Filament)
|
||||
- Type-to-confirm required for ≥20 destructive items
|
||||
- Tenant isolation enforced at job level
|
||||
|
||||
**Scale/Scope**:
|
||||
- 4 primary resources (Policies, PolicyVersions, BackupSets, RestoreRuns)
|
||||
- 8-12 bulk actions (P1/P2 priority)
|
||||
- Estimated 26-34 hours implementation (3 phases for P1/P2)
|
||||
**Primary Dependencies**: Laravel 12, Filament v4, Livewire v3
|
||||
**Storage**: PostgreSQL (app), SQLite in-memory (tests)
|
||||
**Testing**: Pest v4 + PHPUnit 12
|
||||
**Target Platform**: Containerized Linux (Sail/Dokploy)
|
||||
**Project Type**: Web application (Laravel + Filament admin panel)
|
||||
**Performance Goals**: Handle bulk actions up to hundreds of items with predictable runtime; keep UI responsive via queued processing for larger selections
|
||||
**Constraints**: Tenant isolation; least privilege; safe destructive actions (confirmation + auditability); avoid long locks/timeouts by chunking
|
||||
**Scale/Scope**: Admin-focused operations, moderate concurrency, emphasis on correctness/auditability over throughput
|
||||
|
||||
## Constitution Check
|
||||
|
||||
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
|
||||
|
||||
**Note**: Project constitution is template-only (not populated). Using Laravel/TenantPilot conventions instead.
|
||||
The constitution file at `.specify/memory/constitution.md` is a placeholder template (no concrete principles/gates are defined). For this feature, the effective gates follow repository agent guidelines in `Agents.md`:
|
||||
|
||||
### Architecture Principles
|
||||
- Spec artifacts exist and are consistent: PASS (`spec.md`, `plan.md`, `tasks.md`, `research.md`, `data-model.md`, `quickstart.md`)
|
||||
- Tests cover changes: PASS (Pest suite; full test run exits 0)
|
||||
- Safe admin operations: PASS (explicit confirmations, type-to-confirm for large destructive ops, audit logging)
|
||||
|
||||
✅ **Library-First**: N/A (feature extends existing app, no new libraries)
|
||||
✅ **Test-First**: TDD enforced - Pest tests required before implementation
|
||||
✅ **Simplicity**: Uses existing patterns (Jobs, Filament bulk actions, Livewire polling)
|
||||
✅ **Sail-First**: Local development uses Laravel Sail (Docker)
|
||||
✅ **Dokploy Deployment**: Production/staging via Dokploy (VPS containers)
|
||||
|
||||
### Laravel Conventions
|
||||
|
||||
✅ **PSR-12**: Code formatting enforced via Laravel Pint
|
||||
✅ **Eloquent-First**: No raw DB queries, use Model::query() patterns
|
||||
✅ **Permission Gates**: Leverage existing RBAC (Feature 001)
|
||||
✅ **Queue Jobs**: Use ShouldQueue interface, chunked processing
|
||||
✅ **Audit Logging**: Extend existing AuditLog model/service
|
||||
|
||||
### Safety Requirements
|
||||
|
||||
✅ **Tenant Isolation**: Job constructor accepts explicit `tenantId`
|
||||
✅ **Audit Trail**: One audit log entry per bulk operation + per-item outcomes
|
||||
✅ **Confirmation**: Type-to-confirm for ≥20 destructive items
|
||||
✅ **Fail-Soft**: Continue processing on individual failures, abort if >50% fail
|
||||
✅ **Immutability**: Policy Versions check eligibility before prune (referenced, current, age)
|
||||
|
||||
### Gates
|
||||
|
||||
🔒 **GATE-01**: Bulk operations MUST use existing permission model (policies.delete, etc.)
|
||||
🔒 **GATE-02**: Progress tracking MUST use BulkOperationRun model (not fire-and-forget)
|
||||
🔒 **GATE-03**: Type-to-confirm MUST be case-sensitive "DELETE" for ≥20 items
|
||||
🔒 **GATE-04**: Policies bulk delete = local only (ignored_at flag, NO Graph DELETE)
|
||||
Re-check after Phase 1: PASS (no new unknowns introduced).
|
||||
|
||||
## Project Structure
|
||||
|
||||
@ -84,180 +40,42 @@ ### Documentation (this feature)
|
||||
|
||||
```text
|
||||
specs/005-bulk-operations/
|
||||
├── plan.md # This file
|
||||
├── research.md # Phase 0 output (see below)
|
||||
├── data-model.md # Phase 1 output (see below)
|
||||
├── quickstart.md # Phase 1 output (see below)
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command - generated and tracked here)
|
||||
├── plan.md # This file (/speckit.plan command output)
|
||||
├── research.md # Phase 0 output (/speckit.plan command)
|
||||
├── data-model.md # Phase 1 output (/speckit.plan command)
|
||||
├── quickstart.md # Phase 1 output (/speckit.plan command)
|
||||
├── contracts/ # Phase 1 output (/speckit.plan command)
|
||||
└── tasks.md # Phase 2 output (/speckit.tasks command - NOT created by /speckit.plan)
|
||||
```
|
||||
|
||||
### Source Code (repository root)
|
||||
|
||||
```text
|
||||
app/
|
||||
├── Models/
|
||||
│ ├── BulkOperationRun.php # NEW: Tracks progress/outcomes
|
||||
│ ├── Policy.php # EXTEND: Add markIgnored() scope
|
||||
│ ├── PolicyVersion.php # EXTEND: Add pruneEligible() scope
|
||||
│ ├── BackupSet.php # EXTEND: Cascade delete logic
|
||||
│ └── RestoreRun.php # EXTEND: Skip running status
|
||||
│
|
||||
├── Jobs/
|
||||
│ ├── BulkPolicyDeleteJob.php # NEW: Async bulk delete (local)
|
||||
│ ├── BulkPolicyExportJob.php # NEW: Export to backup set
|
||||
│ ├── BulkPolicyVersionPruneJob.php # NEW: Prune old versions
|
||||
│ ├── BulkBackupSetDeleteJob.php # NEW: Delete backup sets
|
||||
│ └── BulkRestoreRunDeleteJob.php # NEW: Delete restore runs
|
||||
│
|
||||
├── Services/
|
||||
│ ├── BulkOperationService.php # NEW: Orchestrates bulk ops + tracking
|
||||
│ └── Audit/
|
||||
│ └── AuditLogger.php # EXTEND: Add bulk operation events
|
||||
│
|
||||
├── Filament/
|
||||
│ └── Resources/
|
||||
│ ├── PolicyResource.php # EXTEND: Add bulk actions
|
||||
│ ├── PolicyVersionResource.php # EXTEND: Add bulk prune
|
||||
│ ├── BackupSetResource.php # EXTEND: Add bulk delete
|
||||
│ └── RestoreRunResource.php # EXTEND: Add bulk delete
|
||||
│
|
||||
└── Livewire/
|
||||
└── BulkOperationProgress.php # NEW: Progress polling component
|
||||
├── Jobs/
|
||||
├── Models/
|
||||
└── Services/
|
||||
|
||||
database/
|
||||
├── factories/
|
||||
└── migrations/
|
||||
└── YYYY_MM_DD_create_bulk_operation_runs_table.php # NEW
|
||||
|
||||
routes/
|
||||
├── web.php
|
||||
└── console.php
|
||||
|
||||
resources/
|
||||
└── views/
|
||||
|
||||
tests/
|
||||
├── Unit/
|
||||
│ ├── BulkPolicyDeleteJobTest.php
|
||||
│ ├── BulkActionPermissionTest.php
|
||||
│ └── BulkEligibilityCheckTest.php
|
||||
│
|
||||
└── Feature/
|
||||
├── BulkDeletePoliciesTest.php
|
||||
├── BulkExportToBackupTest.php
|
||||
├── BulkProgressNotificationTest.php
|
||||
└── BulkTypeToConfirmTest.php
|
||||
├── Feature/
|
||||
└── Unit/
|
||||
```
|
||||
|
||||
**Structure Decision**: Single web application structure (Laravel + Filament). New bulk operations extend existing Resources with BulkAction definitions. New BulkOperationRun model tracks async job progress. No separate API layer needed (Livewire polling uses Filament infolists/resource pages).
|
||||
**Structure Decision**: Web application (Laravel + Filament admin panel) using existing repository layout.
|
||||
|
||||
## Complexity Tracking
|
||||
|
||||
> No constitution violations requiring justification.
|
||||
|
||||
---
|
||||
|
||||
## Phase 0: Research & Technology Decisions
|
||||
|
||||
See [research.md](./research.md) for detailed research findings.
|
||||
|
||||
### Key Decisions Summary
|
||||
|
||||
| Decision | Chosen | Rationale |
|
||||
|----------|--------|-----------|
|
||||
| Progress tracking | BulkOperationRun model + Livewire polling | Explicit state, survives page refresh, queryable outcomes |
|
||||
| Job chunking | collect()->chunk(10) | Simple, memory-efficient, easy to test |
|
||||
| Type-to-confirm | Filament form + validation rule | Built-in UI, reusable pattern |
|
||||
| Tenant isolation | Explicit tenantId param | Fail-safe, auditable, no reliance on global scopes |
|
||||
| Policy deletion | ignored_at flag | Prevents re-sync, restorable, doesn't touch Intune |
|
||||
| Eligibility checks | Eloquent scopes | Reusable, testable, composable |
|
||||
|
||||
---
|
||||
|
||||
## Phase 1: Data Model & Contracts
|
||||
|
||||
See [data-model.md](./data-model.md) for detailed schemas and entity diagrams.
|
||||
|
||||
### Core Entities
|
||||
|
||||
**BulkOperationRun** (NEW):
|
||||
- Tracks progress, outcomes, failures for bulk operations
|
||||
- Fields: resource, action, status, total_items, processed_items, succeeded, failed, skipped
|
||||
- JSONB: item_ids, failures
|
||||
- Relationships: tenant, user, auditLog
|
||||
|
||||
**Policy** (EXTEND):
|
||||
- Add `ignored_at` timestamp (prevents re-sync)
|
||||
- Add `markIgnored()` method and `notIgnored()` scope
|
||||
|
||||
**PolicyVersion** (EXTEND):
|
||||
- Add `pruneEligible()` scope (checks age, references, current status)
|
||||
|
||||
**RestoreRun** (EXTEND):
|
||||
- Add `deletable()` scope (filters by completed/failed status)
|
||||
|
||||
---
|
||||
|
||||
## Phase 2: Implementation Tasks
|
||||
|
||||
Detailed tasks will be generated via `/speckit.tasks` command. High-level phases:
|
||||
|
||||
### Phase 2.1: Foundation (P1 - Policies) - 8-12 hours
|
||||
- BulkOperationRun migration + model
|
||||
- Policies: ignored_at column, bulk delete/export jobs
|
||||
- Filament bulk actions + type-to-confirm
|
||||
- BulkOperationService orchestration
|
||||
- Tests (unit, feature)
|
||||
|
||||
### Phase 2.2: Progress Tracking (P1) - 8-10 hours
|
||||
- Livewire progress component
|
||||
- Job progress updates (chunked)
|
||||
- Circuit breaker (>50% fail abort)
|
||||
- Audit logging integration
|
||||
- Tests (progress, polling, audit)
|
||||
|
||||
### Phase 2.3: Additional Resources (P2) - 6-8 hours
|
||||
- PolicyVersion prune (eligibility scope)
|
||||
- BackupSet bulk delete
|
||||
- RestoreRun bulk delete
|
||||
- Resource extensions
|
||||
- Tests for each resource
|
||||
|
||||
### Phase 2.4: Polish & Deployment - 4-6 hours
|
||||
- Manual QA (type-to-confirm, progress UI)
|
||||
- Load testing (500 items)
|
||||
- Documentation updates
|
||||
- Staging → Production deployment
|
||||
|
||||
---
|
||||
|
||||
## Risk Mitigation
|
||||
|
||||
| Risk | Mitigation |
|
||||
|------|------------|
|
||||
| Queue timeouts | Chunk processing (10-20 items), timeout config (300s), circuit breaker |
|
||||
| Progress polling overhead | Limit interval (5s), index queries, cache recent runs |
|
||||
| Accidental deletes | Type-to-confirm ≥20 items, `ignored_at` flag (restorable), audit trail |
|
||||
| Job crashes | Fail-soft, BulkOperationRun status tracking, Laravel retry |
|
||||
| Eligibility misses | Conservative JSONB queries, manual review before hard delete |
|
||||
| Sync re-adds policies | `ignored_at` filter in SyncPoliciesJob |
|
||||
|
||||
---
|
||||
|
||||
## Success Criteria
|
||||
|
||||
- ✅ Bulk delete 100 policies in <2 minutes
|
||||
- ✅ Type-to-confirm prevents accidents (≥20 items)
|
||||
- ✅ Progress updates every 5-10s
|
||||
- ✅ Audit log captures per-item outcomes
|
||||
- ✅ 95%+ operation success rate
|
||||
- ✅ All P1/P2 tests pass
|
||||
|
||||
---
|
||||
|
||||
## Next Steps
|
||||
|
||||
1. ✅ Generate plan.md (this file)
|
||||
2. → Generate research.md (detailed technology findings)
|
||||
3. → Generate data-model.md (schemas + diagrams)
|
||||
4. → Generate quickstart.md (developer onboarding)
|
||||
5. → Run `/speckit.tasks` to create task breakdown
|
||||
6. → Begin Phase 2.1 implementation
|
||||
|
||||
---
|
||||
|
||||
**Status**: Plan Complete - Ready for Research
|
||||
**Created**: 2025-12-22
|
||||
**Last Updated**: 2025-12-22
|
||||
No constitution violations requiring justification.
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('backup sets table bulk archive creates a run and archives selected sets', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$sets = collect(range(1, 3))->map(function (int $i) use ($tenant) {
|
||||
@ -56,6 +57,7 @@
|
||||
|
||||
test('backup sets table bulk archive requires type-to-confirm for 10+ sets', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$sets = collect(range(1, 10))->map(function (int $i) use ($tenant) {
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('bulk delete restore runs skips running items', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$backupSet = BackupSet::create([
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('bulk delete restore runs soft deletes selected runs', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$backupSet = BackupSet::create([
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('backup sets table bulk force delete permanently deletes archived sets and their items', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$set = BackupSet::create([
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('bulk force delete restore runs permanently deletes archived runs', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$backupSet = BackupSet::create([
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('backup sets table bulk restore restores archived sets and their items', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$set = BackupSet::create([
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
|
||||
test('restore runs table bulk restore creates a run and restores archived records', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$backupSet = BackupSet::create([
|
||||
|
||||
@ -1,19 +1,21 @@
|
||||
<?php
|
||||
|
||||
use App\Filament\Resources\PolicyResource;
|
||||
use App\Jobs\BulkPolicySyncJob;
|
||||
use App\Models\AuditLog;
|
||||
use App\Models\BulkOperationRun;
|
||||
use App\Models\Policy;
|
||||
use App\Models\Tenant;
|
||||
use App\Models\User;
|
||||
use App\Services\BulkOperationService;
|
||||
use App\Services\Graph\GraphClientInterface;
|
||||
use App\Services\Graph\GraphResponse;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
test('bulk sync updates selected policies from graph', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
|
||||
$policies = Policy::factory()
|
||||
@ -25,7 +27,7 @@
|
||||
'last_synced_at' => null,
|
||||
]);
|
||||
|
||||
app()->bind(GraphClientInterface::class, fn () => new class implements GraphClientInterface
|
||||
app()->instance(GraphClientInterface::class, new class implements GraphClientInterface
|
||||
{
|
||||
public function listPolicies(string $policyType, array $options = []): GraphResponse
|
||||
{
|
||||
@ -65,10 +67,17 @@ public function request(string $method, string $path, array $options = []): Grap
|
||||
}
|
||||
});
|
||||
|
||||
Livewire::actingAs($user)
|
||||
->test(PolicyResource\Pages\ListPolicies::class)
|
||||
->callTableBulkAction('bulk_sync', $policies)
|
||||
->assertHasNoTableBulkActionErrors();
|
||||
$service = app(BulkOperationService::class);
|
||||
$run = $service->createRun($tenant, $user, 'policy', 'sync', $policies->modelKeys(), 3);
|
||||
|
||||
BulkPolicySyncJob::dispatchSync($run->id);
|
||||
|
||||
$bulkRun = BulkOperationRun::query()->find($run->id);
|
||||
expect($bulkRun)->not->toBeNull();
|
||||
expect($bulkRun->status)->toBe('completed');
|
||||
expect($bulkRun->total_items)->toBe(3);
|
||||
expect($bulkRun->succeeded)->toBe(3);
|
||||
expect($bulkRun->failed)->toBe(0);
|
||||
|
||||
$policies->each(function (Policy $policy) {
|
||||
$policy->refresh();
|
||||
|
||||
@ -11,6 +11,7 @@
|
||||
|
||||
test('bulk delete requires confirmation string for large batches', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
$policies = Policy::factory()->count(20)->create(['tenant_id' => $tenant->id]);
|
||||
|
||||
@ -26,6 +27,7 @@
|
||||
|
||||
test('bulk delete fails with incorrect confirmation string', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
$policies = Policy::factory()->count(20)->create(['tenant_id' => $tenant->id]);
|
||||
|
||||
@ -41,6 +43,7 @@
|
||||
|
||||
test('bulk delete does not require confirmation string for small batches', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
$policies = Policy::factory()->count(10)->create(['tenant_id' => $tenant->id]);
|
||||
|
||||
|
||||
@ -6,7 +6,11 @@
|
||||
use App\Models\User;
|
||||
|
||||
beforeEach(function () {
|
||||
putenv('INTUNE_TENANT_ID');
|
||||
unset($_ENV['INTUNE_TENANT_ID'], $_SERVER['INTUNE_TENANT_ID']);
|
||||
|
||||
$this->tenant = Tenant::factory()->create();
|
||||
$this->tenant->makeCurrent();
|
||||
$this->policy = Policy::factory()->create([
|
||||
'tenant_id' => $this->tenant->id,
|
||||
]);
|
||||
|
||||
@ -17,6 +17,11 @@
|
||||
->use(RefreshDatabase::class)
|
||||
->in('Feature');
|
||||
|
||||
beforeEach(function () {
|
||||
putenv('INTUNE_TENANT_ID');
|
||||
unset($_ENV['INTUNE_TENANT_ID'], $_SERVER['INTUNE_TENANT_ID']);
|
||||
});
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Expectations
|
||||
|
||||
@ -12,6 +12,7 @@
|
||||
|
||||
test('policies bulk actions are available for authenticated users', function () {
|
||||
$tenant = Tenant::factory()->create();
|
||||
$tenant->makeCurrent();
|
||||
$user = User::factory()->create();
|
||||
$policies = Policy::factory()->count(2)->create(['tenant_id' => $tenant->id]);
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user