using Admin.NET.Plugin.AiDOP.Controllers; using Admin.NET.Plugin.AiDOP.Infrastructure; using Admin.NET.Plugin.AiDOP.Service; using Admin.NET.Plugin.AiDOP.SmartOps; using Xunit; namespace Admin.NET.Plugin.AiDOP.Tests.SmartOps; public class SmartOpsTrustedScopeTests { private const long TenantA = 838257186181189; private const long FactoryA = 838257186320453; private const long FactoryB = 838257212858437; [Fact] public void PlatformTenant_IsRejected() { Assert.Throws(() => SmartOpsTrustedScopeRules.EnsureBusinessTenant(SmartOpsTrustedScopeRules.PlatformTenantId)); Assert.Throws(() => SmartOpsTrustedScopeRules.EnsureBusinessTenant(0)); Assert.Throws(() => SmartOpsTrustedScopeRules.EnsureBusinessTenant(1)); } [Fact] public void BusinessTenant_IsAccepted() { SmartOpsTrustedScopeRules.EnsureBusinessTenant(TenantA); } [Theory] [InlineData(838257186181189, 838257186320453, 1L, 838257186320453)] [InlineData(838257186181189, 838257186320453, 838257212858437L, 838257186320453)] [InlineData(838257186181189, 838257186320453, 838257186181189L, 838257186320453)] [InlineData(838257186181189, 838257186320453, 838257186320453L, 838257186320453)] public void ClientFactory_IsNeverUsedAsPermissionBoundary( long tenantId, long orgId, long candidate, long expected) { Assert.Equal(expected, SmartOpsTrustedScopeRules.ResolveFactory(tenantId, orgId, candidate)); } [Fact] public void MissingOrSelfFactory_IsRejected() { Assert.Throws(() => SmartOpsTrustedScopeRules.ResolveFactory(TenantA, 0, FactoryA)); Assert.Throws(() => SmartOpsTrustedScopeRules.ResolveFactory(TenantA, TenantA, FactoryA)); } [Fact] public void AuthorizedFactory_MatchesTenantOrgOnly() { Assert.True(SmartOpsTrustedScopeRules.IsAuthorizedFactory(TenantA, FactoryA, FactoryA)); Assert.False(SmartOpsTrustedScopeRules.IsAuthorizedFactory(TenantA, FactoryA, FactoryB)); } [Fact] public void OperationalFactory_AcceptsLegacyDefaultAndTrustedOrg() { Assert.True(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, 1)); Assert.True(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, FactoryA)); Assert.True(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, 0)); Assert.True(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, null)); Assert.False(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, TenantA)); Assert.False(SmartOpsOperationalFactory.Matches(TenantA, FactoryA, FactoryB)); } [Fact] public void PlanNo_IsTenantScopedAndMonotonic() { var prefix = SmartOpsImprovementPlanNo.BuildPrefix(new DateTime(2026, 8, 17)); Assert.Equal("SOP-20260817-", prefix); Assert.Equal("SOP-20260817-001", SmartOpsImprovementPlanNo.Next(prefix, Array.Empty())); Assert.Equal( "SOP-20260817-003", SmartOpsImprovementPlanNo.Next(prefix, new[] { "SOP-20260817-001", "SOP-20260817-002" })); } [Fact] public void ConcurrentPlanNo_DoesNotReuseExistingNumbers() { var prefix = "SOP-20260817-"; var existing = new List(); var generated = new HashSet(); for (var i = 0; i < 20; i++) { var next = SmartOpsImprovementPlanNo.Next(prefix, existing); Assert.True(generated.Add(next)); existing.Add(next); } Assert.Equal(20, generated.Count); Assert.Contains("SOP-20260817-020", generated); } [Fact] public void DiagnosisAndImprovement_UseTrustedScopeNotRequestTenant() { var diagnosis = typeof(AidopKanbanController).GetMethod(nameof(AidopKanbanController.GetSmartDiagnosis)); Assert.NotNull(diagnosis); var controllerSource = ReadSiblingSource( typeof(AdoSmartOpsImprovementPlanController), "AdoSmartOpsImprovementPlanController.cs"); var diagnosisSource = ReadSiblingSource( typeof(AidopKanbanController), "AidopKanbanController.Diagnosis.cs"); Assert.DoesNotContain("AidopTenantHelper.GetTenantId", controllerSource); Assert.DoesNotContain("AidopTenantHelper.GetTenantId", diagnosisSource); Assert.Contains("_trustedScope.ResolveAsync", controllerSource); Assert.Contains("_trustedScope.ResolveAsync", diagnosisSource); Assert.Contains("x.TenantId == plan.TenantId", controllerSource); Assert.Contains("x.TenantId == tenantId", controllerSource); Assert.DoesNotContain("[AllowAnonymous]", controllerSource); Assert.DoesNotContain("FactoryId = input.FactoryId", controllerSource); } [Fact] public void ApproverGuard_ClearsFiltersWhenResolvingRoleMembers() { var source = ReadSiblingSource( typeof(SmartOpsImprovementApproverGuard), "SmartOpsImprovementApproverGuard.cs"); Assert.Contains("Queryable()", source); Assert.Contains(".ClearFilter()", source); Assert.Contains("u.TenantId == tenantId", source); } [Fact] public void ApprovalCallback_UpdatesWithTenantCondition() { var source = ReadSiblingSource( typeof(SmartOpsImprovementBizHandler), "SmartOpsImprovementBizHandler.cs"); Assert.Contains("x.TenantId == plan.TenantId", source); Assert.DoesNotContain(".Where(x => x.Id == bizId)", source.Replace("x.Id == bizId && x.TenantId == plan.TenantId", "")); } private static string ReadSiblingSource(Type type, string fileName) { var root = FindRepoRoot(); var matches = Directory.GetFiles(root, fileName, SearchOption.AllDirectories) .Where(path => path.Replace('\\', '/').Contains("/Admin.NET.Plugin.AiDOP/") && !path.Contains(".Tests")) .ToList(); Assert.NotEmpty(matches); return File.ReadAllText(matches[0]); } private static string FindRepoRoot() { var dir = new DirectoryInfo(AppContext.BaseDirectory); while (dir != null) { if (File.Exists(Path.Combine(dir.FullName, "AGENTS.md"))) return dir.FullName; dir = dir.Parent; } return Path.GetFullPath(Path.Combine(AppContext.BaseDirectory, "..", "..", "..", "..", "..", "..")); } }