'pol-1', 'displayName' => 'Test Policy', 'assignments' => [ ['target' => ['groupId' => 'group-1']], ['target' => ['groupId' => 'group-2']], ], 'roleScopeTagIds' => ['scope-tag-1', 'scope-tag-2'], ], ], 200); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true, [], 200); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true, [], 200); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true, [], 200); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true, [], 200); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true, [], 200); } } it('extracts edges during inventory sync and marks missing appropriately', function () { $tenant = Tenant::factory()->create(); $this->app->bind(GraphClientInterface::class, fn () => new FakeGraphClientForDeps); $svc = app(InventorySyncService::class); $run = $svc->syncNow($tenant, [ 'policy_types' => ['deviceConfiguration'], 'categories' => [], 'include_foundations' => false, 'include_dependencies' => true, ]); expect($run->status)->toBe('success'); $edges = InventoryLink::query()->where('tenant_id', $tenant->getKey())->get(); // 2 assigned_to + 2 scoped_by = 4 expect($edges->count())->toBe(4); }); it('respects 50-edge limit for outbound extraction', function () { $tenant = Tenant::factory()->create(); // Fake client returning 60 group assignments $this->app->bind(GraphClientInterface::class, function () { return new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { $assignments = []; for ($i = 1; $i <= 60; $i++) { $assignments[] = ['target' => ['groupId' => 'g-'.$i]]; } return new GraphResponse(true, [[ 'id' => 'pol-2', 'displayName' => 'Big Assignments', 'assignments' => $assignments, ]]); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true); } }; }); $svc = app(InventorySyncService::class); $svc->syncNow($tenant, [ 'policy_types' => ['deviceConfiguration'], 'categories' => [], 'include_foundations' => false, 'include_dependencies' => true, ]); $count = InventoryLink::query()->where('tenant_id', $tenant->getKey())->count(); expect($count)->toBe(50); }); it('persists unsupported reference warnings on the sync run record', function () { $tenant = Tenant::factory()->create(); $this->app->bind(GraphClientInterface::class, function () { return new class implements GraphClientInterface { public function listPolicies(string $policyType, array $options = []): GraphResponse { return new GraphResponse(true, [[ 'id' => 'pol-warn-1', 'displayName' => 'Unsupported Assignment Target', 'assignments' => [ ['target' => ['filterId' => 'filter-only-no-group']], ], ]]); } public function getPolicy(string $policyType, string $policyId, array $options = []): GraphResponse { return new GraphResponse(true); } public function getOrganization(array $options = []): GraphResponse { return new GraphResponse(true); } public function applyPolicy(string $policyType, string $policyId, array $payload, array $options = []): GraphResponse { return new GraphResponse(true); } public function getServicePrincipalPermissions(array $options = []): GraphResponse { return new GraphResponse(true); } public function request(string $method, string $path, array $options = []): GraphResponse { return new GraphResponse(true); } }; }); $svc = app(InventorySyncService::class); $run = $svc->syncNow($tenant, [ 'policy_types' => ['deviceConfiguration'], 'categories' => [], 'include_foundations' => false, 'include_dependencies' => true, ]); $warnings = $run->error_context['warnings'] ?? null; expect($warnings)->toBeArray()->toHaveCount(1); expect($warnings[0]['type'] ?? null)->toBe('unsupported_reference'); expect(InventoryLink::query()->where('tenant_id', $tenant->getKey())->count())->toBe(0); });