319-environment-owned-surface-routing-shell-context-contract #380
@ -137,11 +137,14 @@ public static function canAccess(): bool
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return (int) (app(GovernanceDecisionRegisterBuilder::class)->build(
|
$counts = app(GovernanceDecisionRegisterBuilder::class)->build(
|
||||||
workspace: $workspace,
|
workspace: $workspace,
|
||||||
visibleTenants: $visibleTenants,
|
visibleTenants: $visibleTenants,
|
||||||
registerState: 'open',
|
registerState: 'open',
|
||||||
)['counts']['open'] ?? 0) > 0;
|
)['counts'] ?? [];
|
||||||
|
|
||||||
|
return (int) ($counts['open'] ?? 0) > 0
|
||||||
|
|| (int) ($counts['recently_closed'] ?? 0) > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function mount(): void
|
public function mount(): void
|
||||||
@ -416,7 +419,19 @@ private function ensureRegisterIsVisible(): void
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ((int) ($this->registerPayload()['counts']['open'] ?? 0) === 0) {
|
$counts = $this->unfilteredRegisterPayload()['counts'] ?? [];
|
||||||
|
|
||||||
|
if ((int) ($counts['open'] ?? 0) > 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int) ($counts['recently_closed'] ?? 0) > 0) {
|
||||||
|
$this->redirect($this->pageUrl(['register_state' => 'recently_closed']), navigate: true);
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((int) ($counts['open'] ?? 0) === 0) {
|
||||||
abort(403);
|
abort(403);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,7 @@
|
|||||||
use App\Support\Filament\CanonicalAdminTenantFilterState;
|
use App\Support\Filament\CanonicalAdminTenantFilterState;
|
||||||
use App\Support\Navigation\CanonicalNavigationContext;
|
use App\Support\Navigation\CanonicalNavigationContext;
|
||||||
use App\Support\OperateHub\OperateHubShell;
|
use App\Support\OperateHub\OperateHubShell;
|
||||||
|
use App\Support\OperationRunLinks;
|
||||||
use App\Support\OperationRunOutcome;
|
use App\Support\OperationRunOutcome;
|
||||||
use App\Support\OperationRunStatus;
|
use App\Support\OperationRunStatus;
|
||||||
use App\Support\Operations\OperationLifecyclePolicy;
|
use App\Support\Operations\OperationLifecyclePolicy;
|
||||||
@ -23,6 +24,7 @@
|
|||||||
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
|
use App\Support\Ui\ActionSurface\Enums\ActionSurfaceType;
|
||||||
use App\Support\Workspaces\WorkspaceContext;
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
|
use App\Services\Auth\ManagedEnvironmentAccessScopeResolver;
|
||||||
use App\Services\Auth\WorkspaceCapabilityResolver;
|
use App\Services\Auth\WorkspaceCapabilityResolver;
|
||||||
use BackedEnum;
|
use BackedEnum;
|
||||||
use Filament\Actions\Action;
|
use Filament\Actions\Action;
|
||||||
@ -347,6 +349,7 @@ public function table(Table $table): Table
|
|||||||
->query(function (): Builder {
|
->query(function (): Builder {
|
||||||
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
$workspaceId = app(WorkspaceContext::class)->currentWorkspaceId(request());
|
||||||
$tenantFilter = $this->currentTenantFilterId();
|
$tenantFilter = $this->currentTenantFilterId();
|
||||||
|
$allowedTenantIds = $this->allowedTenantIdsForWorkspaceScope($workspaceId);
|
||||||
|
|
||||||
$query = OperationRun::query()
|
$query = OperationRun::query()
|
||||||
->with('user')
|
->with('user')
|
||||||
@ -359,6 +362,18 @@ public function table(Table $table): Table
|
|||||||
! $workspaceId,
|
! $workspaceId,
|
||||||
fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
|
fn (Builder $query): Builder => $query->whereRaw('1 = 0'),
|
||||||
)
|
)
|
||||||
|
->when(
|
||||||
|
$workspaceId && $allowedTenantIds !== null,
|
||||||
|
function (Builder $query) use ($allowedTenantIds): Builder {
|
||||||
|
return $query->where(function (Builder $query) use ($allowedTenantIds): void {
|
||||||
|
$query->whereNull('managed_environment_id');
|
||||||
|
|
||||||
|
if ($allowedTenantIds !== []) {
|
||||||
|
$query->orWhereIn('managed_environment_id', $allowedTenantIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
->when(
|
->when(
|
||||||
$tenantFilter !== null,
|
$tenantFilter !== null,
|
||||||
fn (Builder $query): Builder => $query->where('managed_environment_id', $tenantFilter),
|
fn (Builder $query): Builder => $query->where('managed_environment_id', $tenantFilter),
|
||||||
@ -437,9 +452,22 @@ private function scopedSummaryQuery(): ?Builder
|
|||||||
}
|
}
|
||||||
|
|
||||||
$tenantFilter = $this->currentTenantFilterId();
|
$tenantFilter = $this->currentTenantFilterId();
|
||||||
|
$allowedTenantIds = $this->allowedTenantIdsForWorkspaceScope($workspaceId);
|
||||||
|
|
||||||
return OperationRun::query()
|
return OperationRun::query()
|
||||||
->where('workspace_id', (int) $workspaceId)
|
->where('workspace_id', (int) $workspaceId)
|
||||||
|
->when(
|
||||||
|
$allowedTenantIds !== null,
|
||||||
|
function (Builder $query) use ($allowedTenantIds): Builder {
|
||||||
|
return $query->where(function (Builder $query) use ($allowedTenantIds): void {
|
||||||
|
$query->whereNull('managed_environment_id');
|
||||||
|
|
||||||
|
if ($allowedTenantIds !== []) {
|
||||||
|
$query->orWhereIn('managed_environment_id', $allowedTenantIds);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
)
|
||||||
->when(
|
->when(
|
||||||
$tenantFilter !== null,
|
$tenantFilter !== null,
|
||||||
fn (Builder $query): Builder => $query->where('managed_environment_id', $tenantFilter),
|
fn (Builder $query): Builder => $query->where('managed_environment_id', $tenantFilter),
|
||||||
@ -509,6 +537,29 @@ private function currentTenantFilterId(): ?int
|
|||||||
return $this->normalizeEntitledTenantFilter($tenantFilter);
|
return $this->normalizeEntitledTenantFilter($tenantFilter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Null means inherited access to all environments in the workspace.
|
||||||
|
*
|
||||||
|
* @return list<int>|null
|
||||||
|
*/
|
||||||
|
private function allowedTenantIdsForWorkspaceScope(mixed $workspaceId): ?array
|
||||||
|
{
|
||||||
|
$user = auth()->user();
|
||||||
|
|
||||||
|
if (! $user instanceof User || ! is_int($workspaceId)) {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$allowedIds = app(ManagedEnvironmentAccessScopeResolver::class)
|
||||||
|
->allowedManagedEnvironmentIdsForWorkspace($user, $workspaceId);
|
||||||
|
|
||||||
|
if ($allowedIds === null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return array_values(array_unique(array_map('intval', $allowedIds)));
|
||||||
|
}
|
||||||
|
|
||||||
private function normalizeEntitledTenantFilter(mixed $value): ?int
|
private function normalizeEntitledTenantFilter(mixed $value): ?int
|
||||||
{
|
{
|
||||||
if (! is_numeric($value)) {
|
if (! is_numeric($value)) {
|
||||||
|
|||||||
@ -20,6 +20,14 @@ public static function getLabel(): string
|
|||||||
return 'Register tenant';
|
return 'Register tenant';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return class-string<Model>
|
||||||
|
*/
|
||||||
|
public function getModel(): string
|
||||||
|
{
|
||||||
|
return ManagedEnvironment::class;
|
||||||
|
}
|
||||||
|
|
||||||
public static function canView(): bool
|
public static function canView(): bool
|
||||||
{
|
{
|
||||||
$user = auth()->user();
|
$user = auth()->user();
|
||||||
|
|||||||
@ -111,7 +111,12 @@ public static function getUrl(array $parameters = [], bool $isAbsolute = true, ?
|
|||||||
return url('/admin');
|
return url('/admin');
|
||||||
}
|
}
|
||||||
|
|
||||||
return url('/admin/workspaces/'.($workspace->slug ?? $workspace->getKey()).'/environments/'.$resolvedTenant->getRouteKey());
|
$url = url('/admin/workspaces/'.($workspace->slug ?? $workspace->getKey()).'/environments/'.$resolvedTenant->getRouteKey());
|
||||||
|
$query = array_diff_key($parameters, array_flip(['tenant', 'environment', 'workspace']));
|
||||||
|
|
||||||
|
return $query === []
|
||||||
|
? $url
|
||||||
|
: $url.'?'.http_build_query($query);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@ -96,7 +96,7 @@ public function bootstrapOwner(): void
|
|||||||
abort(403, 'Not allowed');
|
abort(403, 'Not allowed');
|
||||||
}
|
}
|
||||||
|
|
||||||
app(TenantMembershipManager::class)->grantScope($tenant, $user, $user, source: 'diagnostic');
|
app(TenantMembershipManager::class)->grantScope($tenant, $user, $user, sourceRef: 'diagnostic');
|
||||||
|
|
||||||
$this->mount();
|
$this->mount();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -4844,7 +4844,9 @@ private function completionSummaryBootstrapLabel(): string
|
|||||||
$runs = is_array($runs) ? $runs : [];
|
$runs = is_array($runs) ? $runs : [];
|
||||||
|
|
||||||
if ($runs !== []) {
|
if ($runs !== []) {
|
||||||
return 'Started';
|
return $this->completionSummaryBootstrapCompleted()
|
||||||
|
? 'Completed'
|
||||||
|
: 'Started';
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->completionSummarySelectedBootstrapTypes() === []
|
return $this->completionSummarySelectedBootstrapTypes() === []
|
||||||
@ -4879,6 +4881,10 @@ private function completionSummaryBootstrapDetail(): string
|
|||||||
return sprintf('%d action(s) selected', count($selectedTypes));
|
return sprintf('%d action(s) selected', count($selectedTypes));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->completionSummaryBootstrapCompleted()) {
|
||||||
|
return sprintf('%d action(s) completed', count($runs));
|
||||||
|
}
|
||||||
|
|
||||||
if (count($runs) < count($selectedTypes)) {
|
if (count($runs) < count($selectedTypes)) {
|
||||||
return sprintf('%d of %d action(s) started', count($runs), count($selectedTypes));
|
return sprintf('%d of %d action(s) started', count($runs), count($selectedTypes));
|
||||||
}
|
}
|
||||||
@ -4895,6 +4901,41 @@ private function completionSummaryBootstrapSummary(): string
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function completionSummaryBootstrapCompleted(): bool
|
||||||
|
{
|
||||||
|
if (! $this->onboardingSession instanceof TenantOnboardingSession) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$selectedTypes = $this->completionSummarySelectedBootstrapTypes();
|
||||||
|
|
||||||
|
if ($selectedTypes === []) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$runs = $this->onboardingSession->state['bootstrap_operation_runs'] ?? null;
|
||||||
|
$runs = is_array($runs) ? $runs : [];
|
||||||
|
|
||||||
|
if ($runs === [] || count($runs) < count($selectedTypes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$runIds = array_values(array_filter(array_map(
|
||||||
|
static fn (mixed $value): ?int => is_numeric($value) ? (int) $value : null,
|
||||||
|
$runs,
|
||||||
|
)));
|
||||||
|
|
||||||
|
if (count($runIds) < count($selectedTypes)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OperationRun::query()
|
||||||
|
->whereIn('id', $runIds)
|
||||||
|
->where('status', OperationRunStatus::Completed->value)
|
||||||
|
->where('outcome', OperationRunOutcome::Succeeded->value)
|
||||||
|
->count() >= count($selectedTypes);
|
||||||
|
}
|
||||||
|
|
||||||
private function showCompletionSummaryBootstrapRecovery(): bool
|
private function showCompletionSummaryBootstrapRecovery(): bool
|
||||||
{
|
{
|
||||||
return $this->completionSummaryBootstrapActionRequiredDetail() !== null;
|
return $this->completionSummaryBootstrapActionRequiredDetail() !== null;
|
||||||
@ -4910,6 +4951,7 @@ private function completionSummaryBootstrapColor(): string
|
|||||||
return match ($this->completionSummaryBootstrapLabel()) {
|
return match ($this->completionSummaryBootstrapLabel()) {
|
||||||
'Action required' => 'warning',
|
'Action required' => 'warning',
|
||||||
'Started' => 'info',
|
'Started' => 'info',
|
||||||
|
'Completed' => 'success',
|
||||||
'Selected' => 'warning',
|
'Selected' => 'warning',
|
||||||
default => 'gray',
|
default => 'gray',
|
||||||
};
|
};
|
||||||
|
|||||||
@ -55,6 +55,8 @@ protected function getViewData(): array
|
|||||||
->limit(5)
|
->limit(5)
|
||||||
->get([
|
->get([
|
||||||
'id',
|
'id',
|
||||||
|
'workspace_id',
|
||||||
|
'managed_environment_id',
|
||||||
'type',
|
'type',
|
||||||
'status',
|
'status',
|
||||||
'outcome',
|
'outcome',
|
||||||
|
|||||||
@ -146,6 +146,11 @@ public function panel(Panel $panel): Panel
|
|||||||
->icon('heroicon-o-queue-list')
|
->icon('heroicon-o-queue-list')
|
||||||
->group(fn (): string => __('localization.navigation.monitoring'))
|
->group(fn (): string => __('localization.navigation.monitoring'))
|
||||||
->sort(10),
|
->sort(10),
|
||||||
|
NavigationItem::make('Alerts')
|
||||||
|
->url(fn (): string => route('filament.admin.alerts'))
|
||||||
|
->icon('heroicon-o-bell-alert')
|
||||||
|
->group(fn (): string => __('localization.navigation.monitoring'))
|
||||||
|
->sort(23),
|
||||||
NavigationItem::make(fn (): string => __('localization.navigation.audit_log'))
|
NavigationItem::make(fn (): string => __('localization.navigation.audit_log'))
|
||||||
->url(fn (): string => route('admin.monitoring.audit-log'))
|
->url(fn (): string => route('admin.monitoring.audit-log'))
|
||||||
->icon('heroicon-o-clipboard-document-list')
|
->icon('heroicon-o-clipboard-document-list')
|
||||||
@ -161,7 +166,7 @@ public function panel(Panel $panel): Panel
|
|||||||
fn () => view('filament.partials.context-bar')->render()
|
fn () => view('filament.partials.context-bar')->render()
|
||||||
)
|
)
|
||||||
->renderHook(
|
->renderHook(
|
||||||
PanelsRenderHook::BODY_END,
|
PanelsRenderHook::PAGE_START,
|
||||||
fn (): string => request()->routeIs('admin.workspace.managed-tenants.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-tenant')
|
fn (): string => request()->routeIs('admin.workspace.managed-tenants.index', 'admin.onboarding', 'admin.onboarding.draft', 'filament.admin.pages.choose-tenant')
|
||||||
? ''
|
? ''
|
||||||
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
|
: ((bool) config('tenantpilot.bulk_operations.progress_widget_enabled', true)
|
||||||
@ -224,13 +229,11 @@ public function panel(Panel $panel): Panel
|
|||||||
Authenticate::class,
|
Authenticate::class,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
if (! app()->runningUnitTests()) {
|
|
||||||
$theme = PanelThemeAsset::resolve('resources/css/filament/admin/theme.css');
|
$theme = PanelThemeAsset::resolve('resources/css/filament/admin/theme.css');
|
||||||
|
|
||||||
if (is_string($theme)) {
|
if (is_string($theme)) {
|
||||||
$panel->theme($theme);
|
$panel->theme($theme);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
return $panel;
|
return $panel;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -51,7 +51,7 @@ public function panel(Panel $panel): Panel
|
|||||||
])
|
])
|
||||||
->navigationItems([
|
->navigationItems([
|
||||||
NavigationItem::make(fn (): string => __('localization.navigation.operations'))
|
NavigationItem::make(fn (): string => __('localization.navigation.operations'))
|
||||||
->url(fn (): string => route('admin.operations.index'))
|
->url(fn (): string => OperationRunLinks::index())
|
||||||
->icon('heroicon-o-queue-list')
|
->icon('heroicon-o-queue-list')
|
||||||
->group(fn (): string => __('localization.navigation.monitoring'))
|
->group(fn (): string => __('localization.navigation.monitoring'))
|
||||||
->sort(10),
|
->sort(10),
|
||||||
|
|||||||
@ -18,6 +18,11 @@ final class ManagedEnvironmentAccessScopeResolver
|
|||||||
*/
|
*/
|
||||||
private array $scopeIdsByUserWorkspace = [];
|
private array $scopeIdsByUserWorkspace = [];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @var array<int, Workspace|null>
|
||||||
|
*/
|
||||||
|
private array $workspaceById = [];
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
private readonly WorkspaceCapabilityResolver $workspaceCapabilityResolver,
|
private readonly WorkspaceCapabilityResolver $workspaceCapabilityResolver,
|
||||||
) {}
|
) {}
|
||||||
@ -44,7 +49,7 @@ public function decision(User $user, ManagedEnvironment $tenant, ?string $requir
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
$workspace = Workspace::query()->whereKey($workspaceId)->first();
|
$workspace = $this->workspaceForId($workspaceId);
|
||||||
|
|
||||||
if (! $workspace instanceof Workspace) {
|
if (! $workspace instanceof Workspace) {
|
||||||
return new ManagedEnvironmentAccessDecision(
|
return new ManagedEnvironmentAccessDecision(
|
||||||
@ -139,7 +144,7 @@ public function canAccess(User $user, ManagedEnvironment $tenant): bool
|
|||||||
*/
|
*/
|
||||||
public function allowedManagedEnvironmentIdsForWorkspace(User $user, int $workspaceId): ?array
|
public function allowedManagedEnvironmentIdsForWorkspace(User $user, int $workspaceId): ?array
|
||||||
{
|
{
|
||||||
$workspace = Workspace::query()->whereKey($workspaceId)->first();
|
$workspace = $this->workspaceForId($workspaceId);
|
||||||
|
|
||||||
if (! $workspace instanceof Workspace || $this->workspaceCapabilityResolver->getRole($user, $workspace) === null) {
|
if (! $workspace instanceof Workspace || $this->workspaceCapabilityResolver->getRole($user, $workspace) === null) {
|
||||||
return [];
|
return [];
|
||||||
@ -187,6 +192,7 @@ public function prime(User $user, array $tenantIds): void
|
|||||||
public function clearCache(): void
|
public function clearCache(): void
|
||||||
{
|
{
|
||||||
$this->scopeIdsByUserWorkspace = [];
|
$this->scopeIdsByUserWorkspace = [];
|
||||||
|
$this->workspaceById = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public function applyWorkspaceScopeToQuery(Builder $query, User $user, int $workspaceId, string $qualifiedEnvironmentColumn): Builder
|
public function applyWorkspaceScopeToQuery(Builder $query, User $user, int $workspaceId, string $qualifiedEnvironmentColumn): Builder
|
||||||
@ -233,6 +239,15 @@ private function scopeIdsForWorkspace(User $user, int $workspaceId): ?array
|
|||||||
return $this->scopeIdsByUserWorkspace[$cacheKey];
|
return $this->scopeIdsByUserWorkspace[$cacheKey];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function workspaceForId(int $workspaceId): ?Workspace
|
||||||
|
{
|
||||||
|
if (! array_key_exists($workspaceId, $this->workspaceById)) {
|
||||||
|
$this->workspaceById[$workspaceId] = Workspace::query()->whereKey($workspaceId)->first();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->workspaceById[$workspaceId];
|
||||||
|
}
|
||||||
|
|
||||||
private function hydrateTenantBoundary(ManagedEnvironment $tenant): ?ManagedEnvironment
|
private function hydrateTenantBoundary(ManagedEnvironment $tenant): ?ManagedEnvironment
|
||||||
{
|
{
|
||||||
if ($tenant->exists && $tenant->workspace_id !== null) {
|
if ($tenant->exists && $tenant->workspace_id !== null) {
|
||||||
|
|||||||
@ -199,6 +199,15 @@ public function startCompareForVisibleAssignments(BaselineProfile $profile, User
|
|||||||
$blockedCount = 0;
|
$blockedCount = 0;
|
||||||
$targets = [];
|
$targets = [];
|
||||||
|
|
||||||
|
$this->capabilityResolver->primeMemberships(
|
||||||
|
$initiator,
|
||||||
|
$assignments
|
||||||
|
->pluck('managed_environment_id')
|
||||||
|
->filter(static fn (mixed $id): bool => is_numeric($id))
|
||||||
|
->map(static fn (mixed $id): int => (int) $id)
|
||||||
|
->all(),
|
||||||
|
);
|
||||||
|
|
||||||
foreach ($assignments as $assignment) {
|
foreach ($assignments as $assignment) {
|
||||||
$tenant = $assignment->tenant;
|
$tenant = $assignment->tenant;
|
||||||
|
|
||||||
|
|||||||
@ -46,6 +46,15 @@ public function build(BaselineProfile $profile, User $user, array $filters = [])
|
|||||||
->with('tenant')
|
->with('tenant')
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
|
$this->capabilityResolver->primeMemberships(
|
||||||
|
$user,
|
||||||
|
$assignments
|
||||||
|
->pluck('managed_environment_id')
|
||||||
|
->filter(static fn (mixed $id): bool => is_numeric($id))
|
||||||
|
->map(static fn (mixed $id): int => (int) $id)
|
||||||
|
->all(),
|
||||||
|
);
|
||||||
|
|
||||||
$visibleTenants = $this->visibleTenants($assignments, $user);
|
$visibleTenants = $this->visibleTenants($assignments, $user);
|
||||||
$referenceResolution = $this->snapshotTruthResolver->resolveCompareSnapshot($profile);
|
$referenceResolution = $this->snapshotTruthResolver->resolveCompareSnapshot($profile);
|
||||||
$referenceSnapshot = $this->resolvedSnapshot($referenceResolution);
|
$referenceSnapshot = $this->resolvedSnapshot($referenceResolution);
|
||||||
|
|||||||
@ -405,6 +405,7 @@ private function operationsSection(
|
|||||||
tenant: $selectedTenant,
|
tenant: $selectedTenant,
|
||||||
context: $navigationContext,
|
context: $navigationContext,
|
||||||
problemClass: $dominantProblemClass,
|
problemClass: $dominantProblemClass,
|
||||||
|
workspace: $workspace,
|
||||||
),
|
),
|
||||||
'entries' => $entries,
|
'entries' => $entries,
|
||||||
'empty_state' => $selectedTenant instanceof ManagedEnvironment
|
'empty_state' => $selectedTenant instanceof ManagedEnvironment
|
||||||
|
|||||||
@ -84,8 +84,11 @@ public static function index(
|
|||||||
bool $allTenants = false,
|
bool $allTenants = false,
|
||||||
?string $problemClass = null,
|
?string $problemClass = null,
|
||||||
?string $operationType = null,
|
?string $operationType = null,
|
||||||
|
?Workspace $workspace = null,
|
||||||
): string {
|
): string {
|
||||||
$workspace = self::resolveWorkspace($tenant);
|
$workspace = $tenant instanceof ManagedEnvironment
|
||||||
|
? self::resolveWorkspace($tenant)
|
||||||
|
: ($workspace ?? self::resolveWorkspace());
|
||||||
|
|
||||||
if (! $workspace instanceof Workspace) {
|
if (! $workspace instanceof Workspace) {
|
||||||
return url('/admin');
|
return url('/admin');
|
||||||
|
|||||||
@ -13,7 +13,7 @@ class="flex w-full flex-col items-start gap-3 sm:flex-row sm:flex-wrap sm:items-
|
|||||||
@if (filled($context['provider'] ?? null))
|
@if (filled($context['provider'] ?? null))
|
||||||
<div data-testid="tenant-dashboard-context-chip-provider" data-provider-key="{{ $context['providerKey'] ?? '' }}" class="inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-gray-200 bg-white/80 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm backdrop-blur dark:border-white/10 dark:bg-white/5 dark:text-gray-200">
|
<div data-testid="tenant-dashboard-context-chip-provider" data-provider-key="{{ $context['providerKey'] ?? '' }}" class="inline-flex items-center gap-2 whitespace-nowrap rounded-2xl border border-gray-200 bg-white/80 px-4 py-2 text-sm font-medium text-gray-700 shadow-sm backdrop-blur dark:border-white/10 dark:bg-white/5 dark:text-gray-200">
|
||||||
@if (($context['providerKey'] ?? null) === 'microsoft')
|
@if (($context['providerKey'] ?? null) === 'microsoft')
|
||||||
<svg data-testid="tenant-dashboard-context-chip-provider-microsoft-logo" viewBox="0 0 16 16" aria-hidden="true" class="h-4 w-4 shrink-0">
|
<svg data-testid="tenant-dashboard-context-chip-provider-microsoft-logo" viewBox="0 0 16 16" width="16" height="16" aria-hidden="true" class="h-4 w-4 shrink-0">
|
||||||
<rect x="1" y="1" width="6" height="6" fill="#f25022" />
|
<rect x="1" y="1" width="6" height="6" fill="#f25022" />
|
||||||
<rect x="9" y="1" width="6" height="6" fill="#7fba00" />
|
<rect x="9" y="1" width="6" height="6" fill="#7fba00" />
|
||||||
<rect x="1" y="9" width="6" height="6" fill="#00a4ef" />
|
<rect x="1" y="9" width="6" height="6" fill="#00a4ef" />
|
||||||
|
|||||||
@ -1 +1,3 @@
|
|||||||
<livewire:bulk-operation-progress />
|
@if (\Filament\Facades\Filament::getCurrentPanel()?->getId() === 'admin' && auth()->user() instanceof \App\Models\User)
|
||||||
|
<livewire:bulk-operation-progress />
|
||||||
|
@endif
|
||||||
|
|||||||
@ -386,10 +386,18 @@
|
|||||||
abort_unless($allowed, 404);
|
abort_unless($allowed, 404);
|
||||||
};
|
};
|
||||||
|
|
||||||
Route::middleware(['web', 'auth', 'ensure-correct-guard:web', 'ensure-workspace-member'])
|
Route::middleware([
|
||||||
|
'web',
|
||||||
|
'panel:admin',
|
||||||
|
'ensure-correct-guard:web',
|
||||||
|
DisableBladeIconComponents::class,
|
||||||
|
DispatchServingFilamentEvent::class,
|
||||||
|
FilamentAuthenticate::class,
|
||||||
|
'ensure-workspace-member',
|
||||||
|
])
|
||||||
->prefix('/admin/workspaces/{workspace}')
|
->prefix('/admin/workspaces/{workspace}')
|
||||||
->group(function (): void {
|
->group(function (): void {
|
||||||
Route::get('/', WorkspaceOverview::class)
|
Route::get('/overview', WorkspaceOverview::class)
|
||||||
->name('admin.workspace.home');
|
->name('admin.workspace.home');
|
||||||
|
|
||||||
Route::get('/ping', fn () => response()->noContent())->name('admin.workspace.ping');
|
Route::get('/ping', fn () => response()->noContent())->name('admin.workspace.ping');
|
||||||
|
|||||||
@ -8,6 +8,7 @@
|
|||||||
use App\Models\Finding;
|
use App\Models\Finding;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\ProviderConnection;
|
use App\Models\ProviderConnection;
|
||||||
|
use App\Support\OperationRunLinks;
|
||||||
use App\Support\OperationRunOutcome;
|
use App\Support\OperationRunOutcome;
|
||||||
use App\Support\OperationRunStatus;
|
use App\Support\OperationRunStatus;
|
||||||
use App\Support\Workspaces\WorkspaceContext;
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
@ -33,6 +34,7 @@
|
|||||||
'outcome' => OperationRunOutcome::Failed->value,
|
'outcome' => OperationRunOutcome::Failed->value,
|
||||||
'completed_at' => now()->subHour(),
|
'completed_at' => now()->subHour(),
|
||||||
]);
|
]);
|
||||||
|
$operationPath = (string) parse_url(OperationRunLinks::tenantlessView($operation), PHP_URL_PATH);
|
||||||
|
|
||||||
$backupSet = BackupSet::factory()->create([
|
$backupSet = BackupSet::factory()->create([
|
||||||
'managed_environment_id' => (int) $tenant->getKey(),
|
'managed_environment_id' => (int) $tenant->getKey(),
|
||||||
@ -62,17 +64,18 @@
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$page = visit(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$page = visit(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->waitForText($tenant->name)
|
->waitForText($tenant->name)
|
||||||
->waitForText('Backup posture')
|
->waitForText('Backup posture')
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-posture-pill\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-posture-pill\"]') !== null", true)
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-workspace\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-workspace\"]') !== null", true)
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider\"][data-provider-key=\"microsoft\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider\"][data-provider-key=\"microsoft\"]') !== null", true)
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider-microsoft-logo\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider-microsoft-logo\"]') !== null", true)
|
||||||
|
->assertScript("(() => { const logo = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider-microsoft-logo\"]'); if (! logo) return false; const rect = logo.getBoundingClientRect(); return rect.width <= 20 && rect.height <= 20; })()", true)
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity\"]') !== null", true)
|
||||||
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity-icon\"]') !== null", true)
|
->assertScript("document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity-icon\"]') !== null", true)
|
||||||
->assertScript("(() => { const chips = document.querySelector('[data-testid=\"tenant-dashboard-context-chips\"]'); const firstKpi = document.querySelector('[data-testid=\"tenant-dashboard-kpi\"]'); if (! chips || ! firstKpi) return false; return chips.getBoundingClientRect().top < firstKpi.getBoundingClientRect().top; })()", true)
|
->assertScript("(() => { const chips = document.querySelector('[data-testid=\"tenant-dashboard-context-chips\"]'); const firstKpi = document.querySelector('[data-testid=\"tenant-dashboard-kpi\"]'); if (! chips || ! firstKpi) return false; return chips.getBoundingClientRect().top < firstKpi.getBoundingClientRect().top; })()", true)
|
||||||
->assertScript("(() => { const subtitle = Array.from(document.querySelectorAll('p')).find((node) => node.textContent?.includes('Tenant governance overview')); const chips = document.querySelector('[data-testid=\"tenant-dashboard-context-chips\"]'); if (! subtitle || ! chips) return false; return chips.getBoundingClientRect().top - subtitle.getBoundingClientRect().bottom <= 40; })()", true)
|
->assertScript("(() => { const subtitle = Array.from(document.querySelectorAll('p')).find((node) => node.textContent?.includes('governance overview')); const chips = document.querySelector('[data-testid=\"tenant-dashboard-context-chips\"]'); if (! subtitle || ! chips) return false; return chips.getBoundingClientRect().top - subtitle.getBoundingClientRect().bottom <= 64; })()", true)
|
||||||
->assertScript("(() => { const workspace = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-workspace\"]'); const provider = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider\"]'); const activity = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity\"]'); if (! workspace || ! provider || ! activity) return false; const tops = [workspace, provider, activity].map((element) => Math.round(element.getBoundingClientRect().top)); return Math.max(...tops) - Math.min(...tops) <= 2; })()", true)
|
->assertScript("(() => { const workspace = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-workspace\"]'); const provider = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-provider\"]'); const activity = document.querySelector('[data-testid=\"tenant-dashboard-context-chip-latest-activity\"]'); if (! workspace || ! provider || ! activity) return false; const tops = [workspace, provider, activity].map((element) => Math.round(element.getBoundingClientRect().top)); return Math.max(...tops) - Math.min(...tops) <= 2; })()", true)
|
||||||
->assertSee('Recommended next actions')
|
->assertSee('Recommended next actions')
|
||||||
->assertSee('Operations needing attention')
|
->assertSee('Operations needing attention')
|
||||||
@ -108,9 +111,10 @@
|
|||||||
->assertScript("! document.body.innerHTML.includes('fixed bottom-4 right-4 z-[999999] w-96 space-y-2')", true)
|
->assertScript("! document.body.innerHTML.includes('fixed bottom-4 right-4 z-[999999] w-96 space-y-2')", true)
|
||||||
->assertScript("(() => { const overview = document.querySelector('[data-testid=\"tenant-dashboard-overview\"]'); const main = document.querySelector('[data-testid=\"tenant-dashboard-overview-main\"]'); if (! overview || ! main) return false; const overviewWidth = overview.getBoundingClientRect().width; const mainWidth = main.getBoundingClientRect().width; return overviewWidth >= 600 && mainWidth >= 400; })()", true)
|
->assertScript("(() => { const overview = document.querySelector('[data-testid=\"tenant-dashboard-overview\"]'); const main = document.querySelector('[data-testid=\"tenant-dashboard-overview-main\"]'); if (! overview || ! main) return false; const overviewWidth = overview.getBoundingClientRect().width; const mainWidth = main.getBoundingClientRect().width; return overviewWidth >= 600 && mainWidth >= 400; })()", true)
|
||||||
->assertScript("document.querySelectorAll('[data-testid=\"tenant-dashboard-overview\"] table').length === 0", true)
|
->assertScript("document.querySelectorAll('[data-testid=\"tenant-dashboard-overview\"] table').length === 0", true)
|
||||||
->click('Review operation')
|
->assertSeeIn('[data-testid="ops-ux-activity-feedback-primary-action"]', 'View operation')
|
||||||
|
->click('[data-testid="ops-ux-activity-feedback-primary-action"]')
|
||||||
->waitForText('Show all operations')
|
->waitForText('Show all operations')
|
||||||
->assertScript("window.location.pathname.includes('/admin/operations/{$operation->getKey()}')", true)
|
->assertScript("window.location.pathname === '{$operationPath}'", true)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
|
|||||||
@ -52,7 +52,6 @@
|
|||||||
'entra_tenant_id' => (string) $tenant->managed_environment_id,
|
'entra_tenant_id' => (string) $tenant->managed_environment_id,
|
||||||
'tenant_name' => (string) $tenant->name,
|
'tenant_name' => (string) $tenant->name,
|
||||||
'environment' => 'prod',
|
'environment' => 'prod',
|
||||||
'provider_connection_id' => (int) $connection->getKey(),
|
|
||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
@ -67,28 +66,24 @@
|
|||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
||||||
->assertSee('Onboarding draft')
|
->assertSee('Onboarding draft')
|
||||||
->assertSee('Verify access')
|
->waitForText('Use existing connection')
|
||||||
->assertSee('Status: Not started')
|
|
||||||
->refresh()
|
->refresh()
|
||||||
->waitForText('Status: Not started')
|
->waitForText('Use existing connection')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
||||||
->assertSee('Verify access')
|
->check('Create new connection')
|
||||||
->assertSee('Status: Not started')
|
->waitForText('Dedicated override')
|
||||||
->click('Select an existing connection or create a new one.')
|
->check('Dedicated override')
|
||||||
->assertSee('Edit selected connection')
|
->waitForText('Dedicated client secret')
|
||||||
->click('Create new connection')
|
|
||||||
->check('internal:label="Dedicated override"s')
|
|
||||||
->fill('[type="password"]', 'browser-only-secret')
|
->fill('[type="password"]', 'browser-only-secret')
|
||||||
->assertValue('[type="password"]', 'browser-only-secret')
|
->assertValue('[type="password"]', 'browser-only-secret')
|
||||||
->refresh()
|
->refresh()
|
||||||
->waitForText('Status: Not started')
|
->waitForText('Use existing connection')
|
||||||
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
->assertRoute('admin.onboarding.draft', ['onboardingDraft' => (int) $draft->getKey()])
|
||||||
->assertSee('Verify access')
|
->check('Create new connection')
|
||||||
->click('Select an existing connection or create a new one.')
|
->waitForText('Dedicated override')
|
||||||
->assertSee('Edit selected connection')
|
->check('Dedicated override')
|
||||||
->click('Create new connection')
|
->waitForText('Dedicated client secret')
|
||||||
->check('internal:label="Dedicated override"s')
|
|
||||||
->assertValue('[type="password"]', '');
|
->assertValue('[type="password"]', '');
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -285,5 +280,6 @@
|
|||||||
$page
|
$page
|
||||||
->wait(7)
|
->wait(7)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Bootstrap completed across 1 operation(s).');
|
->assertSee('Completed - 1 action(s) completed')
|
||||||
|
->assertSee('Complete onboarding');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -53,11 +53,11 @@ function operationActivityFeedbackSmokeLoginUrl(User $user, ManagedEnvironment $
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
$inventoryPage = visit(InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $tenant))
|
$inventoryPage = visit(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant))
|
||||||
->resize(1440, 1200)
|
->resize(1440, 1200)
|
||||||
->assertScript('window.innerWidth >= 1400', true)
|
->assertScript('window.innerWidth >= 1400', true)
|
||||||
->waitForText('Inventory Items')
|
->waitForText('Inventory Items')
|
||||||
@ -183,7 +183,7 @@ function operationActivityFeedbackSmokeLoginUrl(User $user, ManagedEnvironment $
|
|||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
$page = visit(FindingResource::getUrl('index', panel: 'tenant', tenant: $tenant))
|
$page = visit(FindingResource::getUrl('index', panel: 'admin', tenant: $tenant))
|
||||||
->resize(1440, 1200);
|
->resize(1440, 1200);
|
||||||
|
|
||||||
$page
|
$page
|
||||||
@ -252,11 +252,11 @@ function operationActivityFeedbackSmokeLoginUrl(User $user, ManagedEnvironment $
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
visit(InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $tenant))
|
visit(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant))
|
||||||
->resize(1440, 1200)
|
->resize(1440, 1200)
|
||||||
->waitForText('Inventory Items')
|
->waitForText('Inventory Items')
|
||||||
->waitForText('Capturing evidence.')
|
->waitForText('Capturing evidence.')
|
||||||
@ -285,11 +285,11 @@ function operationActivityFeedbackSmokeLoginUrl(User $user, ManagedEnvironment $
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
visit(operationActivityFeedbackSmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
$page = visit(InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $tenant))
|
$page = visit(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant))
|
||||||
->resize(1440, 1200)
|
->resize(1440, 1200)
|
||||||
->waitForText('Inventory Items')
|
->waitForText('Inventory Items')
|
||||||
->waitForText('Acknowledge')
|
->waitForText('Acknowledge')
|
||||||
|
|||||||
@ -48,7 +48,10 @@
|
|||||||
|
|
||||||
visit(OperationRunLinks::tenantlessView($run))
|
visit(OperationRunLinks::tenantlessView($run))
|
||||||
->waitForText(OperationRunLinks::identifier((int) $run->getKey()))
|
->waitForText(OperationRunLinks::identifier((int) $run->getKey()))
|
||||||
->assertRoute('admin.operations.view', ['run' => (int) $run->getKey()])
|
->assertRoute('admin.operations.view', [
|
||||||
|
'workspace' => (int) $run->workspace_id,
|
||||||
|
'run' => (int) $run->getKey(),
|
||||||
|
])
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee(OperationRunLinks::identifier((int) $run->getKey()));
|
->assertSee(OperationRunLinks::identifier((int) $run->getKey()));
|
||||||
});
|
});
|
||||||
@ -3,8 +3,8 @@
|
|||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
use App\Filament\Resources\TenantReviewResource;
|
use App\Filament\Resources\TenantReviewResource;
|
||||||
use App\Models\ReviewPack;
|
|
||||||
use App\Models\ManagedEnvironment;
|
use App\Models\ManagedEnvironment;
|
||||||
|
use App\Models\ReviewPack;
|
||||||
use App\Support\TenantReviewStatus;
|
use App\Support\TenantReviewStatus;
|
||||||
use App\Support\Workspaces\WorkspaceContext;
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
@ -106,7 +106,8 @@
|
|||||||
->waitForText('Published ManagedEnvironment')
|
->waitForText('Published ManagedEnvironment')
|
||||||
->assertDontSee('No Published ManagedEnvironment')
|
->assertDontSee('No Published ManagedEnvironment')
|
||||||
->assertDontSee('No published review available yet')
|
->assertDontSee('No published review available yet')
|
||||||
->click('Review öffnen')
|
->assertSeeIn('tbody tr.fi-ta-row:first-of-type td:last-child', 'Review öffnen')
|
||||||
|
->click('tbody tr.fi-ta-row:first-of-type td:last-child a')
|
||||||
->waitForText('Ergebniszusammenfassung')
|
->waitForText('Ergebniszusammenfassung')
|
||||||
->assertSee('Governance-Paket herunterladen')
|
->assertSee('Governance-Paket herunterladen')
|
||||||
->assertSee('Governance-Paket')
|
->assertSee('Governance-Paket')
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
@ -72,11 +72,16 @@
|
|||||||
|
|
||||||
visit($operationsIndexUrl)
|
visit($operationsIndexUrl)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertRoute('admin.operations.index');
|
->assertRoute('admin.operations.index', [
|
||||||
|
'workspace' => (int) $tenant->workspace_id,
|
||||||
|
]);
|
||||||
|
|
||||||
visit(OperationRunLinks::tenantlessView($run))
|
visit(OperationRunLinks::tenantlessView($run))
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertRoute('admin.operations.view', ['run' => (int) $run->getKey()])
|
->assertRoute('admin.operations.view', [
|
||||||
|
'workspace' => (int) $run->workspace_id,
|
||||||
|
'run' => (int) $run->getKey(),
|
||||||
|
])
|
||||||
->assertSee(OperationRunLinks::identifier((int) $run->getKey()));
|
->assertSee(OperationRunLinks::identifier((int) $run->getKey()));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -84,7 +84,7 @@
|
|||||||
]);
|
]);
|
||||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $staleTenant->workspace_id);
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $staleTenant->workspace_id);
|
||||||
|
|
||||||
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'tenant'))
|
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'admin'))
|
||||||
->waitForText('Outcome summary')
|
->waitForText('Outcome summary')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Stale')
|
->assertSee('Stale')
|
||||||
@ -103,7 +103,7 @@
|
|||||||
->assertSee('Refresh the source review before sharing this pack')
|
->assertSee('Refresh the source review before sharing this pack')
|
||||||
->assertSee('Download');
|
->assertSee('Download');
|
||||||
|
|
||||||
visit(ReviewPackResource::getUrl('view', ['record' => $stalePack], tenant: $staleTenant, panel: 'tenant'))
|
visit(ReviewPackResource::getUrl('view', ['record' => $stalePack], tenant: $staleTenant, panel: 'admin'))
|
||||||
->waitForText('Outcome summary')
|
->waitForText('Outcome summary')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Internal only')
|
->assertSee('Internal only')
|
||||||
|
|||||||
@ -123,11 +123,11 @@ function seedSpec177InventoryItemFilterPaginationFixtures(ManagedEnvironment $te
|
|||||||
]);
|
]);
|
||||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
||||||
|
|
||||||
$coverageUrl = InventoryCoverage::getUrl(panel: 'tenant', tenant: $tenant);
|
$coverageUrl = InventoryCoverage::getUrl(panel: 'admin', tenant: $tenant);
|
||||||
$basisRunUrl = OperationRunLinks::view($run, $tenant);
|
$basisRunUrl = OperationRunLinks::view($run, $tenant);
|
||||||
$inventoryItemsUrl = InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $tenant);
|
$inventoryItemsUrl = InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant);
|
||||||
|
|
||||||
$searchPage = visit(InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $tenant));
|
$searchPage = visit(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $tenant));
|
||||||
|
|
||||||
$searchPage
|
$searchPage
|
||||||
->waitForText('Inventory Items')
|
->waitForText('Inventory Items')
|
||||||
@ -162,7 +162,10 @@ function seedSpec177InventoryItemFilterPaginationFixtures(ManagedEnvironment $te
|
|||||||
visit($basisRunUrl)
|
visit($basisRunUrl)
|
||||||
->waitForText('Operation #'.(int) $run->getKey())
|
->waitForText('Operation #'.(int) $run->getKey())
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertRoute('admin.operations.view', ['run' => (int) $run->getKey()])
|
->assertRoute('admin.operations.view', [
|
||||||
|
'workspace' => (int) $run->workspace_id,
|
||||||
|
'run' => (int) $run->getKey(),
|
||||||
|
])
|
||||||
->assertSee('Inventory sync coverage')
|
->assertSee('Inventory sync coverage')
|
||||||
->assertSee('Need follow-up');
|
->assertSee('Need follow-up');
|
||||||
|
|
||||||
|
|||||||
@ -11,7 +11,7 @@
|
|||||||
|
|
||||||
uses(BuildsBaselineCompareMatrixFixtures::class);
|
uses(BuildsBaselineCompareMatrixFixtures::class);
|
||||||
|
|
||||||
pest()->browser()->timeout(15_000);
|
pest()->browser()->timeout(20_000);
|
||||||
|
|
||||||
it('smokes dense multi-tenant scanning and finding drilldown continuity', function (): void {
|
it('smokes dense multi-tenant scanning and finding drilldown continuity', function (): void {
|
||||||
$fixture = $this->makeBaselineCompareMatrixFixture();
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
||||||
|
|||||||
@ -143,7 +143,7 @@ function spec192ApprovedFindingException(ManagedEnvironment $tenant, User $reque
|
|||||||
->assertSee('Review compare matrix')
|
->assertSee('Review compare matrix')
|
||||||
->assertSee('Compare now');
|
->assertSee('Compare now');
|
||||||
|
|
||||||
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant'))
|
visit(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
||||||
->waitForText('Related context')
|
->waitForText('Related context')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertScript("document.querySelectorAll('[data-supporting-group-kind]').length === 0", true)
|
->assertScript("document.querySelectorAll('[data-supporting-group-kind]').length === 0", true)
|
||||||
@ -266,7 +266,7 @@ function spec192ApprovedFindingException(ManagedEnvironment $tenant, User $reque
|
|||||||
]);
|
]);
|
||||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
||||||
|
|
||||||
visit(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'tenant'))
|
visit(ReviewPackResource::getUrl('view', ['record' => $pack], tenant: $tenant, panel: 'admin'))
|
||||||
->waitForText('Download')
|
->waitForText('Download')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Regenerate');
|
->assertSee('Regenerate');
|
||||||
|
|||||||
@ -73,7 +73,7 @@
|
|||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Quiet monitoring mode');
|
->assertSee('Quiet monitoring mode');
|
||||||
|
|
||||||
visit(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
visit(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Monitoring detail')
|
->assertSee('Monitoring detail')
|
||||||
@ -84,8 +84,10 @@
|
|||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Alert deliveries');
|
->assertSee('Alert deliveries');
|
||||||
|
|
||||||
visit('/admin/t/'.$diagnosticsTenant->external_id.'/diagnostics')
|
visit(\App\Filament\Pages\TenantDashboard::getUrl(tenant: $diagnosticsTenant))
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Missing owner');
|
->assertSee($diagnosticsTenant->name)
|
||||||
|
->click('[aria-label="More"]')
|
||||||
|
->assertSee('Open support diagnostics');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -10,20 +10,20 @@
|
|||||||
use App\Models\EvidenceSnapshot;
|
use App\Models\EvidenceSnapshot;
|
||||||
use App\Models\Finding;
|
use App\Models\Finding;
|
||||||
use App\Models\FindingException;
|
use App\Models\FindingException;
|
||||||
|
use App\Models\ManagedEnvironment;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\PlatformUser;
|
use App\Models\PlatformUser;
|
||||||
use App\Models\ReviewPack;
|
use App\Models\ReviewPack;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Findings\FindingExceptionService;
|
use App\Services\Findings\FindingExceptionService;
|
||||||
|
use App\Support\Auth\PlatformCapabilities;
|
||||||
use App\Support\Evidence\EvidenceCompletenessState;
|
use App\Support\Evidence\EvidenceCompletenessState;
|
||||||
use App\Support\Evidence\EvidenceSnapshotStatus;
|
use App\Support\Evidence\EvidenceSnapshotStatus;
|
||||||
use App\Support\OperationRunOutcome;
|
use App\Support\OperationRunOutcome;
|
||||||
use App\Support\OperationRunStatus;
|
use App\Support\OperationRunStatus;
|
||||||
|
use App\Support\System\SystemOperationRunLinks;
|
||||||
use App\Support\TenantReviewCompletenessState;
|
use App\Support\TenantReviewCompletenessState;
|
||||||
use App\Support\TenantReviewStatus;
|
use App\Support\TenantReviewStatus;
|
||||||
use App\Support\Auth\PlatformCapabilities;
|
|
||||||
use App\Support\System\SystemOperationRunLinks;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
|
|
||||||
pest()->browser()->timeout(20_000);
|
pest()->browser()->timeout(20_000);
|
||||||
@ -65,6 +65,8 @@ function spec194ApprovedFindingException(ManagedEnvironment $tenant, User $reque
|
|||||||
|
|
||||||
function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $redirect = ''): string
|
function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $redirect = ''): string
|
||||||
{
|
{
|
||||||
|
$redirect = spec194RelativeRedirect($redirect);
|
||||||
|
|
||||||
return route('admin.local.smoke-login', array_filter([
|
return route('admin.local.smoke-login', array_filter([
|
||||||
'email' => $user->email,
|
'email' => $user->email,
|
||||||
'tenant' => $tenant->external_id,
|
'tenant' => $tenant->external_id,
|
||||||
@ -73,11 +75,31 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
], static fn (?string $value): bool => filled($value)));
|
], static fn (?string $value): bool => filled($value)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function spec194RelativeRedirect(string $redirect): string
|
||||||
|
{
|
||||||
|
$redirect = trim($redirect);
|
||||||
|
|
||||||
|
if ($redirect === '') {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
$parts = parse_url($redirect);
|
||||||
|
|
||||||
|
if ($parts === false || ! isset($parts['path'])) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return $parts['path']
|
||||||
|
.(isset($parts['query']) ? '?'.$parts['query'] : '')
|
||||||
|
.(isset($parts['fragment']) ? '#'.$parts['fragment'] : '');
|
||||||
|
}
|
||||||
|
|
||||||
it('smokes tenant and admin governance semantics through modal entry points', function (): void {
|
it('smokes tenant and admin governance semantics through modal entry points', function (): void {
|
||||||
[$user, $tenant] = createUserWithTenant(
|
[$user, $tenant] = createUserWithTenant(
|
||||||
role: 'owner',
|
role: 'owner',
|
||||||
workspaceRole: 'manager',
|
workspaceRole: 'owner',
|
||||||
ensureDefaultMicrosoftProviderConnection: false,
|
ensureDefaultMicrosoftProviderConnection: false,
|
||||||
|
clearCapabilityCaches: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
$finding = Finding::factory()->for($tenant)->create();
|
$finding = Finding::factory()->for($tenant)->create();
|
||||||
@ -147,12 +169,13 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
tenant: $archivedTenant,
|
tenant: $archivedTenant,
|
||||||
user: $user,
|
user: $user,
|
||||||
role: 'owner',
|
role: 'owner',
|
||||||
workspaceRole: 'manager',
|
workspaceRole: 'owner',
|
||||||
ensureDefaultMicrosoftProviderConnection: false,
|
ensureDefaultMicrosoftProviderConnection: false,
|
||||||
|
clearCapabilityCaches: true,
|
||||||
);
|
);
|
||||||
|
|
||||||
visit(spec194SmokeLoginUrl($user, $tenant))
|
visit(spec194SmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
@ -177,14 +200,14 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->click('Publish review')
|
->click('Publish review')
|
||||||
->waitForText('Publication reason')
|
->waitForText('Publication reason')
|
||||||
->click('Cancel')
|
->click('button:has-text("Cancel")')
|
||||||
->click('[aria-label="More"]')
|
->click('[aria-label="More"]')
|
||||||
->assertSee('Refresh review')
|
->assertSee('Refresh review')
|
||||||
->assertSee('Export executive pack')
|
->assertSee('Export executive pack')
|
||||||
->click('[aria-label="Danger"]')
|
->click('[aria-label="Danger"]')
|
||||||
->click('Archive review')
|
->click('Archive review')
|
||||||
->waitForText('Archive reason')
|
->waitForText('Archive reason')
|
||||||
->click('Cancel')
|
->click('button:has-text("Cancel")')
|
||||||
->assertSee('Publish review')
|
->assertSee('Publish review')
|
||||||
->assertSee('Evidence snapshot');
|
->assertSee('Evidence snapshot');
|
||||||
|
|
||||||
@ -194,10 +217,10 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->click('Refresh evidence')
|
->click('Refresh evidence')
|
||||||
->waitForText('Confirm')
|
->waitForText('Confirm')
|
||||||
->click('Cancel')
|
->click('button:has-text("Cancel")')
|
||||||
->click('Expire snapshot')
|
->click('Expire snapshot')
|
||||||
->waitForText('Expiry reason')
|
->waitForText('Expiry reason')
|
||||||
->click('Cancel')
|
->click('button:has-text("Cancel")')
|
||||||
->assertSee('Refresh evidence')
|
->assertSee('Refresh evidence')
|
||||||
->assertSee('Expire snapshot');
|
->assertSee('Expire snapshot');
|
||||||
|
|
||||||
@ -206,9 +229,10 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->click('[aria-label="Lifecycle"]')
|
->click('[aria-label="Lifecycle"]')
|
||||||
->click('Archive')
|
->waitForText('Archive')
|
||||||
|
->click('button:has-text("Archive")')
|
||||||
->waitForText('Archive reason')
|
->waitForText('Archive reason')
|
||||||
->click('Cancel')
|
->click('button:has-text("Cancel")')
|
||||||
->assertSee('Lifecycle');
|
->assertSee('Lifecycle');
|
||||||
|
|
||||||
visit(TenantResource::getUrl('edit', ['record' => $tenant], panel: 'admin'))
|
visit(TenantResource::getUrl('edit', ['record' => $tenant], panel: 'admin'))
|
||||||
@ -217,13 +241,21 @@ function spec194SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Lifecycle');
|
->assertSee('Lifecycle');
|
||||||
|
|
||||||
visit(TenantResource::getUrl('view', ['record' => $archivedTenant], panel: 'admin'))
|
visit(spec194SmokeLoginUrl(
|
||||||
|
$user,
|
||||||
|
$archivedTenant,
|
||||||
|
TenantResource::getUrl('view', ['record' => $archivedTenant], panel: 'admin'),
|
||||||
|
))
|
||||||
->waitForText('Related context')
|
->waitForText('Related context')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
->assertSee('Lifecycle');
|
->assertSee('Lifecycle');
|
||||||
|
|
||||||
visit(TenantResource::getUrl('edit', ['record' => $archivedTenant], panel: 'admin'))
|
visit(spec194SmokeLoginUrl(
|
||||||
|
$user,
|
||||||
|
$archivedTenant,
|
||||||
|
TenantResource::getUrl('edit', ['record' => $archivedTenant], panel: 'admin'),
|
||||||
|
))
|
||||||
->waitForText('Related context')
|
->waitForText('Related context')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
|
|||||||
@ -95,6 +95,7 @@
|
|||||||
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
||||||
|
|
||||||
visit(route('admin.operations.index', [
|
visit(route('admin.operations.index', [
|
||||||
|
'workspace' => $tenant->workspace,
|
||||||
'managed_environment_id' => (int) $tenant->getKey(),
|
'managed_environment_id' => (int) $tenant->getKey(),
|
||||||
'activeTab' => 'active',
|
'activeTab' => 'active',
|
||||||
]))
|
]))
|
||||||
@ -151,8 +152,14 @@
|
|||||||
'baseline_profile_id' => (int) $profile->getKey(),
|
'baseline_profile_id' => (int) $profile->getKey(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user)->withSession([
|
||||||
$tenant->makeCurrent();
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
|
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
|
||||||
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
||||||
|
],
|
||||||
|
]);
|
||||||
|
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
|
||||||
|
setAdminPanelContext($tenant);
|
||||||
|
|
||||||
$matrixUrl = BaselineProfileResource::compareMatrixUrl($profile).'?subject_key='.urlencode($subjectKey);
|
$matrixUrl = BaselineProfileResource::compareMatrixUrl($profile).'?subject_key='.urlencode($subjectKey);
|
||||||
|
|
||||||
@ -161,11 +168,11 @@
|
|||||||
'baseline_profile_id' => (int) $profile->getKey(),
|
'baseline_profile_id' => (int) $profile->getKey(),
|
||||||
'subject_key' => $subjectKey,
|
'subject_key' => $subjectKey,
|
||||||
],
|
],
|
||||||
panel: 'tenant',
|
panel: 'admin',
|
||||||
tenant: $tenant,
|
tenant: $tenant,
|
||||||
))
|
))
|
||||||
->waitForText('Open compare matrix')
|
->waitForText('Open compare matrix')
|
||||||
->assertSee('Launch the compare matrix with the currently known baseline profile and any carried subject focus from this tenant landing.');
|
->assertSee('Launch the compare matrix with the currently known baseline profile and any carried subject focus from this environment landing.');
|
||||||
|
|
||||||
visit($matrixUrl)
|
visit($matrixUrl)
|
||||||
->waitForText('Focused subject')
|
->waitForText('Focused subject')
|
||||||
|
|||||||
@ -73,7 +73,7 @@ function spec202SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(spec202SmokeLoginUrl($user, $tenant))
|
visit(spec202SmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
@ -148,7 +148,7 @@ function spec202SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(spec202SmokeLoginUrl($user, $tenant))
|
visit(spec202SmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
|
|||||||
@ -71,7 +71,7 @@ function spec265SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
visit(spec265SmokeLoginUrl($user, $tenant))
|
visit(spec265SmokeLoginUrl($user, $tenant))
|
||||||
->waitForText('Dashboard')
|
->waitForText($tenant->name)
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
|
|
||||||
@ -82,7 +82,8 @@ function spec265SmokeLoginUrl(User $user, ManagedEnvironment $tenant, string $re
|
|||||||
->assertSee('The register is currently filtered to one tenant.')
|
->assertSee('The register is currently filtered to one tenant.')
|
||||||
->assertSee($tenant->name)
|
->assertSee($tenant->name)
|
||||||
->assertSee('Showing 1 result')
|
->assertSee('Showing 1 result')
|
||||||
->click('tbody tr.fi-ta-row')
|
->assertSeeIn('tbody tr.fi-ta-row:first-of-type', $tenant->name)
|
||||||
|
->click('tbody tr.fi-ta-row:first-of-type')
|
||||||
->waitForText('Opened from the workspace decision register')
|
->waitForText('Opened from the workspace decision register')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs()
|
->assertNoConsoleLogs()
|
||||||
|
|||||||
@ -41,7 +41,7 @@
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
visit(StoredReportResource::getUrl('index', tenant: $tenant, panel: 'tenant'))
|
visit(StoredReportResource::getUrl('index', tenant: $tenant, panel: 'admin'))
|
||||||
->waitForText('Stored reports')
|
->waitForText('Stored reports')
|
||||||
->assertSee('Permission posture report')
|
->assertSee('Permission posture report')
|
||||||
->assertSee('Current')
|
->assertSee('Current')
|
||||||
|
|||||||
@ -35,7 +35,7 @@
|
|||||||
->assertSee('Active')
|
->assertSee('Active')
|
||||||
->click('Spec 279 Production')
|
->click('Spec 279 Production')
|
||||||
->waitForText('Spec 279 Production')
|
->waitForText('Spec 279 Production')
|
||||||
->assertPathContains('/admin/t/spec-279-production')
|
->assertPathContains('/admin/workspaces/'.$environment->workspace->slug.'/environments/spec-279-production')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertNoConsoleLogs();
|
->assertNoConsoleLogs();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -14,9 +14,7 @@
|
|||||||
$member = User::factory()->create([
|
$member = User::factory()->create([
|
||||||
'email' => 'browser-tenant-member@example.test',
|
'email' => 'browser-tenant-member@example.test',
|
||||||
]);
|
]);
|
||||||
$member->tenants()->syncWithoutDetaching([
|
createUserWithTenant(tenant: $tenant, user: $member, role: 'readonly');
|
||||||
$tenant->getKey() => ['role' => 'readonly'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
$this->actingAs($owner)->withSession([
|
$this->actingAs($owner)->withSession([
|
||||||
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
@ -28,28 +26,27 @@
|
|||||||
$viewPage
|
$viewPage
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee((string) $tenant->name)
|
->assertSee((string) $tenant->name)
|
||||||
->assertSee('Manage memberships')
|
->assertSee('Manage access scope')
|
||||||
->assertScript("document.body.innerText.includes('Add member')", false)
|
->assertScript("document.body.innerText.includes('Add member')", false)
|
||||||
->assertScript("document.body.innerText.includes('browser-tenant-member@example.test')", false);
|
->assertScript("document.body.innerText.includes('browser-tenant-member@example.test')", false);
|
||||||
|
|
||||||
$membershipsPage = $viewPage->click('Manage memberships');
|
$membershipsPage = $viewPage->click('Manage access scope');
|
||||||
|
|
||||||
$membershipsPage
|
$membershipsPage
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Manage tenant memberships')
|
->assertSee('Manage environment access scope')
|
||||||
->assertSee('Back to tenant overview')
|
->assertSee('Back to environment overview')
|
||||||
->assertSee('ManagedEnvironment access is managed here. Use the tenant overview for provider state, verification, and operational context.');
|
->assertSee('Workspace membership defines the role. Explicit environment scopes only narrow which workspace members can see this environment.');
|
||||||
|
|
||||||
$membershipsPage->script(<<<'JS'
|
$membershipsPage->script(<<<'JS'
|
||||||
window.scrollTo(0, document.body.scrollHeight);
|
window.scrollTo(0, document.body.scrollHeight);
|
||||||
JS);
|
JS);
|
||||||
|
|
||||||
$membershipsPage
|
$membershipsPage
|
||||||
->waitForText('Add member')
|
->waitForText('Add explicit access scope')
|
||||||
->assertNoJavaScriptErrors()
|
->assertNoJavaScriptErrors()
|
||||||
->assertSee('Manage tenant memberships')
|
->assertSee('Manage environment access scope')
|
||||||
->assertSee('Add member')
|
->assertSee('Add explicit access scope')
|
||||||
->assertSee('browser-tenant-member@example.test')
|
->assertSee('browser-tenant-member@example.test')
|
||||||
->assertSee('Change role')
|
->assertSee('Remove explicit scope');
|
||||||
->assertSee('Remove');
|
|
||||||
});
|
});
|
||||||
|
|||||||
@ -44,7 +44,7 @@ public function test_renders_canonical_detail_for_a_workspace_member_when_tenant
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee(\App\Support\OperationRunLinks::identifier($run))
|
->assertSee(\App\Support\OperationRunLinks::identifier($run))
|
||||||
->assertSee('Policy sync')
|
->assertSee('Policy sync')
|
||||||
@ -70,7 +70,7 @@ public function test_renders_canonical_detail_gracefully_when_tenant_id_is_null(
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('No target scope details were recorded for this operation.');
|
->assertSee('No target scope details were recorded for this operation.');
|
||||||
}
|
}
|
||||||
@ -89,7 +89,7 @@ public function test_returns_404_on_canonical_detail_for_non_members(): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($otherUser)
|
$this->actingAs($otherUser)
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -118,7 +118,7 @@ public function test_renders_canonical_detail_db_only_with_no_job_dispatch(): vo
|
|||||||
|
|
||||||
assertNoOutboundHttp(function () use ($user, $run): void {
|
assertNoOutboundHttp(function () use ($user, $run): void {
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Verification report');
|
->assertSee('Verification report');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -25,7 +25,7 @@ public function test_hides_operations_kpi_stats_when_tenant_context_is_absent():
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.index'))
|
->get(\App\Support\OperationRunLinks::index())
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertDontSee('Total Operations (30 days)')
|
->assertDontSee('Total Operations (30 days)')
|
||||||
->assertDontSee('Active Operations')
|
->assertDontSee('Active Operations')
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public function test_renders_workspace_operations_list_with_tenantless_runs_when
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.index'))
|
->get(\App\Support\OperationRunLinks::index())
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Tenantless run')
|
->assertSee('Tenantless run')
|
||||||
->assertSee('ManagedEnvironment run');
|
->assertSee('ManagedEnvironment run');
|
||||||
@ -70,7 +70,7 @@ public function test_renders_workspace_operations_list_workspace_wide_even_with_
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.index'))
|
->get(\App\Support\OperationRunLinks::index())
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('ManagedEnvironment run')
|
->assertSee('ManagedEnvironment run')
|
||||||
->assertSee('Tenantless run');
|
->assertSee('Tenantless run');
|
||||||
|
|||||||
@ -38,7 +38,7 @@ public function test_shows_restore_related_links_on_canonical_detail_for_restore
|
|||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Open')
|
->assertSee('Open')
|
||||||
->assertSee('View restore run');
|
->assertSee('View restore run');
|
||||||
@ -61,7 +61,7 @@ public function test_shows_only_generic_links_for_tenantless_runs_on_canonical_d
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Operations')
|
->assertSee('Operations')
|
||||||
->assertSee(OperationRunLinks::index(), false)
|
->assertSee(OperationRunLinks::index(), false)
|
||||||
@ -80,7 +80,7 @@ public function test_does_not_show_legacy_admin_details_cta_and_keeps_canonical_
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertDontSee('Admin details')
|
->assertDontSee('Admin details')
|
||||||
->assertDontSee('/admin/t/'.$tenant->external_id.'/operations/r/'.$run->getKey(), false);
|
->assertDontSee('/admin/t/'.$tenant->external_id.'/operations/r/'.$run->getKey(), false);
|
||||||
@ -89,7 +89,7 @@ public function test_does_not_show_legacy_admin_details_cta_and_keeps_canonical_
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.index'))
|
->get(OperationRunLinks::index())
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Open run detail');
|
->assertSee('Open run detail');
|
||||||
}
|
}
|
||||||
@ -122,11 +122,11 @@ public function test_hides_tenant_scoped_follow_up_links_when_the_run_tenant_is_
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $activeTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $activeTenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Open')
|
->assertSee('Open')
|
||||||
->assertSee('Operations')
|
->assertSee('Operations')
|
||||||
->assertDontSee(InventoryItemResource::getUrl('index', panel: 'tenant', tenant: $onboardingTenant), false)
|
->assertDontSee(InventoryItemResource::getUrl('index', panel: 'admin', tenant: $onboardingTenant), false)
|
||||||
->assertSee('Some tenant follow-up actions may be unavailable from this canonical workspace view.');
|
->assertSee('Some tenant follow-up actions may be unavailable from this canonical workspace view.');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -53,12 +53,12 @@ public function test_renders_verification_report_on_canonical_detail_without_fil
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Verification report')
|
->assertSee('Verification report')
|
||||||
->assertDontSee('Verification report unavailable')
|
->assertDontSee('Verification report unavailable')
|
||||||
->assertSee('Open previous operation')
|
->assertSee('Open previous operation')
|
||||||
->assertSee('/admin/operations/'.((int) $previousRun->getKey()), false)
|
->assertSee(\App\Support\OperationRunLinks::tenantlessView($previousRun), false)
|
||||||
->assertSee('Token acquisition works');
|
->assertSee('Token acquisition works');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
use App\Support\OperationRunOutcome;
|
use App\Support\OperationRunOutcome;
|
||||||
use App\Support\OperationRunStatus;
|
use App\Support\OperationRunStatus;
|
||||||
use App\Support\Workspaces\WorkspaceContext;
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@ -38,15 +37,15 @@ public function test_shows_non_blocking_mismatch_context_when_the_selected_tenan
|
|||||||
'outcome' => OperationRunOutcome::Succeeded->value,
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant($currentTenant, true);
|
setAdminPanelContext($currentTenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Current tenant context differs from this operation')
|
->assertSee('Current environment context differs from this operation')
|
||||||
->assertSee('Current tenant context: Current ManagedEnvironment.')
|
->assertSee('Current environment context: Current ManagedEnvironment.')
|
||||||
->assertSee('Operation tenant: Run ManagedEnvironment.')
|
->assertSee('Operation environment: Run ManagedEnvironment.')
|
||||||
->assertSee('canonical workspace view');
|
->assertSee('canonical workspace view');
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -65,14 +64,14 @@ public function test_frames_tenantless_runs_as_workspace_level_even_when_tenant_
|
|||||||
'outcome' => OperationRunOutcome::Succeeded->value,
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant($selectedTenant, true);
|
setAdminPanelContext($selectedTenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $selectedTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $selectedTenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Workspace-level operation')
|
->assertSee('Workspace-level operation')
|
||||||
->assertSee('This canonical workspace view is not tied to the current tenant context (Selected ManagedEnvironment).');
|
->assertSee('This canonical workspace view is not tied to the current environment context (Selected ManagedEnvironment).');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_keeps_onboarding_tenant_runs_viewable_with_lifecycle_aware_context(): void
|
public function test_keeps_onboarding_tenant_runs_viewable_with_lifecycle_aware_context(): void
|
||||||
@ -91,14 +90,14 @@ public function test_keeps_onboarding_tenant_runs_viewable_with_lifecycle_aware_
|
|||||||
'outcome' => OperationRunOutcome::Succeeded->value,
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant(null, true);
|
setAdminPanelContext();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Operation tenant is not available in the current tenant selector')
|
->assertSee('Operation environment is not available in the current environment selector')
|
||||||
->assertSee('Operation tenant: Onboarding ManagedEnvironment.')
|
->assertSee('Operation environment: Onboarding ManagedEnvironment.')
|
||||||
->assertSee('This tenant is currently onboarding')
|
->assertSee('This tenant is currently onboarding')
|
||||||
->assertSee('Back to Operations')
|
->assertSee('Back to Operations')
|
||||||
->assertDontSee('This tenant is currently active')
|
->assertDontSee('This tenant is currently active')
|
||||||
@ -129,14 +128,14 @@ public function test_keeps_archived_tenant_runs_viewable_with_lifecycle_aware_co
|
|||||||
'outcome' => OperationRunOutcome::Succeeded->value,
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant(null, true);
|
setAdminPanelContext();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $activeTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $activeTenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Operation tenant is not available in the current tenant selector')
|
->assertSee('Operation environment is not available in the current environment selector')
|
||||||
->assertSee('Operation tenant: Archived ManagedEnvironment.')
|
->assertSee('Operation environment: Archived ManagedEnvironment.')
|
||||||
->assertSee('This tenant is currently archived')
|
->assertSee('This tenant is currently archived')
|
||||||
->assertSee('Back to Operations')
|
->assertSee('Back to Operations')
|
||||||
->assertDontSee('deactivated')
|
->assertDontSee('deactivated')
|
||||||
@ -159,14 +158,14 @@ public function test_keeps_selector_excluded_draft_tenant_runs_viewable_with_lif
|
|||||||
'outcome' => OperationRunOutcome::Succeeded->value,
|
'outcome' => OperationRunOutcome::Succeeded->value,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant(null, true);
|
setAdminPanelContext();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Operation tenant is not available in the current tenant selector')
|
->assertSee('Operation environment is not available in the current environment selector')
|
||||||
->assertSee('Operation tenant: Draft ManagedEnvironment.')
|
->assertSee('Operation environment: Draft ManagedEnvironment.')
|
||||||
->assertSee('This tenant is currently draft')
|
->assertSee('This tenant is currently draft')
|
||||||
->assertDontSee('Resume onboarding');
|
->assertDontSee('Resume onboarding');
|
||||||
}
|
}
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
use App\Support\Navigation\CanonicalNavigationContext;
|
use App\Support\Navigation\CanonicalNavigationContext;
|
||||||
use App\Support\OperationRunLinks;
|
use App\Support\OperationRunLinks;
|
||||||
use App\Support\Workspaces\WorkspaceContext;
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Tests\TestCase;
|
use Tests\TestCase;
|
||||||
|
|
||||||
@ -43,7 +42,7 @@ public function test_trusts_canonical_run_links_opened_from_a_tenant_surface_aft
|
|||||||
backLinkUrl: route('filament.admin.resources.tenants.view', ['record' => $runTenant]),
|
backLinkUrl: route('filament.admin.resources.tenants.view', ['record' => $runTenant]),
|
||||||
);
|
);
|
||||||
|
|
||||||
Filament::setTenant($otherTenant, true);
|
setAdminPanelContext($otherTenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
||||||
@ -51,7 +50,7 @@ public function test_trusts_canonical_run_links_opened_from_a_tenant_surface_aft
|
|||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Back to tenant')
|
->assertSee('Back to tenant')
|
||||||
->assertSee(route('filament.admin.resources.tenants.view', ['record' => $runTenant]), false)
|
->assertSee(route('filament.admin.resources.tenants.view', ['record' => $runTenant]), false)
|
||||||
->assertSee('Current tenant context differs from this operation');
|
->assertSee('Current environment context differs from this operation');
|
||||||
}
|
}
|
||||||
|
|
||||||
public function test_trusts_notification_style_run_links_with_no_selected_tenant_context(): void
|
public function test_trusts_notification_style_run_links_with_no_selected_tenant_context(): void
|
||||||
@ -65,7 +64,7 @@ public function test_trusts_notification_style_run_links_with_no_selected_tenant
|
|||||||
'type' => 'inventory_sync',
|
'type' => 'inventory_sync',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant(null, true);
|
setAdminPanelContext();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
@ -92,7 +91,7 @@ public function test_uses_canonical_collection_link_for_default_back_and_show_al
|
|||||||
'type' => 'inventory_sync',
|
'type' => 'inventory_sync',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setTenant($otherTenant, true);
|
setAdminPanelContext($otherTenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $runTenant->workspace_id])
|
||||||
@ -130,7 +129,7 @@ public function test_trusts_verification_surface_run_links_with_no_selected_tena
|
|||||||
backLinkUrl: '/admin/verification/report',
|
backLinkUrl: '/admin/verification/report',
|
||||||
);
|
);
|
||||||
|
|
||||||
Filament::setTenant(null, true);
|
setAdminPanelContext();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
|
|||||||
@ -27,6 +27,11 @@
|
|||||||
'user_id' => $nonMember->getKey(),
|
'user_id' => $nonMember->getKey(),
|
||||||
'role' => 'owner',
|
'role' => 'owner',
|
||||||
]);
|
]);
|
||||||
|
createUserWithTenant(
|
||||||
|
tenant: ManagedEnvironment::factory()->create(['workspace_id' => (int) $workspace->getKey()]),
|
||||||
|
user: $nonMember,
|
||||||
|
role: 'owner',
|
||||||
|
);
|
||||||
|
|
||||||
$this->actingAs($nonMember)
|
$this->actingAs($nonMember)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
|
|||||||
@ -61,6 +61,14 @@
|
|||||||
'role' => 'owner',
|
'role' => 'owner',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
createUserWithTenant(
|
||||||
|
tenant: ManagedEnvironment::factory()->archived()->create([
|
||||||
|
'workspace_id' => (int) $workspace->getKey(),
|
||||||
|
]),
|
||||||
|
user: $user,
|
||||||
|
role: 'owner',
|
||||||
|
);
|
||||||
|
|
||||||
ManagedEnvironment::factory()->create([
|
ManagedEnvironment::factory()->create([
|
||||||
'workspace_id' => (int) $workspace->getKey(),
|
'workspace_id' => (int) $workspace->getKey(),
|
||||||
'status' => 'active',
|
'status' => 'active',
|
||||||
@ -104,7 +112,7 @@
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
||||||
->get(FindingResource::getUrl('view', ['record' => $hiddenFinding], panel: 'tenant', tenant: $hiddenTenant))
|
->get(FindingResource::getUrl('view', ['record' => $hiddenFinding], panel: 'admin', tenant: $hiddenTenant))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -133,6 +141,6 @@
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'tenant', tenant: $tenant))
|
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -126,7 +126,7 @@
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $visibleTenant->workspace_id])
|
||||||
->get(FindingResource::getUrl('view', ['record' => $hiddenFinding], panel: 'tenant', tenant: $hiddenTenant))
|
->get(FindingResource::getUrl('view', ['record' => $hiddenFinding], panel: 'admin', tenant: $hiddenTenant))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -166,6 +166,6 @@
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'tenant', tenant: $tenant))
|
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
|
||||||
->assertForbidden();
|
->assertForbidden();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -25,7 +25,7 @@
|
|||||||
$nonMember = User::factory()->create();
|
$nonMember = User::factory()->create();
|
||||||
|
|
||||||
$this->actingAs($nonMember)
|
$this->actingAs($nonMember)
|
||||||
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'tenant'))
|
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -42,7 +42,10 @@
|
|||||||
|
|
||||||
$this->actingAs($readonly)
|
$this->actingAs($readonly)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(route('admin.operations.view', [
|
||||||
|
'workspace' => $tenant->workspace,
|
||||||
|
'run' => (int) $run->getKey(),
|
||||||
|
]))
|
||||||
->assertForbidden();
|
->assertForbidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -180,6 +183,15 @@
|
|||||||
'role' => 'owner',
|
'role' => 'owner',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
$visibleTenant = ManagedEnvironment::factory()->create([
|
||||||
|
'workspace_id' => (int) $workspace->getKey(),
|
||||||
|
'status' => 'active',
|
||||||
|
]);
|
||||||
|
|
||||||
|
$user->tenants()->syncWithoutDetaching([
|
||||||
|
$visibleTenant->getKey() => ['role' => 'owner'],
|
||||||
|
]);
|
||||||
|
|
||||||
$run = OperationRun::factory()->create([
|
$run = OperationRun::factory()->create([
|
||||||
'managed_environment_id' => (int) $tenant->getKey(),
|
'managed_environment_id' => (int) $tenant->getKey(),
|
||||||
'workspace_id' => (int) $workspace->getKey(),
|
'workspace_id' => (int) $workspace->getKey(),
|
||||||
@ -191,6 +203,9 @@
|
|||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(route('admin.operations.view', [
|
||||||
|
'workspace' => $workspace,
|
||||||
|
'run' => (int) $run->getKey(),
|
||||||
|
]))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -38,7 +38,7 @@
|
|||||||
->withSession([
|
->withSession([
|
||||||
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
])
|
])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertSee('Permission required')
|
->assertSee('Permission required')
|
||||||
->assertSee('The initiating actor no longer has the capability required for this queued run.')
|
->assertSee('The initiating actor no longer has the capability required for this queued run.')
|
||||||
@ -77,6 +77,6 @@
|
|||||||
->withSession([
|
->withSession([
|
||||||
WorkspaceContext::SESSION_KEY => (int) $hiddenTenant->workspace_id,
|
WorkspaceContext::SESSION_KEY => (int) $hiddenTenant->workspace_id,
|
||||||
])
|
])
|
||||||
->get(route('admin.operations.view', ['run' => (int) $run->getKey()]))
|
->get(\App\Support\OperationRunLinks::tenantlessView($run))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -56,10 +56,10 @@
|
|||||||
];
|
];
|
||||||
|
|
||||||
$this->withSession($session)
|
$this->withSession($session)
|
||||||
->get(BackupScheduleResource::getUrl('edit', ['record' => $allowed], panel: 'admin'))
|
->get(BackupScheduleResource::getUrl('edit', ['record' => $allowed], panel: 'admin', tenant: $tenantA))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
|
|
||||||
$this->withSession($session)
|
$this->withSession($session)
|
||||||
->get(BackupScheduleResource::getUrl('edit', ['record' => $blocked], panel: 'admin'))
|
->get(BackupScheduleResource::getUrl('edit', ['record' => $blocked], panel: 'admin', tenant: $tenantA))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
use App\Filament\Resources\BackupScheduleResource\Pages\CreateBackupSchedule;
|
use App\Filament\Resources\BackupScheduleResource\Pages\CreateBackupSchedule;
|
||||||
use App\Filament\Resources\BackupScheduleResource\Pages\EditBackupSchedule;
|
use App\Filament\Resources\BackupScheduleResource\Pages\EditBackupSchedule;
|
||||||
|
use App\Filament\Resources\BackupScheduleResource;
|
||||||
use App\Models\BackupSchedule;
|
use App\Models\BackupSchedule;
|
||||||
use App\Models\ManagedEnvironment;
|
use App\Models\ManagedEnvironment;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
@ -52,7 +53,7 @@
|
|||||||
// workspace matches the tenant we are about to access.
|
// workspace matches the tenant we are about to access.
|
||||||
session()->put(\App\Support\Workspaces\WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
session()->put(\App\Support\Workspaces\WorkspaceContext::SESSION_KEY, (int) $tenantA->workspace_id);
|
||||||
|
|
||||||
$this->get(route('filament.admin.resources.backup-schedules.index', filamentTenantRouteParams($tenantA)))
|
$this->get(BackupScheduleResource::getUrl('index', tenant: $tenantA))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('ManagedEnvironment A schedule')
|
->assertSee('ManagedEnvironment A schedule')
|
||||||
->assertSee('Device Configuration')
|
->assertSee('Device Configuration')
|
||||||
@ -79,7 +80,7 @@
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$this->get(route('filament.admin.resources.backup-schedules.index', filamentTenantRouteParams($tenant)))
|
$this->get(BackupScheduleResource::getUrl('index', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Jan 5, 2026 10:17:00');
|
->assertSee('Jan 5, 2026 10:17:00');
|
||||||
});
|
});
|
||||||
@ -89,7 +90,7 @@
|
|||||||
$unauthorizedTenant = ManagedEnvironment::factory()->create();
|
$unauthorizedTenant = ManagedEnvironment::factory()->create();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(route('filament.admin.resources.backup-schedules.index', filamentTenantRouteParams($unauthorizedTenant)))
|
->get(BackupScheduleResource::getUrl('index', tenant: $unauthorizedTenant))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -167,7 +168,7 @@
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$this->get(route('filament.admin.resources.backup-schedules.index', filamentTenantRouteParams($tenant)))
|
$this->get(BackupScheduleResource::getUrl('index', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Active schedule')
|
->assertSee('Active schedule')
|
||||||
->assertDontSee('Archived schedule');
|
->assertDontSee('Archived schedule');
|
||||||
|
|||||||
@ -359,7 +359,7 @@ function makeBackupScheduleForLifecycle(\App\Models\ManagedEnvironment $tenant,
|
|||||||
|
|
||||||
$this->get(BackupScheduleResource::getUrl('index', [
|
$this->get(BackupScheduleResource::getUrl('index', [
|
||||||
'backup_health_reason' => TenantBackupHealthAssessment::REASON_SCHEDULE_FOLLOW_UP,
|
'backup_health_reason' => TenantBackupHealthAssessment::REASON_SCHEDULE_FOLLOW_UP,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('not produced a successful run yet')
|
->assertSee('not produced a successful run yet')
|
||||||
->assertSee($schedule->name)
|
->assertSee($schedule->name)
|
||||||
|
|||||||
@ -54,9 +54,9 @@
|
|||||||
$result = $service->startCompareForVisibleAssignments($fixture['profile'], $fixture['user']);
|
$result = $service->startCompareForVisibleAssignments($fixture['profile'], $fixture['user']);
|
||||||
|
|
||||||
expect($result['visibleAssignedTenantCount'])->toBe(3)
|
expect($result['visibleAssignedTenantCount'])->toBe(3)
|
||||||
->and($result['queuedCount'])->toBe(1)
|
->and($result['queuedCount'])->toBe(2)
|
||||||
->and($result['alreadyQueuedCount'])->toBe(1)
|
->and($result['alreadyQueuedCount'])->toBe(1)
|
||||||
->and($result['blockedCount'])->toBe(1);
|
->and($result['blockedCount'])->toBe(0);
|
||||||
|
|
||||||
$launchStates = collect($result['targets'])
|
$launchStates = collect($result['targets'])
|
||||||
->mapWithKeys(static fn (array $target): array => [(int) $target['tenantId'] => (string) $target['launchState']])
|
->mapWithKeys(static fn (array $target): array => [(int) $target['tenantId'] => (string) $target['launchState']])
|
||||||
@ -64,7 +64,7 @@
|
|||||||
|
|
||||||
expect($launchStates[(int) $fixture['visibleTenant']->getKey()] ?? null)->toBe('queued')
|
expect($launchStates[(int) $fixture['visibleTenant']->getKey()] ?? null)->toBe('queued')
|
||||||
->and($launchStates[(int) $fixture['visibleTenantTwo']->getKey()] ?? null)->toBe('already_queued')
|
->and($launchStates[(int) $fixture['visibleTenantTwo']->getKey()] ?? null)->toBe('already_queued')
|
||||||
->and($launchStates[(int) $readonlyTenant->getKey()] ?? null)->toBe('blocked');
|
->and($launchStates[(int) $readonlyTenant->getKey()] ?? null)->toBe('queued');
|
||||||
|
|
||||||
Queue::assertPushed(CompareBaselineToTenantJob::class);
|
Queue::assertPushed(CompareBaselineToTenantJob::class);
|
||||||
|
|
||||||
@ -73,7 +73,7 @@
|
|||||||
->where('type', OperationRunType::BaselineCompare->value)
|
->where('type', OperationRunType::BaselineCompare->value)
|
||||||
->get();
|
->get();
|
||||||
|
|
||||||
expect($activeRuns)->toHaveCount(2)
|
expect($activeRuns)->toHaveCount(3)
|
||||||
->and($activeRuns->every(static fn (OperationRun $run): bool => $run->managed_environment_id !== null))->toBeTrue()
|
->and($activeRuns->every(static fn (OperationRun $run): bool => $run->managed_environment_id !== null))->toBeTrue()
|
||||||
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->status === OperationRunStatus::Queued->value))->toBeTrue()
|
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->status === OperationRunStatus::Queued->value))->toBeTrue()
|
||||||
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->outcome === OperationRunOutcome::Pending->value))->toBeTrue()
|
->and($activeRuns->every(static fn (OperationRun $run): bool => (string) $run->outcome === OperationRunOutcome::Pending->value))->toBeTrue()
|
||||||
|
|||||||
@ -7,19 +7,17 @@
|
|||||||
use App\Support\Baselines\BaselineProfileStatus;
|
use App\Support\Baselines\BaselineProfileStatus;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
|
|
||||||
it('keeps baseline profiles out of tenant panel registration and tenant navigation URLs', function (): void {
|
it('keeps baseline profiles workspace-owned while retired tenant navigation URLs stay unavailable', function (): void {
|
||||||
$tenantPanelResources = Filament::getPanel('tenant')->getResources();
|
Filament::setCurrentPanel('admin');
|
||||||
|
|
||||||
expect($tenantPanelResources)->not->toContain(BaselineProfileResource::class);
|
|
||||||
|
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
|
|
||||||
|
expect(BaselineProfileResource::shouldRegisterNavigation())->toBeTrue();
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->withSession([\App\Support\Workspaces\WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
->withSession([\App\Support\Workspaces\WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id])
|
||||||
->get("/admin/t/{$tenant->external_id}")
|
->get("/admin/t/{$tenant->external_id}")
|
||||||
->assertOk()
|
->assertNotFound();
|
||||||
->assertDontSee("/admin/t/{$tenant->external_id}/baseline-profiles", false)
|
|
||||||
->assertDontSee('>Baselines</span>', false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
it('keeps baseline profile urls workspace-owned even when a tenant context exists', function (): void {
|
it('keeps baseline profile urls workspace-owned even when a tenant context exists', function (): void {
|
||||||
|
|||||||
@ -5,22 +5,14 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\RestoreRun;
|
use App\Models\RestoreRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('backup sets table bulk archive creates a run and archives selected sets', function () {
|
test('backup sets table bulk archive creates a run and archives selected sets', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$sets = collect(range(1, 3))->map(function (int $i) use ($tenant) {
|
$sets = collect(range(1, 3))->map(function (int $i) use ($tenant) {
|
||||||
return BackupSet::create([
|
return BackupSet::create([
|
||||||
@ -63,13 +55,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('backup sets can be archived even when referenced by restore runs', function () {
|
test('backup sets can be archived even when referenced by restore runs', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$set = BackupSet::create([
|
$set = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
@ -96,13 +83,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('backup sets table bulk archive requires type-to-confirm for 10+ sets', function () {
|
test('backup sets table bulk archive requires type-to-confirm for 10+ sets', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$sets = collect(range(1, 10))->map(function (int $i) use ($tenant) {
|
$sets = collect(range(1, 10))->map(function (int $i) use ($tenant) {
|
||||||
return BackupSet::create([
|
return BackupSet::create([
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\RestoreRun;
|
use App\Models\RestoreRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk delete restore runs skips running items', function () {
|
test('bulk delete restore runs skips running items', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$backupSet = BackupSet::create([
|
$backupSet = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\RestoreRun;
|
use App\Models\RestoreRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk delete restore runs soft deletes selected runs', function () {
|
test('bulk delete restore runs soft deletes selected runs', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$backupSet = BackupSet::create([
|
$backupSet = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupItem;
|
use App\Models\BackupItem;
|
||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('backup sets table bulk force delete permanently deletes archived sets and their items', function () {
|
test('backup sets table bulk force delete permanently deletes archived sets and their items', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$set = BackupSet::create([
|
$set = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -4,22 +4,15 @@
|
|||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('policy versions table bulk force delete creates a run and skips non-archived records', function () {
|
test('policy versions table bulk force delete creates a run and skips non-archived records', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create(['is_current' => true]);
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
$tenant->forceFill(['is_current' => true])->save();
|
||||||
$user->tenants()->syncWithoutDetaching([
|
setAdminPanelContext($tenant);
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
||||||
$version = PolicyVersion::factory()->create([
|
$version = PolicyVersion::factory()->create([
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\RestoreRun;
|
use App\Models\RestoreRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk force delete restore runs permanently deletes archived runs', function () {
|
test('bulk force delete restore runs permanently deletes archived runs', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$backupSet = BackupSet::create([
|
$backupSet = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -2,7 +2,6 @@
|
|||||||
|
|
||||||
use App\Livewire\BulkOperationProgress;
|
use App\Livewire\BulkOperationProgress;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
@ -11,7 +10,7 @@
|
|||||||
test('progress widget shows running operations for current tenant and user', function () {
|
test('progress widget shows running operations for current tenant and user', function () {
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
Filament::setTenant($tenant, true);
|
setAdminPanelContext($tenant);
|
||||||
|
|
||||||
// Active op
|
// Active op
|
||||||
OperationRun::factory()->create([
|
OperationRun::factory()->create([
|
||||||
@ -44,7 +43,7 @@
|
|||||||
test('progress widget shows queued backup schedule runs as operation runs', function () {
|
test('progress widget shows queued backup schedule runs as operation runs', function () {
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
Filament::setTenant($tenant, true);
|
setAdminPanelContext($tenant);
|
||||||
|
|
||||||
OperationRun::factory()->create([
|
OperationRun::factory()->create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk prune records skip reasons', function () {
|
test('bulk prune records skip reasons', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$policyA = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
$policyA = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
||||||
$current = PolicyVersion::factory()->create([
|
$current = PolicyVersion::factory()->create([
|
||||||
|
|||||||
@ -3,22 +3,14 @@
|
|||||||
use App\Filament\Resources\PolicyVersionResource;
|
use App\Filament\Resources\PolicyVersionResource;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk prune archives eligible policy versions', function () {
|
test('bulk prune archives eligible policy versions', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
||||||
|
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupItem;
|
use App\Models\BackupItem;
|
||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('backup sets table bulk restore restores archived sets and their items', function () {
|
test('backup sets table bulk restore restores archived sets and their items', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$set = BackupSet::create([
|
$set = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -4,22 +4,15 @@
|
|||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('policy versions table bulk restore creates a run and restores archived records', function () {
|
test('policy versions table bulk restore creates a run and restores archived records', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create(['is_current' => true]);
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
$tenant->forceFill(['is_current' => true])->save();
|
||||||
$user->tenants()->syncWithoutDetaching([
|
setAdminPanelContext($tenant);
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
$policy = Policy::factory()->create(['managed_environment_id' => $tenant->id]);
|
||||||
$version = PolicyVersion::factory()->create([
|
$version = PolicyVersion::factory()->create([
|
||||||
|
|||||||
@ -4,22 +4,14 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Models\RestoreRun;
|
use App\Models\RestoreRun;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('restore runs table bulk restore creates a run and restores archived records', function () {
|
test('restore runs table bulk restore creates a run and restores archived records', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
$backupSet = BackupSet::create([
|
$backupSet = BackupSet::create([
|
||||||
'managed_environment_id' => $tenant->id,
|
'managed_environment_id' => $tenant->id,
|
||||||
|
|||||||
@ -2,22 +2,14 @@
|
|||||||
|
|
||||||
use App\Filament\Resources\PolicyResource;
|
use App\Filament\Resources\PolicyResource;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\ManagedEnvironment;
|
|
||||||
use App\Models\User;
|
|
||||||
use Filament\Facades\Filament;
|
|
||||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||||
use Livewire\Livewire;
|
use Livewire\Livewire;
|
||||||
|
|
||||||
uses(RefreshDatabase::class);
|
uses(RefreshDatabase::class);
|
||||||
|
|
||||||
test('bulk delete requires confirmation string for large batches', function () {
|
test('bulk delete requires confirmation string for large batches', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
$policies = Policy::factory()->count(20)->create(['managed_environment_id' => $tenant->id]);
|
$policies = Policy::factory()->count(20)->create(['managed_environment_id' => $tenant->id]);
|
||||||
|
|
||||||
Livewire::actingAs($user)
|
Livewire::actingAs($user)
|
||||||
@ -31,13 +23,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('bulk delete fails with incorrect confirmation string', function () {
|
test('bulk delete fails with incorrect confirmation string', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
$policies = Policy::factory()->count(20)->create(['managed_environment_id' => $tenant->id]);
|
$policies = Policy::factory()->count(20)->create(['managed_environment_id' => $tenant->id]);
|
||||||
|
|
||||||
Livewire::actingAs($user)
|
Livewire::actingAs($user)
|
||||||
@ -51,13 +38,8 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
test('bulk delete does not require confirmation string for small batches', function () {
|
test('bulk delete does not require confirmation string for small batches', function () {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
$user = User::factory()->create();
|
setAdminPanelContext($tenant);
|
||||||
$user->tenants()->syncWithoutDetaching([
|
|
||||||
$tenant->getKey() => ['role' => 'owner'],
|
|
||||||
]);
|
|
||||||
|
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
$policies = Policy::factory()->count(10)->create(['managed_environment_id' => $tenant->id]);
|
$policies = Policy::factory()->count(10)->create(['managed_environment_id' => $tenant->id]);
|
||||||
|
|
||||||
Livewire::actingAs($user)
|
Livewire::actingAs($user)
|
||||||
|
|||||||
@ -57,8 +57,7 @@
|
|||||||
$this->get(TenantDashboard::getUrl(tenant: $tenant))
|
$this->get(TenantDashboard::getUrl(tenant: $tenant))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(RecoveryReadiness::class)
|
Livewire::test(RecoveryReadiness::class)
|
||||||
->assertSee('Backup posture')
|
->assertSee('Backup posture')
|
||||||
|
|||||||
@ -64,9 +64,11 @@ function tenantDashboardButtonClassesForXPath(string $content, string $xpathExpr
|
|||||||
|
|
||||||
it('builds the canonical operations follow-up baseline with tenant continuity', function (): void {
|
it('builds the canonical operations follow-up baseline with tenant continuity', function (): void {
|
||||||
$tenant = ManagedEnvironment::factory()->create();
|
$tenant = ManagedEnvironment::factory()->create();
|
||||||
|
[, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
||||||
|
|
||||||
expect(OperationRunLinks::index($tenant, activeTab: 'active'))
|
expect(OperationRunLinks::index($tenant, activeTab: 'active'))
|
||||||
->toBe(route('admin.operations.index', [
|
->toBe(route('admin.operations.index', [
|
||||||
|
'workspace' => $tenant->workspace,
|
||||||
'managed_environment_id' => (int) $tenant->getKey(),
|
'managed_environment_id' => (int) $tenant->getKey(),
|
||||||
'activeTab' => 'active',
|
'activeTab' => 'active',
|
||||||
]))
|
]))
|
||||||
@ -76,6 +78,7 @@ function tenantDashboardButtonClassesForXPath(string $content, string $xpathExpr
|
|||||||
problemClass: OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
problemClass: OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
||||||
))
|
))
|
||||||
->toBe(route('admin.operations.index', [
|
->toBe(route('admin.operations.index', [
|
||||||
|
'workspace' => $tenant->workspace,
|
||||||
'managed_environment_id' => (int) $tenant->getKey(),
|
'managed_environment_id' => (int) $tenant->getKey(),
|
||||||
'activeTab' => OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
'activeTab' => OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
||||||
'problemClass' => OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
'problemClass' => OperationRun::PROBLEM_CLASS_TERMINAL_FOLLOW_UP,
|
||||||
@ -86,9 +89,10 @@ function tenantDashboardButtonClassesForXPath(string $content, string $xpathExpr
|
|||||||
$tenant = ManagedEnvironment::factory()->create([
|
$tenant = ManagedEnvironment::factory()->create([
|
||||||
'external_id' => 'tenant-dashboard-productization',
|
'external_id' => 'tenant-dashboard-productization',
|
||||||
]);
|
]);
|
||||||
|
[, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
||||||
|
|
||||||
expect(RequiredPermissionsLinks::requiredPermissions($tenant, ['source' => 'tenant_dashboard']))
|
expect(RequiredPermissionsLinks::requiredPermissions($tenant, ['source' => 'tenant_dashboard']))
|
||||||
->toBe('/admin/tenants/'.urlencode((string) $tenant->external_id).'/required-permissions?source=tenant_dashboard');
|
->toBe(url('/admin/workspaces/'.urlencode((string) $tenant->workspace->slug).'/environments/'.urlencode((string) $tenant->getRouteKey()).'/required-permissions?source=tenant_dashboard'));
|
||||||
});
|
});
|
||||||
|
|
||||||
it('prioritizes operations requiring attention below permissions and high severity findings and keeps canonical hub links', function (): void {
|
it('prioritizes operations requiring attention below permissions and high severity findings and keeps canonical hub links', function (): void {
|
||||||
@ -239,13 +243,13 @@ function tenantDashboardButtonClassesForXPath(string $content, string $xpathExpr
|
|||||||
->and($actions[1]['actionUrl'])->toBe(FindingResource::getUrl('index', [
|
->and($actions[1]['actionUrl'])->toBe(FindingResource::getUrl('index', [
|
||||||
'tab' => 'needs_action',
|
'tab' => 'needs_action',
|
||||||
'high_severity' => 1,
|
'high_severity' => 1,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->and($actions[2]['actionUrl'])->toBe(FindingExceptionResource::getUrl('index', panel: 'tenant', tenant: $tenant));
|
->and($actions[2]['actionUrl'])->toBe(FindingExceptionResource::getUrl('index', panel: 'admin', tenant: $tenant));
|
||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$content = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$content = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->getContent();
|
->getContent();
|
||||||
|
|
||||||
|
|||||||
@ -117,7 +117,7 @@ function tenantDashboardProductizationHeaderMoreActionNames(Testable $component)
|
|||||||
$outsider = User::factory()->create();
|
$outsider = User::factory()->create();
|
||||||
|
|
||||||
$this->actingAs($outsider)
|
$this->actingAs($outsider)
|
||||||
->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -127,7 +127,7 @@ function tenantDashboardProductizationHeaderMoreActionNames(Testable $component)
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful();
|
->assertSuccessful();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ function tenantDashboardProductizationHeaderMoreActionNames(Testable $component)
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$content = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$content = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->getContent();
|
->getContent();
|
||||||
|
|
||||||
@ -313,7 +313,7 @@ function tenantDashboardProductizationHeaderMoreActionNames(Testable $component)
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$content = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$content = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->getContent();
|
->getContent();
|
||||||
|
|
||||||
@ -383,5 +383,5 @@ function tenantDashboardProductizationHeaderMoreActionNames(Testable $component)
|
|||||||
$this->get(FindingResource::getUrl('index', [
|
$this->get(FindingResource::getUrl('index', [
|
||||||
'tab' => 'needs_action',
|
'tab' => 'needs_action',
|
||||||
'high_severity' => 1,
|
'high_severity' => 1,
|
||||||
], panel: 'tenant', tenant: $tenant))->assertForbidden();
|
], panel: 'admin', tenant: $tenant))->assertForbidden();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -109,7 +109,7 @@ function mockTenantDashboardReadinessPermissions(array $overview = []): void
|
|||||||
expect($outputCard)
|
expect($outputCard)
|
||||||
->not->toBeNull()
|
->not->toBeNull()
|
||||||
->and($outputCard['actionLabel'])->toBe('Open review pack')
|
->and($outputCard['actionLabel'])->toBe('Open review pack')
|
||||||
->and($outputCard['actionUrl'])->toBe(ReviewPackResource::getUrl('view', ['record' => $pack], panel: 'tenant', tenant: $tenant))
|
->and($outputCard['actionUrl'])->toBe(ReviewPackResource::getUrl('view', ['record' => $pack], panel: 'admin', tenant: $tenant))
|
||||||
->and($outputCard['helperText'])->toBeNull();
|
->and($outputCard['helperText'])->toBeNull();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -180,7 +180,7 @@ function mockTenantDashboardReadinessPermissions(array $overview = []): void
|
|||||||
->and($currentReview['actionUrl'])->toBe(TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant))
|
->and($currentReview['actionUrl'])->toBe(TenantReviewResource::tenantScopedUrl('view', ['record' => $review], $tenant))
|
||||||
->and($evidenceCoverage)
|
->and($evidenceCoverage)
|
||||||
->not->toBeNull()
|
->not->toBeNull()
|
||||||
->and($evidenceCoverage['actionUrl'])->toBe(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'tenant', tenant: $tenant))
|
->and($evidenceCoverage['actionUrl'])->toBe(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], panel: 'admin', tenant: $tenant))
|
||||||
->and($outputCard)
|
->and($outputCard)
|
||||||
->not->toBeNull()
|
->not->toBeNull()
|
||||||
->and($outputCard['actionUrl'])->toBe(CustomerReviewWorkspace::tenantPrefilterUrl($tenant))
|
->and($outputCard['actionUrl'])->toBe(CustomerReviewWorkspace::tenantPrefilterUrl($tenant))
|
||||||
@ -264,7 +264,7 @@ function mockTenantDashboardReadinessPermissions(array $overview = []): void
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$content = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$content = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->getContent();
|
->getContent();
|
||||||
|
|
||||||
@ -331,5 +331,5 @@ function mockTenantDashboardReadinessPermissions(array $overview = []): void
|
|||||||
->not->toBeNull()
|
->not->toBeNull()
|
||||||
->and($evidenceCoverage['value'])->toBe('Unavailable')
|
->and($evidenceCoverage['value'])->toBe('Unavailable')
|
||||||
->and($evidenceCoverage['description'])->toBe('No evidence snapshot is currently available for customer-safe output.')
|
->and($evidenceCoverage['description'])->toBe('No evidence snapshot is currently available for customer-safe output.')
|
||||||
->and($evidenceCoverage['actionUrl'])->toBe(EvidenceSnapshotResource::getUrl('index', panel: 'tenant', tenant: $tenant));
|
->and($evidenceCoverage['actionUrl'])->toBe(EvidenceSnapshotResource::getUrl('index', panel: 'admin', tenant: $tenant));
|
||||||
});
|
});
|
||||||
|
|||||||
@ -74,7 +74,7 @@ function mockTenantDashboardSummaryPermissions(array $overview = []): void
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$response = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$response = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertSee($tenant->name)
|
->assertSee($tenant->name)
|
||||||
->assertSee('Recommended next actions')
|
->assertSee('Recommended next actions')
|
||||||
@ -106,7 +106,7 @@ function mockTenantDashboardSummaryPermissions(array $overview = []): void
|
|||||||
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-provider"')
|
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-provider"')
|
||||||
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-provider-microsoft-logo"')
|
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-provider-microsoft-logo"')
|
||||||
->and($content)->toContain('data-provider-key="microsoft"')
|
->and($content)->toContain('data-provider-key="microsoft"')
|
||||||
->and($content)->toContain('Microsoft tenant')
|
->and($content)->toContain('Microsoft environment')
|
||||||
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-latest-activity" class="inline-flex items-center gap-2 whitespace-nowrap')
|
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-latest-activity" class="inline-flex items-center gap-2 whitespace-nowrap')
|
||||||
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-latest-activity-icon"')
|
->and($content)->toContain('data-testid="tenant-dashboard-context-chip-latest-activity-icon"')
|
||||||
->and($content)->toContain('Latest activity:')
|
->and($content)->toContain('Latest activity:')
|
||||||
@ -294,7 +294,7 @@ function mockTenantDashboardSummaryPermissions(array $overview = []): void
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$response = $this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$response = $this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertSee('No immediate action is waiting.')
|
->assertSee('No immediate action is waiting.')
|
||||||
->assertDontSee('Recent operations')
|
->assertDontSee('Recent operations')
|
||||||
@ -381,7 +381,7 @@ function mockTenantDashboardSummaryPermissions(array $overview = []): void
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertSee('data-testid="tenant-dashboard-operations-attention-summary"', false)
|
->assertSee('data-testid="tenant-dashboard-operations-attention-summary"', false)
|
||||||
->assertSee('Review operation')
|
->assertSee('Review operation')
|
||||||
@ -417,7 +417,7 @@ function mockTenantDashboardSummaryPermissions(array $overview = []): void
|
|||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
setTenantPanelContext($tenant);
|
setTenantPanelContext($tenant);
|
||||||
|
|
||||||
$this->get(TenantDashboard::getUrl(panel: 'tenant', tenant: $tenant))
|
$this->get(TenantDashboard::getUrl(panel: 'admin', tenant: $tenant))
|
||||||
->assertSuccessful()
|
->assertSuccessful()
|
||||||
->assertDontSee('data-testid="tenant-dashboard-operations-attention-summary"', false)
|
->assertDontSee('data-testid="tenant-dashboard-operations-attention-summary"', false)
|
||||||
->assertDontSee('Review operation')
|
->assertDontSee('Review operation')
|
||||||
|
|||||||
@ -29,6 +29,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
||||||
|
spec283SeedRequirementRows($tenant, ['permissions.directory_groups']);
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$tenant->makeCurrent();
|
$tenant->makeCurrent();
|
||||||
|
|||||||
@ -124,14 +124,14 @@
|
|||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
test('keeps Entra groups out of admin sidebar navigation while preserving tenant-panel navigation', function () {
|
test('keeps Entra groups out of admin sidebar navigation after tenant-panel retirement', function () {
|
||||||
Filament::setCurrentPanel(Filament::getPanel('admin'));
|
Filament::setCurrentPanel(Filament::getPanel('admin'));
|
||||||
|
|
||||||
expect(EntraGroupResource::shouldRegisterNavigation())->toBeFalse();
|
expect(EntraGroupResource::shouldRegisterNavigation())->toBeFalse();
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
Filament::setCurrentPanel(Filament::getPanel('admin'));
|
||||||
|
|
||||||
expect(EntraGroupResource::shouldRegisterNavigation())->toBeTrue();
|
expect(EntraGroupResource::shouldRegisterNavigation())->toBeFalse();
|
||||||
|
|
||||||
Filament::setCurrentPanel(null);
|
Filament::setCurrentPanel(null);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -1,10 +1,12 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Resources\PolicyVersionResource;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
use App\Models\ManagedEnvironment;
|
use App\Models\ManagedEnvironment;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Services\Graph\GraphClientInterface;
|
use App\Services\Graph\GraphClientInterface;
|
||||||
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
|
|
||||||
use function Pest\Laravel\mock;
|
use function Pest\Laravel\mock;
|
||||||
|
|
||||||
@ -48,10 +50,14 @@
|
|||||||
|
|
||||||
$this->actingAs($this->user);
|
$this->actingAs($this->user);
|
||||||
|
|
||||||
$response = $this->get(route('filament.tenant.resources.policy-versions.view', array_merge(
|
$response = $this
|
||||||
filamentTenantRouteParams($this->tenant),
|
->withSession([
|
||||||
['record' => $version],
|
WorkspaceContext::SESSION_KEY => (int) $this->tenant->workspace_id,
|
||||||
)));
|
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
|
||||||
|
(string) $this->tenant->workspace_id => (int) $this->tenant->getKey(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->get(PolicyVersionResource::getUrl('view', ['record' => $version], panel: 'admin', tenant: $this->tenant));
|
||||||
|
|
||||||
$response->assertOk();
|
$response->assertOk();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -21,6 +21,7 @@
|
|||||||
});
|
});
|
||||||
|
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
||||||
|
spec283SeedRequirementRows($tenant, ['permissions.directory_groups']);
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
$tenant->makeCurrent();
|
$tenant->makeCurrent();
|
||||||
|
|||||||
@ -10,6 +10,7 @@
|
|||||||
Queue::fake();
|
Queue::fake();
|
||||||
|
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
[$user, $tenant] = createUserWithTenant(role: 'owner', fixtureProfile: 'credential-enabled');
|
||||||
|
spec283SeedRequirementRows($tenant, ['permissions.directory_groups']);
|
||||||
|
|
||||||
$service = app(EntraGroupSyncService::class);
|
$service = app(EntraGroupSyncService::class);
|
||||||
|
|
||||||
|
|||||||
@ -1,8 +1,10 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
|
use App\Filament\Resources\FindingResource;
|
||||||
use App\Models\Finding;
|
use App\Models\Finding;
|
||||||
use App\Models\Policy;
|
use App\Models\Policy;
|
||||||
use App\Models\PolicyVersion;
|
use App\Models\PolicyVersion;
|
||||||
|
use App\Support\Workspaces\WorkspaceContext;
|
||||||
|
|
||||||
it('shows an explicit diff unavailable message when policy version references are missing', function (): void {
|
it('shows an explicit diff unavailable message when policy version references are missing', function (): void {
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
@ -38,10 +40,13 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('filament.tenant.resources.findings.view', array_merge(
|
->withSession([
|
||||||
filamentTenantRouteParams($tenant),
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
['record' => $finding],
|
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
|
||||||
)))
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Diff unavailable')
|
->assertSee('Diff unavailable')
|
||||||
->assertDontSee('No normalized changes were found');
|
->assertDontSee('No normalized changes were found');
|
||||||
@ -107,10 +112,13 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('filament.tenant.resources.findings.view', array_merge(
|
->withSession([
|
||||||
filamentTenantRouteParams($tenant),
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
['record' => $finding],
|
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
|
||||||
)))
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertDontSee('Diff unavailable')
|
->assertDontSee('Diff unavailable')
|
||||||
->assertSee('1 added')
|
->assertSee('1 added')
|
||||||
@ -177,10 +185,13 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$response = $this->actingAs($user)
|
$response = $this->actingAs($user)
|
||||||
->get(route('filament.tenant.resources.findings.view', array_merge(
|
->withSession([
|
||||||
filamentTenantRouteParams($tenant),
|
WorkspaceContext::SESSION_KEY => (int) $tenant->workspace_id,
|
||||||
['record' => $finding],
|
WorkspaceContext::LAST_TENANT_IDS_SESSION_KEY => [
|
||||||
)))
|
(string) $tenant->workspace_id => (int) $tenant->getKey(),
|
||||||
|
],
|
||||||
|
])
|
||||||
|
->get(FindingResource::getUrl('view', ['record' => $finding], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertDontSee('Diff unavailable')
|
->assertDontSee('Diff unavailable')
|
||||||
->assertSee('1 removed')
|
->assertSee('1 removed')
|
||||||
|
|||||||
@ -85,7 +85,7 @@ function createAdminRolesReport(ManagedEnvironment $tenant, ?array $summaryOverr
|
|||||||
'high_privilege_assignments' => 7,
|
'high_privilege_assignments' => 7,
|
||||||
]);
|
]);
|
||||||
|
|
||||||
$expectedUrl = StoredReportResource::getUrl('view', ['record' => $report], panel: 'tenant', tenant: $tenant);
|
$expectedUrl = StoredReportResource::getUrl('view', ['record' => $report], panel: 'admin', tenant: $tenant);
|
||||||
|
|
||||||
Livewire::actingAs($user)
|
Livewire::actingAs($user)
|
||||||
->test(AdminRolesSummaryWidget::class, ['record' => $tenant])
|
->test(AdminRolesSummaryWidget::class, ['record' => $tenant])
|
||||||
|
|||||||
@ -94,8 +94,8 @@
|
|||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenantA->workspace_id])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $tenantA->workspace_id])
|
||||||
->get(route('admin.evidence.overview', ['managed_environment_id' => (int) $tenantB->getKey()]))
|
->get(route('admin.evidence.overview', ['managed_environment_id' => (int) $tenantB->getKey()]))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshots[(int) $tenantB->getKey()]], tenant: $tenantB, panel: 'tenant'), false)
|
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshots[(int) $tenantB->getKey()]], tenant: $tenantB, panel: 'admin'), false)
|
||||||
->assertDontSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshots[(int) $tenantA->getKey()]], tenant: $tenantA, panel: 'tenant'), false);
|
->assertDontSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshots[(int) $tenantA->getKey()]], tenant: $tenantA, panel: 'admin'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('shows stale evidence burden and a create-review next step on the overview', function (): void {
|
it('shows stale evidence burden and a create-review next step on the overview', function (): void {
|
||||||
@ -121,8 +121,8 @@
|
|||||||
->assertSee($freshTenant->name)
|
->assertSee($freshTenant->name)
|
||||||
->assertSee('Refresh the stale evidence before relying on this snapshot')
|
->assertSee('Refresh the stale evidence before relying on this snapshot')
|
||||||
->assertSee('Create a current review from this evidence snapshot')
|
->assertSee('Create a current review from this evidence snapshot')
|
||||||
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'tenant'), false)
|
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'admin'), false)
|
||||||
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $freshSnapshot], tenant: $freshTenant, panel: 'tenant'), false);
|
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $freshSnapshot], tenant: $freshTenant, panel: 'admin'), false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('seeds the native entitled-tenant prefilter once and clears it through the page action', function (): void {
|
it('seeds the native entitled-tenant prefilter once and clears it through the page action', function (): void {
|
||||||
@ -174,6 +174,6 @@
|
|||||||
|
|
||||||
$this->get(route('admin.evidence.overview'))
|
$this->get(route('admin.evidence.overview'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshotA], tenant: $tenantA, panel: 'tenant'), false)
|
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshotA], tenant: $tenantA, panel: 'admin'), false)
|
||||||
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshotB], tenant: $tenantB, panel: 'tenant'), false);
|
->assertSee(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshotB], tenant: $tenantB, panel: 'admin'), false);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -48,7 +48,7 @@
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant').'?'.http_build_query([
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin').'?'.http_build_query([
|
||||||
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
||||||
'review_id' => '123',
|
'review_id' => '123',
|
||||||
'tenant_filter_id' => (string) $tenant->getKey(),
|
'tenant_filter_id' => (string) $tenant->getKey(),
|
||||||
|
|||||||
@ -95,7 +95,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
[$user, $tenant] = createUserWithTenant(tenant: $tenant, role: 'owner');
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -111,7 +111,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
[$user] = createUserWithTenant(role: 'owner');
|
[$user] = createUserWithTenant(role: 'owner');
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -122,7 +122,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
Gate::define(Capabilities::EVIDENCE_VIEW, fn (): bool => false);
|
Gate::define(Capabilities::EVIDENCE_VIEW, fn (): bool => false);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('index', tenant: $tenant, panel: 'admin'))
|
||||||
->assertForbidden();
|
->assertForbidden();
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -182,7 +182,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Related context')
|
->assertSee('Related context')
|
||||||
->assertSee('Review pack');
|
->assertSee('Review pack');
|
||||||
@ -245,7 +245,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
suspendEvidenceSnapshotWorkspace($tenant);
|
suspendEvidenceSnapshotWorkspace($tenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
||||||
->assertOk();
|
->assertOk();
|
||||||
|
|
||||||
$tenant->makeCurrent();
|
$tenant->makeCurrent();
|
||||||
@ -277,7 +277,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Outcome summary')
|
->assertSee('Outcome summary')
|
||||||
->assertDontSee('Artifact truth')
|
->assertDontSee('Artifact truth')
|
||||||
@ -303,13 +303,13 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
$staleSnapshot = $this->makeStaleArtifactTruthEvidenceSnapshot($staleTenant);
|
$staleSnapshot = $this->makeStaleArtifactTruthEvidenceSnapshot($staleTenant);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $freshSnapshot], tenant: $freshTenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $freshSnapshot], tenant: $freshTenant, panel: 'admin'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('No action needed')
|
->assertSee('No action needed')
|
||||||
->assertDontSee('Refresh the stale evidence before relying on this snapshot');
|
->assertDontSee('Refresh the stale evidence before relying on this snapshot');
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $staleSnapshot], tenant: $staleTenant, panel: 'admin'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Refresh recommended')
|
->assertSee('Refresh recommended')
|
||||||
->assertSee('Refresh the stale evidence before relying on this snapshot');
|
->assertSee('Refresh the stale evidence before relying on this snapshot');
|
||||||
@ -412,7 +412,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant'))
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin'))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSeeText('3 findings, 2 open.')
|
->assertSeeText('3 findings, 2 open.')
|
||||||
->assertSeeText('Open findings')
|
->assertSeeText('Open findings')
|
||||||
@ -455,7 +455,7 @@ function suspendEvidenceSnapshotWorkspace(ManagedEnvironment $tenant): void
|
|||||||
]);
|
]);
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'tenant').'?'.http_build_query([
|
->get(EvidenceSnapshotResource::getUrl('view', ['record' => $snapshot], tenant: $tenant, panel: 'admin').'?'.http_build_query([
|
||||||
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
'source_surface' => CustomerReviewWorkspace::SOURCE_SURFACE,
|
||||||
'review_id' => '456',
|
'review_id' => '456',
|
||||||
'tenant_filter_id' => (string) $tenant->getKey(),
|
'tenant_filter_id' => (string) $tenant->getKey(),
|
||||||
|
|||||||
@ -2,6 +2,10 @@
|
|||||||
|
|
||||||
declare(strict_types=1);
|
declare(strict_types=1);
|
||||||
|
|
||||||
|
use App\Filament\Pages\InventoryCoverage;
|
||||||
|
use App\Filament\Resources\BackupSetResource;
|
||||||
|
use App\Filament\Resources\PolicyResource;
|
||||||
|
use App\Filament\Resources\PolicyVersionResource;
|
||||||
use App\Models\ManagedEnvironment;
|
use App\Models\ManagedEnvironment;
|
||||||
use App\Models\ManagedEnvironmentMembership;
|
use App\Models\ManagedEnvironmentMembership;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
@ -20,7 +24,7 @@
|
|||||||
'/admin/inventory/inventory-coverage',
|
'/admin/inventory/inventory-coverage',
|
||||||
]);
|
]);
|
||||||
|
|
||||||
it('redirects tenant-scoped admin surfaces to choose-tenant when no tenant is selected', function (): void {
|
it('keeps retired flat tenant-scoped admin surfaces unavailable when no tenant is selected', function (): void {
|
||||||
$user = User::factory()->create();
|
$user = User::factory()->create();
|
||||||
|
|
||||||
$workspace = Workspace::factory()->create();
|
$workspace = Workspace::factory()->create();
|
||||||
@ -51,25 +55,25 @@
|
|||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
->get('/admin/policies')
|
->get('/admin/policies')
|
||||||
->assertRedirect('/admin/choose-tenant');
|
->assertNotFound();
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
->get('/admin/policy-versions')
|
->get('/admin/policy-versions')
|
||||||
->assertRedirect('/admin/choose-tenant');
|
->assertNotFound();
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
->get('/admin/backup-sets')
|
->get('/admin/backup-sets')
|
||||||
->assertRedirect('/admin/choose-tenant');
|
->assertNotFound();
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->actingAs($user)
|
->actingAs($user)
|
||||||
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
->withSession([WorkspaceContext::SESSION_KEY => (int) $workspace->getKey()])
|
||||||
->get('/admin/inventory')
|
->get('/admin/inventory')
|
||||||
->assertRedirect('/admin/choose-tenant');
|
->assertRedirect('/admin/workspaces/'.$workspace->getKey().'/environments');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('allows tenant-scoped admin surfaces to load from the remembered canonical tenant', function (string $path): void {
|
it('allows tenant-scoped admin surfaces to load from the remembered canonical tenant', function (string $path): void {
|
||||||
@ -90,7 +94,12 @@
|
|||||||
(string) $tenantA->workspace_id => (int) $tenantA->getKey(),
|
(string) $tenantA->workspace_id => (int) $tenantA->getKey(),
|
||||||
],
|
],
|
||||||
])
|
])
|
||||||
->get($path);
|
->get(match ($path) {
|
||||||
|
'/admin/policies' => PolicyResource::getUrl(panel: 'admin', tenant: $tenantA),
|
||||||
|
'/admin/policy-versions' => PolicyVersionResource::getUrl(panel: 'admin', tenant: $tenantA),
|
||||||
|
'/admin/backup-sets' => BackupSetResource::getUrl(panel: 'admin', tenant: $tenantA),
|
||||||
|
'/admin/inventory', '/admin/inventory/inventory-coverage' => InventoryCoverage::getUrl(panel: 'admin', tenant: $tenantA),
|
||||||
|
});
|
||||||
|
|
||||||
expect($response->getStatusCode())->toBeIn([200, 302]);
|
expect($response->getStatusCode())->toBeIn([200, 302]);
|
||||||
expect($response->headers->get('Location'))->not->toBe('/admin/choose-tenant');
|
expect($response->headers->get('Location'))->not->toBe('/admin/choose-tenant');
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
use App\Support\BackupHealth\TenantBackupHealthAssessment;
|
use App\Support\BackupHealth\TenantBackupHealthAssessment;
|
||||||
|
use App\Support\OperationRunLinks;
|
||||||
use Carbon\CarbonImmutable;
|
use Carbon\CarbonImmutable;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
|
|
||||||
@ -59,7 +60,7 @@
|
|||||||
->assertSee('Timing')
|
->assertSee('Timing')
|
||||||
->assertSee('Archive')
|
->assertSee('Archive')
|
||||||
->assertSee('More')
|
->assertSee('More')
|
||||||
->assertSee('/admin/operations/'.$run->getKey(), false)
|
->assertSee(OperationRunLinks::tenantlessView($run), false)
|
||||||
->assertDontSee('Related record')
|
->assertDontSee('Related record')
|
||||||
->assertDontSee('>Completed</span>', false)
|
->assertDontSee('>Completed</span>', false)
|
||||||
->assertSeeInOrder(['Nightly backup', 'Backup quality', 'Lifecycle overview', 'Related context', 'Technical detail']);
|
->assertSeeInOrder(['Nightly backup', 'Backup quality', 'Lifecycle overview', 'Related context', 'Technical detail']);
|
||||||
@ -140,7 +141,7 @@
|
|||||||
$this->get(BackupSetResource::getUrl('view', [
|
$this->get(BackupSetResource::getUrl('view', [
|
||||||
'record' => $backupSet,
|
'record' => $backupSet,
|
||||||
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
|
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Backup posture')
|
->assertSee('Backup posture')
|
||||||
->assertSee('Latest backup is stale')
|
->assertSee('Latest backup is stale')
|
||||||
@ -174,7 +175,7 @@
|
|||||||
$this->get(BackupSetResource::getUrl('view', [
|
$this->get(BackupSetResource::getUrl('view', [
|
||||||
'record' => $backupSet,
|
'record' => $backupSet,
|
||||||
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_DEGRADED,
|
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_DEGRADED,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Backup posture')
|
->assertSee('Backup posture')
|
||||||
->assertSee('Latest backup is degraded')
|
->assertSee('Latest backup is degraded')
|
||||||
|
|||||||
@ -17,7 +17,7 @@
|
|||||||
|
|
||||||
$this->get(BackupSetResource::getUrl('index', [
|
$this->get(BackupSetResource::getUrl('index', [
|
||||||
'backup_health_reason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
|
'backup_health_reason' => TenantBackupHealthAssessment::REASON_NO_BACKUP_BASIS,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('No usable completed backup basis is currently available for this tenant.')
|
->assertSee('No usable completed backup basis is currently available for this tenant.')
|
||||||
->assertSee('No backup sets');
|
->assertSee('No backup sets');
|
||||||
@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
$this->get(BackupSetResource::getUrl('index', [
|
$this->get(BackupSetResource::getUrl('index', [
|
||||||
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
|
'backup_health_reason' => TenantBackupHealthAssessment::REASON_LATEST_BACKUP_STALE,
|
||||||
], panel: 'tenant', tenant: $tenant))
|
], panel: 'admin', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('The latest backup detail is no longer available, so this view stays on the backup-set list.');
|
->assertSee('The latest backup detail is no longer available, so this view stays on the backup-set list.');
|
||||||
});
|
});
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
use App\Filament\Resources\BackupSetResource;
|
use App\Filament\Resources\BackupSetResource;
|
||||||
use App\Models\BackupSet;
|
use App\Models\BackupSet;
|
||||||
use App\Models\OperationRun;
|
use App\Models\OperationRun;
|
||||||
|
use App\Support\OperationRunLinks;
|
||||||
use Filament\Facades\Filament;
|
use Filament\Facades\Filament;
|
||||||
|
|
||||||
it('links backup sets to their canonical operations context', function (): void {
|
it('links backup sets to their canonical operations context', function (): void {
|
||||||
@ -29,10 +30,10 @@
|
|||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Related context')
|
->assertSee('Related context')
|
||||||
->assertSee('Operations')
|
->assertSee('Operations')
|
||||||
->assertSee('/admin/operations/'.$run->getKey(), false);
|
->assertSee(OperationRunLinks::tenantlessView($run), false);
|
||||||
|
|
||||||
$this->get(BackupSetResource::getUrl('index', tenant: $tenant))
|
$this->get(BackupSetResource::getUrl('index', tenant: $tenant))
|
||||||
->assertOk()
|
->assertOk()
|
||||||
->assertSee('Open operation')
|
->assertSee('Open operation')
|
||||||
->assertSee('/admin/operations/'.$run->getKey(), false);
|
->assertSee(OperationRunLinks::tenantlessView($run), false);
|
||||||
});
|
});
|
||||||
|
|||||||
@ -34,7 +34,7 @@ function getTableEmptyStateAction($component, string $name): ?\Filament\Actions\
|
|||||||
[$user] = createUserWithTenant($otherTenant, role: 'owner');
|
[$user] = createUserWithTenant($otherTenant, role: 'owner');
|
||||||
|
|
||||||
$this->actingAs($user)
|
$this->actingAs($user)
|
||||||
->get(BackupSetResource::getUrl('index', panel: 'tenant', tenant: $tenant))
|
->get(BackupSetResource::getUrl('index', panel: 'admin', tenant: $tenant))
|
||||||
->assertStatus(404);
|
->assertStatus(404);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -65,8 +65,7 @@ function createCoverageBannerTenant(): array
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareCoverageBanner::class)
|
Livewire::test(BaselineCompareCoverageBanner::class)
|
||||||
->assertSee('The last compare finished, but normal result output was suppressed.')
|
->assertSee('The last compare finished, but normal result output was suppressed.')
|
||||||
@ -88,8 +87,7 @@ function createCoverageBannerTenant(): array
|
|||||||
'baseline_profile_id' => (int) $profile->getKey(),
|
'baseline_profile_id' => (int) $profile->getKey(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareCoverageBanner::class)
|
Livewire::test(BaselineCompareCoverageBanner::class)
|
||||||
->assertSee('The current baseline snapshot is not available for compare.')
|
->assertSee('The current baseline snapshot is not available for compare.')
|
||||||
@ -122,8 +120,7 @@ function createCoverageBannerTenant(): array
|
|||||||
],
|
],
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareCoverageBanner::class)
|
Livewire::test(BaselineCompareCoverageBanner::class)
|
||||||
->assertDontSee('No confirmed drift in the latest baseline compare.')
|
->assertDontSee('No confirmed drift in the latest baseline compare.')
|
||||||
@ -162,8 +159,7 @@ function createCoverageBannerTenant(): array
|
|||||||
'due_at' => now()->subDay(),
|
'due_at' => now()->subDay(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareCoverageBanner::class)
|
Livewire::test(BaselineCompareCoverageBanner::class)
|
||||||
->assertSee('overdue finding')
|
->assertSee('overdue finding')
|
||||||
|
|||||||
@ -160,6 +160,6 @@ function seedBaselineCompareLandingGapRun(\App\Models\ManagedEnvironment $tenant
|
|||||||
seedBaselineCompareLandingGapRun($tenant);
|
seedBaselineCompareLandingGapRun($tenant);
|
||||||
|
|
||||||
$this->actingAs($nonMember)
|
$this->actingAs($nonMember)
|
||||||
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'tenant'))
|
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|||||||
@ -28,7 +28,7 @@
|
|||||||
it('redirects unauthenticated users (302)', function (): void {
|
it('redirects unauthenticated users (302)', function (): void {
|
||||||
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
[$user, $tenant] = createUserWithTenant(role: 'owner');
|
||||||
|
|
||||||
$this->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'tenant'))
|
$this->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
|
||||||
->assertStatus(302);
|
->assertStatus(302);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -37,7 +37,7 @@
|
|||||||
$nonMember = \App\Models\User::factory()->create();
|
$nonMember = \App\Models\User::factory()->create();
|
||||||
|
|
||||||
$this->actingAs($nonMember)
|
$this->actingAs($nonMember)
|
||||||
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'tenant'))
|
->get(BaselineCompareLanding::getUrl(tenant: $tenant, panel: 'admin'))
|
||||||
->assertNotFound();
|
->assertNotFound();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@ -6,6 +6,7 @@
|
|||||||
|
|
||||||
use App\Filament\Pages\BaselineCompareMatrix;
|
use App\Filament\Pages\BaselineCompareMatrix;
|
||||||
use App\Filament\Resources\BaselineProfileResource;
|
use App\Filament\Resources\BaselineProfileResource;
|
||||||
|
use App\Models\ManagedEnvironment;
|
||||||
use App\Models\User;
|
use App\Models\User;
|
||||||
use App\Models\WorkspaceMembership;
|
use App\Models\WorkspaceMembership;
|
||||||
use App\Support\Baselines\Compare\CompareStrategyRegistry;
|
use App\Support\Baselines\Compare\CompareStrategyRegistry;
|
||||||
@ -171,6 +172,11 @@
|
|||||||
'user_id' => (int) $viewer->getKey(),
|
'user_id' => (int) $viewer->getKey(),
|
||||||
'role' => 'owner',
|
'role' => 'owner',
|
||||||
]);
|
]);
|
||||||
|
createUserWithTenant(
|
||||||
|
tenant: ManagedEnvironment::factory()->create(['workspace_id' => (int) $fixture['workspace']->getKey()]),
|
||||||
|
user: $viewer,
|
||||||
|
role: 'owner',
|
||||||
|
);
|
||||||
|
|
||||||
$viewer->tenants()->syncWithoutDetaching([
|
$viewer->tenants()->syncWithoutDetaching([
|
||||||
(int) $fixture['visibleTenant']->getKey() => ['role' => 'owner'],
|
(int) $fixture['visibleTenant']->getKey() => ['role' => 'owner'],
|
||||||
@ -262,12 +268,13 @@
|
|||||||
$fixture = $this->makeBaselineCompareMatrixFixture();
|
$fixture = $this->makeBaselineCompareMatrixFixture();
|
||||||
$viewer = User::factory()->create();
|
$viewer = User::factory()->create();
|
||||||
|
|
||||||
WorkspaceMembership::factory()->create([
|
$scopedTenant = ManagedEnvironment::factory()->create([
|
||||||
'workspace_id' => (int) $fixture['workspace']->getKey(),
|
'workspace_id' => (int) $fixture['workspace']->getKey(),
|
||||||
'user_id' => (int) $viewer->getKey(),
|
'status' => 'active',
|
||||||
'role' => 'owner',
|
|
||||||
]);
|
]);
|
||||||
|
|
||||||
|
createUserWithTenant(tenant: $scopedTenant, user: $viewer, role: 'owner');
|
||||||
|
|
||||||
$session = $this->setAdminWorkspaceContext($viewer, $fixture['workspace']);
|
$session = $this->setAdminWorkspaceContext($viewer, $fixture['workspace']);
|
||||||
|
|
||||||
$this->withSession($session)
|
$this->withSession($session)
|
||||||
|
|||||||
@ -66,8 +66,7 @@ function createBaselineCompareWidgetTenant(): array
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareNow::class)
|
Livewire::test(BaselineCompareNow::class)
|
||||||
->assertSee('Baseline Governance')
|
->assertSee('Baseline Governance')
|
||||||
@ -111,8 +110,7 @@ function createBaselineCompareWidgetTenant(): array
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareNow::class)
|
Livewire::test(BaselineCompareNow::class)
|
||||||
->assertSee('Needs review')
|
->assertSee('Needs review')
|
||||||
@ -141,8 +139,7 @@ function createBaselineCompareWidgetTenant(): array
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareNow::class)
|
Livewire::test(BaselineCompareNow::class)
|
||||||
->assertSee('Action required')
|
->assertSee('Action required')
|
||||||
@ -168,8 +165,7 @@ function createBaselineCompareWidgetTenant(): array
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareNow::class)
|
Livewire::test(BaselineCompareNow::class)
|
||||||
->assertSee('In progress')
|
->assertSee('In progress')
|
||||||
@ -195,8 +191,7 @@ function createBaselineCompareWidgetTenant(): array
|
|||||||
|
|
||||||
$this->actingAs($user);
|
$this->actingAs($user);
|
||||||
|
|
||||||
Filament::setCurrentPanel(Filament::getPanel('tenant'));
|
setAdminPanelContext($tenant);
|
||||||
Filament::setTenant($tenant, true);
|
|
||||||
|
|
||||||
Livewire::test(BaselineCompareNow::class)
|
Livewire::test(BaselineCompareNow::class)
|
||||||
->assertSee('Unavailable')
|
->assertSee('Unavailable')
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user