@@ -87,3 +87,114 @@ extension CurrencyAmount: ExpressibleByFloatLiteral {
8787 self . value =  Decimal ( value) 
8888 } 
8989} 
90+ 91+ private  func  assertThat( _ dictionary:  [ String :  Any ] , 
92+  file:  StaticString  =  #file, 
93+  line:  UInt  =  #line)  ->  DictionarySubject  { 
94+  return  DictionarySubject ( dictionary,  file:  file,  line:  line) 
95+ } 
96+ 97+ func  assertThat< X:  Equatable  &  Codable > ( _ model:  X ,  file:  StaticString  =  #file, 
98+  line:  UInt  =  #line)  ->  CodableSubject < X >  { 
99+  return  CodableSubject ( model,  file:  file,  line:  line) 
100+ } 
101+ 102+ func  assertThat< X:  Equatable  &  Encodable > ( _ model:  X ,  file:  StaticString  =  #file, 
103+  line:  UInt  =  #line)  ->  EncodableSubject < X >  { 
104+  return  EncodableSubject ( model,  file:  file,  line:  line) 
105+ } 
106+ 107+ class  EncodableSubject < X:  Equatable  &  Encodable >  { 
108+  var  subject :  X 
109+  var  file :  StaticString 
110+  var  line :  UInt 
111+ 112+  init ( _ subject:  X ,  file:  StaticString ,  line:  UInt )  { 
113+  self . subject =  subject
114+  self . file =  file
115+  self . line =  line
116+  } 
117+ 118+  @discardableResult  
119+  func  encodes( to expected:  [ String :  Any ] , 
120+  using encoder:  Database . Encoder  =  . init( ) )  ->  DictionarySubject  { 
121+  let  encoded  =  assertEncodes ( to:  expected,  using:  encoder) 
122+  return  DictionarySubject ( encoded,  file:  file,  line:  line) 
123+  } 
124+ 125+  func  failsToEncode( )  { 
126+  do  { 
127+  let  encoder  =  Database . Encoder ( ) 
128+  encoder. keyEncodingStrategy =  . convertToSnakeCase
129+  _ =  try   encoder. encode ( subject) 
130+  }  catch  { 
131+  return 
132+  } 
133+  XCTFail ( " Failed to throw " ) 
134+  } 
135+ 136+  func  failsEncodingAtTopLevel( )  { 
137+  do  { 
138+  let  encoder  =  Database . Encoder ( ) 
139+  encoder. keyEncodingStrategy =  . convertToSnakeCase
140+  _ =  try   encoder. encode ( subject) 
141+  XCTFail ( " Failed to throw " ,  file:  file,  line:  line) 
142+  }  catch  EncodingError . invalidValue( _,  _)  { 
143+  return 
144+  }  catch  { 
145+  XCTFail ( " Unrecognized error:  \( error) " ,  file:  file,  line:  line) 
146+  } 
147+  } 
148+ 149+  private  func  assertEncodes( to expected:  [ String :  Any ] , 
150+  using encoder:  Database . Encoder  =  . init( ) )  ->  [ String :  Any ]  { 
151+  do  { 
152+  let  enc  =  try   encoder. encode ( subject) 
153+  XCTAssertEqual ( enc as?  NSDictionary ,  expected as  NSDictionary ,  file:  file,  line:  line) 
154+  return  ( enc as!  NSDictionary )  as!  [ String :  Any ] 
155+  }  catch  { 
156+  XCTFail ( " Failed to encode  \( X . self) : error:  \( error) " ) 
157+  return  [ " " :  - 1 ] 
158+  } 
159+  } 
160+ } 
161+ 162+ class  CodableSubject < X:  Equatable  &  Codable > :  EncodableSubject < X >  { 
163+  func  roundTrips( to expected:  [ String :  Any ] , 
164+  using encoder:  Database . Encoder  =  . init( ) , 
165+  decoder:  Database . Decoder  =  . init( ) )  { 
166+  let  reverseSubject  =  encodes ( to:  expected,  using:  encoder) 
167+  reverseSubject. decodes ( to:  subject,  using:  decoder) 
168+  } 
169+ } 
170+ 171+ class  DictionarySubject  { 
172+  var  subject :  [ String :  Any ] 
173+  var  file :  StaticString 
174+  var  line :  UInt 
175+ 176+  init ( _ subject:  [ String :  Any ] ,  file:  StaticString ,  line:  UInt )  { 
177+  self . subject =  subject
178+  self . file =  file
179+  self . line =  line
180+  } 
181+ 182+  func  decodes< X:  Equatable  &  Codable > ( to expected:  X , 
183+  using decoder:  Database . Decoder  =  . init( ) )  ->  Void  { 
184+  do  { 
185+  let  decoded  =  try   decoder. decode ( X . self,  from:  subject) 
186+  XCTAssertEqual ( decoded,  expected) 
187+  }  catch  { 
188+  XCTFail ( " Failed to decode  \( X . self) :  \( error) " ,  file:  file,  line:  line) 
189+  } 
190+  } 
191+ 192+  func  failsDecoding< X:  Equatable  &  Codable > ( to _:  X . Type , 
193+  using decoder:  Database . Decoder  =  . init( ) )  ->  Void  { 
194+  XCTAssertThrowsError ( 
195+  try   decoder. decode ( X . self,  from:  subject) , 
196+  file:  file, 
197+  line:  line
198+  ) 
199+  } 
200+ } 
0 commit comments