1- using SqlServerSimulator . Storage . Bacpac ;
1+ using System . Reflection ;
2+ using SqlServerSimulator . Storage . Bacpac ;
23
34namespace SqlServerSimulator ;
45
@@ -9,45 +10,111 @@ public class QualityTests
910 [ Description ( "Prevents unintentional expansion of the public API." ) ]
1011 public void PublicApiWhitelist ( )
1112 {
12- var simulation = typeof ( Simulation ) ;
13-
14- var types = simulation
13+ var publicTypes = typeof ( Simulation )
1514 . Assembly
1615 . GetTypes ( )
1716 . Where ( type => type . IsPublic )
1817 . ToArray ( ) ;
1918
20- HashSet < Type > allowedTypes = [
21- simulation ,
22- typeof ( TableValuedParameterExtensions ) ,
23- typeof ( BacpacImportOptions ) ,
24- typeof ( BacpacImportResult ) ,
25- typeof ( BacpacSkipped ) ,
26- typeof ( SimulatedDbConnection ) ,
27- typeof ( SimulatedError ) ,
28- typeof ( SimulatedErrorCollection ) ,
29- typeof ( SimulatedInfoMessageEventArgs ) ,
30- ] ;
31- Assert . HasCount ( allowedTypes . Count , types ) ;
32- foreach ( var type in types )
33- Assert . Contains ( type , allowedTypes ) ;
34-
35- var memberNames = simulation
36- . GetMembers ( )
37- . Where ( member => member . DeclaringType == simulation )
38- . Select ( member => member . Name )
39- . ToHashSet ( ) ;
19+ // Per-type whitelist of member names declared directly on the type.
20+ // Property / event / operator accessors and compiler-generated members
21+ // (record <Clone>$, the C# 14 extension's <G>$... nested type) are
22+ // filtered before comparison, so the entries below read as the
23+ // human-meaningful API surface.
24+ Dictionary < Type , HashSet < string > > allowedMembers = new ( )
25+ {
26+ [ typeof ( Simulation ) ] = [
27+ ".ctor" ,
28+ nameof ( Simulation . CreateDbConnection ) ,
29+ nameof ( Simulation . ImportBacpac ) ,
30+ nameof ( Simulation . AddRemoteSimulation ) ,
31+ ] ,
32+ [ typeof ( SimulatedDbConnection ) ] = [
33+ nameof ( SimulatedDbConnection . InfoMessage ) ,
34+ nameof ( SimulatedDbConnection . ConnectionString ) ,
35+ nameof ( SimulatedDbConnection . Database ) ,
36+ nameof ( SimulatedDbConnection . DataSource ) ,
37+ nameof ( SimulatedDbConnection . ServerVersion ) ,
38+ nameof ( SimulatedDbConnection . State ) ,
39+ nameof ( SimulatedDbConnection . ChangeDatabase ) ,
40+ nameof ( SimulatedDbConnection . Close ) ,
41+ nameof ( SimulatedDbConnection . Open ) ,
42+ ] ,
43+ [ typeof ( SimulatedError ) ] = [
44+ nameof ( SimulatedError . Class ) ,
45+ nameof ( SimulatedError . LineNumber ) ,
46+ nameof ( SimulatedError . Message ) ,
47+ nameof ( SimulatedError . Number ) ,
48+ nameof ( SimulatedError . Procedure ) ,
49+ nameof ( SimulatedError . Server ) ,
50+ nameof ( SimulatedError . Source ) ,
51+ nameof ( SimulatedError . State ) ,
52+ nameof ( SimulatedError . ToString ) ,
53+ ] ,
54+ [ typeof ( SimulatedErrorCollection ) ] = [
55+ "Item" ,
56+ nameof ( SimulatedErrorCollection . Count ) ,
57+ nameof ( SimulatedErrorCollection . CopyTo ) ,
58+ nameof ( SimulatedErrorCollection . GetEnumerator ) ,
59+ ] ,
60+ [ typeof ( SimulatedInfoMessageEventArgs ) ] = [
61+ nameof ( SimulatedInfoMessageEventArgs . Errors ) ,
62+ nameof ( SimulatedInfoMessageEventArgs . LineNumber ) ,
63+ nameof ( SimulatedInfoMessageEventArgs . Message ) ,
64+ nameof ( SimulatedInfoMessageEventArgs . Source ) ,
65+ ] ,
66+ // C# 14 lowers the extension(DbParameter) block to static
67+ // accessor methods on the host class plus a compiler-generated
68+ // <G>$... nested type carrying the cross-assembly metadata. The
69+ // nested type is filtered as compiler-generated; the accessors
70+ // surface as regular (non-special-name) methods here.
71+ [ typeof ( TableValuedParameterExtensions ) ] = [
72+ "get_TypeName" ,
73+ "set_TypeName" ,
74+ ] ,
75+ [ typeof ( BacpacImportOptions ) ] = [
76+ ".ctor" ,
77+ nameof ( BacpacImportOptions . DatabaseName ) ,
78+ nameof ( BacpacImportOptions . MaxDegreeOfParallelism ) ,
79+ nameof ( Equals ) ,
80+ nameof ( GetHashCode ) ,
81+ nameof ( ToString ) ,
82+ ] ,
83+ [ typeof ( BacpacImportResult ) ] = [
84+ ".ctor" ,
85+ nameof ( BacpacImportResult . ElementCounts ) ,
86+ nameof ( BacpacImportResult . Skipped ) ,
87+ nameof ( BacpacImportResult . Warnings ) ,
88+ ] ,
89+ [ typeof ( BacpacSkipped ) ] = [
90+ ".ctor" ,
91+ "Deconstruct" ,
92+ nameof ( BacpacSkipped . ElementName ) ,
93+ nameof ( BacpacSkipped . ElementType ) ,
94+ nameof ( BacpacSkipped . Reason ) ,
95+ nameof ( Equals ) ,
96+ nameof ( GetHashCode ) ,
97+ nameof ( ToString ) ,
98+ ] ,
99+ } ;
40100
41- HashSet < string > allowedMemberNames = [
42- ".ctor" ,
43- nameof ( Simulation . CreateDbConnection ) ,
44- nameof ( Simulation . ImportBacpac ) ,
45- nameof ( Simulation . AddRemoteSimulation ) ,
46- ] ;
101+ Assert . HasCount ( allowedMembers . Count , publicTypes ) ;
102+ foreach ( var type in publicTypes )
103+ Assert . Contains ( type , allowedMembers . Keys ) ;
47104
48- Assert . HasCount ( allowedMemberNames . Count , memberNames ) ;
105+ foreach ( var ( type , allowedNames ) in allowedMembers )
106+ {
107+ var memberNames = type
108+ . GetMembers ( )
109+ . Where ( member => member . DeclaringType == type )
110+ . Where ( member => member . Name [ 0 ] != '<' )
111+ . Where ( member => member is not MethodInfo mi || ! mi . IsSpecialName )
112+ . Select ( member => member . Name )
113+ . ToHashSet ( ) ;
49114
50- foreach ( var name in memberNames )
51- Assert . Contains ( name , allowedMemberNames ) ;
115+ Assert . HasCount ( allowedNames . Count , memberNames , $ "Member count mismatch on { type . FullName } ") ;
116+ foreach ( var name in memberNames )
117+ Assert . Contains ( name , allowedNames , $ "Unexpected public member '{ name } ' on { type . FullName } ") ;
118+ }
52119 }
53120}
0 commit comments