|
| 1 | +using Microsoft.EntityFrameworkCore; |
| 2 | +using OpenBullet2.Core; |
| 3 | +using OpenBullet2.Core.Repositories; |
| 4 | +using System.Reflection; |
| 5 | + |
| 6 | +namespace OpenBullet2.Native.Tests; |
| 7 | + |
| 8 | +[Collection("WPF")] |
| 9 | +public sealed class UiFactoryArchitectureTests |
| 10 | +{ |
| 11 | + [Fact] |
| 12 | + public void NativeViewsAndViewModels_ShouldNotInjectDbContextOrRepositories() |
| 13 | + { |
| 14 | + var uiAssembly = typeof(OpenBullet2.Native.App).Assembly; |
| 15 | + |
| 16 | + var violations = uiAssembly |
| 17 | + .GetTypes() |
| 18 | + .Where(IsUiType) |
| 19 | + .SelectMany(type => type |
| 20 | + .GetConstructors(BindingFlags.Public | BindingFlags.Instance) |
| 21 | + .SelectMany(ctor => ctor |
| 22 | + .GetParameters() |
| 23 | + .Where(parameter => IsForbidden(parameter.ParameterType)) |
| 24 | + .Select(parameter => |
| 25 | + $"{type.FullName} -> {ctor.DeclaringType?.Name}({string.Join(", ", ctor.GetParameters().Select(p => p.ParameterType.Name))}) injects {parameter.ParameterType.FullName}"))) |
| 26 | + .OrderBy(message => message) |
| 27 | + .ToList(); |
| 28 | + |
| 29 | + Assert.True(violations.Count == 0, |
| 30 | + "UI types created from the root provider must not inject DbContext or EF-backed repositories." |
| 31 | + + $"{Environment.NewLine}{string.Join(Environment.NewLine, violations)}"); |
| 32 | + } |
| 33 | + |
| 34 | + private static bool IsUiType(Type type) |
| 35 | + => type is { IsClass: true, IsAbstract: false } |
| 36 | + && type.Namespace is not null |
| 37 | + && (type.Namespace.StartsWith("OpenBullet2.Native.Views", StringComparison.Ordinal) |
| 38 | + || type.Namespace.StartsWith("OpenBullet2.Native.ViewModels", StringComparison.Ordinal)); |
| 39 | + |
| 40 | + private static bool IsForbidden(Type type) |
| 41 | + => typeof(DbContext).IsAssignableFrom(type) |
| 42 | + || ImplementsOpenGeneric(type, typeof(OpenBullet2.Core.Repositories.IRepository<>)) |
| 43 | + || type == typeof(IWordlistRepository); |
| 44 | + |
| 45 | + private static bool ImplementsOpenGeneric(Type candidateType, Type openGenericType) |
| 46 | + => candidateType.GetInterfaces() |
| 47 | + .Concat([candidateType]) |
| 48 | + .Any(type => type.IsGenericType |
| 49 | + && type.GetGenericTypeDefinition() == openGenericType); |
| 50 | +} |
0 commit comments