@ -1,6 +1,7 @@
// C o p y r i g h t © 2 0 2 2 R a n g e p r o o f P t y L t d . A l l r i g h t s r e s e r v e d .
import Foundation
import GRDB
import Sodium
import SessionUtil
import SessionUtilitiesKit
@ -8,12 +9,248 @@ import SessionUtilitiesKit
import Quick
import Nimble
@ testable import SessionMessagingKit
// / T h i s s p e c i s d e s i g n e d t o r e p l i c a t e t h e i n i t i a l t e s t c a s e s f o r t h e l i b S e s s i o n - u t i l t o e n s u r e t h e b e h a v i o u r m a t c h e s
class ConfigContactsSpec {
enum ContactProperty : CaseIterable {
case name
case nickname
case approved
case approved_me
case blocked
case profile_pic
case created
case notifications
case mute_until
}
// MARK: - S p e c
static func spec ( ) {
it ( " generates Contact configs correctly " ) {
context ( " CONTACTS " ) {
// MARK: - w h e n c h e c k i n g e r r o r c a t c h i n g
context ( " when checking error catching " ) {
var seed : Data !
var identity : ( ed25519KeyPair : KeyPair , x25519KeyPair : KeyPair ) !
var edSK : [ UInt8 ] !
var error : UnsafeMutablePointer < CChar > ?
var conf : UnsafeMutablePointer < config_object > ?
beforeEach {
seed = Data ( hex : " 0123456789abcdef0123456789abcdef " )
// FIXME: W o u l d b e g o o d t o m o v e t h e s e i n t o t h e l i b S e s s i o n - u t i l i n s t e a d o f u s i n g S o d i u m s e p a r a t e l y
identity = try ! Identity . generate ( from : seed )
edSK = identity . ed25519KeyPair . secretKey
// I n i t i a l i z e a b r a n d n e w , e m p t y c o n f i g b e c a u s e w e h a v e n o d u m p d a t a t o d e a l w i t h .
error = nil
conf = nil
_ = contacts_init ( & conf , & edSK , nil , 0 , error )
error ? . deallocate ( )
}
// MARK: - - i t c a n c a t c h s i z e l i m i t e r r o r s t h r o w n w h e n p u s h i n g
it ( " can catch size limit errors thrown when pushing " ) {
try ( 0. . < 10000 ) . forEach { index in
var contact : contacts_contact = try createContact ( for : index , in : conf , maxing : . allProperties )
contacts_set ( conf , & contact )
}
expect ( contacts_size ( conf ) ) . to ( equal ( 10000 ) )
expect ( config_needs_push ( conf ) ) . to ( beTrue ( ) )
expect ( config_needs_dump ( conf ) ) . to ( beTrue ( ) )
expect {
try CExceptionHelper . performSafely { config_push ( conf ) . deallocate ( ) }
}
. to ( throwError ( NSError ( domain : " cpp_exception " , code : - 2 , userInfo : [ " NSLocalizedDescription " : " Config data is too large " ] ) ) )
}
// MARK: - - c a n c a t c h s i z e l i m i t e r r o r s t h r o w n w h e n d u m p i n g
it ( " can catch size limit errors thrown when dumping " ) {
try ( 0. . < 10000 ) . forEach { index in
var contact : contacts_contact = try createContact ( for : index , in : conf , maxing : . allProperties )
contacts_set ( conf , & contact )
}
expect ( contacts_size ( conf ) ) . to ( equal ( 10000 ) )
expect ( config_needs_push ( conf ) ) . to ( beTrue ( ) )
expect ( config_needs_dump ( conf ) ) . to ( beTrue ( ) )
expect {
try CExceptionHelper . performSafely {
var dump : UnsafeMutablePointer < UInt8 > ? = nil
var dumpLen : Int = 0
config_dump ( conf , & dump , & dumpLen )
dump ? . deallocate ( )
}
}
. to ( throwError ( NSError ( domain : " cpp_exception " , code : - 2 , userInfo : [ " NSLocalizedDescription " : " Config data is too large " ] ) ) )
}
}
// MARK: - w h e n c h e c k i n g s i z e l i m i t s
context ( " when checking size limits " ) {
var numRecords : Int !
var seed : Data !
var identity : ( ed25519KeyPair : KeyPair , x25519KeyPair : KeyPair ) !
var edSK : [ UInt8 ] !
var error : UnsafeMutablePointer < CChar > ?
var conf : UnsafeMutablePointer < config_object > ?
beforeEach {
numRecords = 0
seed = Data ( hex : " 0123456789abcdef0123456789abcdef " )
// FIXME: W o u l d b e g o o d t o m o v e t h e s e i n t o t h e l i b S e s s i o n - u t i l i n s t e a d o f u s i n g S o d i u m s e p a r a t e l y
identity = try ! Identity . generate ( from : seed )
edSK = identity . ed25519KeyPair . secretKey
// I n i t i a l i z e a b r a n d n e w , e m p t y c o n f i g b e c a u s e w e h a v e n o d u m p d a t a t o d e a l w i t h .
error = nil
conf = nil
_ = contacts_init ( & conf , & edSK , nil , 0 , error )
error ? . deallocate ( )
}
// MARK: - - h a s n o t c h a n g e d t h e m a x e m p t y r e c o r d s
it ( " has not changed the max empty records " ) {
for index in ( 0. . < 10000 ) {
var contact : contacts_contact = try createContact ( for : index , in : conf )
contacts_set ( conf , & contact )
do { try CExceptionHelper . performSafely { config_push ( conf ) . deallocate ( ) } }
catch { break }
// W e s u c c e s s f u l l y i n s e r t e d a c o n t a c t a n d d i d n ' t h i t t h e l i m i t s o i n c r e m e n t t h e c o u n t e r
numRecords += 1
}
// C h e c k t h a t t h e r e c o r d c o u n t m a t c h e s t h e m a x i m u m w h e n w e l a s t c h e c k e d
expect ( numRecords ) . to ( equal ( 1775 ) )
}
// MARK: - - h a s n o t c h a n g e d t h e m a x n a m e o n l y r e c o r d s
it ( " has not changed the max name only records " ) {
for index in ( 0. . < 10000 ) {
var contact : contacts_contact = try createContact ( for : index , in : conf , maxing : [ . name ] )
contacts_set ( conf , & contact )
do { try CExceptionHelper . performSafely { config_push ( conf ) . deallocate ( ) } }
catch { break }
// W e s u c c e s s f u l l y i n s e r t e d a c o n t a c t a n d d i d n ' t h i t t h e l i m i t s o i n c r e m e n t t h e c o u n t e r
numRecords += 1
}
// C h e c k t h a t t h e r e c o r d c o u n t m a t c h e s t h e m a x i m u m w h e n w e l a s t c h e c k e d
expect ( numRecords ) . to ( equal ( 526 ) )
}
// MARK: - - h a s n o t c h a n g e d t h e m a x n a m e a n d p r o f i l e p i c o n l y r e c o r d s
it ( " has not changed the max name and profile pic only records " ) {
for index in ( 0. . < 10000 ) {
var contact : contacts_contact = try createContact ( for : index , in : conf , maxing : [ . name , . profile_pic ] )
contacts_set ( conf , & contact )
do { try CExceptionHelper . performSafely { config_push ( conf ) . deallocate ( ) } }
catch { break }
// W e s u c c e s s f u l l y i n s e r t e d a c o n t a c t a n d d i d n ' t h i t t h e l i m i t s o i n c r e m e n t t h e c o u n t e r
numRecords += 1
}
// C h e c k t h a t t h e r e c o r d c o u n t m a t c h e s t h e m a x i m u m w h e n w e l a s t c h e c k e d
expect ( numRecords ) . to ( equal ( 184 ) )
}
// MARK: - - h a s n o t c h a n g e d t h e m a x f i l l e d r e c o r d s
it ( " has not changed the max filled records " ) {
for index in ( 0. . < 10000 ) {
var contact : contacts_contact = try createContact ( for : index , in : conf , maxing : . allProperties )
contacts_set ( conf , & contact )
do { try CExceptionHelper . performSafely { config_push ( conf ) . deallocate ( ) } }
catch { break }
// W e s u c c e s s f u l l y i n s e r t e d a c o n t a c t a n d d i d n ' t h i t t h e l i m i t s o i n c r e m e n t t h e c o u n t e r
numRecords += 1
}
// C h e c k t h a t t h e r e c o r d c o u n t m a t c h e s t h e m a x i m u m w h e n w e l a s t c h e c k e d
expect ( numRecords ) . to ( equal ( 134 ) )
}
}
// MARK: - w h e n p r u n i n g
context ( " when pruning " ) {
var mockStorage : Storage !
var seed : Data !
var identity : ( ed25519KeyPair : KeyPair , x25519KeyPair : KeyPair ) !
var edSK : [ UInt8 ] !
var error : UnsafeMutablePointer < CChar > ?
var conf : UnsafeMutablePointer < config_object > ?
beforeEach {
mockStorage = Storage (
customWriter : try ! DatabaseQueue ( ) ,
customMigrations : [
SNUtilitiesKit . migrations ( ) ,
SNMessagingKit . migrations ( )
]
)
seed = Data ( hex : " 0123456789abcdef0123456789abcdef " )
// FIXME: W o u l d b e g o o d t o m o v e t h e s e i n t o t h e l i b S e s s i o n - u t i l i n s t e a d o f u s i n g S o d i u m s e p a r a t e l y
identity = try ! Identity . generate ( from : seed )
edSK = identity . ed25519KeyPair . secretKey
// I n i t i a l i z e a b r a n d n e w , e m p t y c o n f i g b e c a u s e w e h a v e n o d u m p d a t a t o d e a l w i t h .
error = nil
conf = nil
_ = contacts_init ( & conf , & edSK , nil , 0 , error )
error ? . deallocate ( )
}
it ( " does something " ) {
mockStorage . write { db in
try SessionThread . fetchOrCreate ( db , id : " 1 " , variant : . contact , shouldBeVisible : true )
try SessionThread . fetchOrCreate ( db , id : " 2 " , variant : . contact , shouldBeVisible : true )
try SessionThread . fetchOrCreate ( db , id : " 3 " , variant : . contact , shouldBeVisible : true )
_ = try Interaction (
threadId : " 1 " ,
authorId : " 1 " ,
variant : . standardIncoming ,
body : " Test1 "
) . inserted ( db )
_ = try Interaction (
threadId : " 1 " ,
authorId : " 2 " ,
variant : . standardIncoming ,
body : " Test2 "
) . inserted ( db )
_ = try Interaction (
threadId : " 3 " ,
authorId : " 3 " ,
variant : . standardIncoming ,
body : " Test3 "
) . inserted ( db )
try SessionUtil . pruningIfNeeded (
db ,
conf : conf
)
expect ( contacts_size ( conf ) ) . to ( equal ( 0 ) )
}
}
}
// MARK: - g e n e r a t e s c o n f i g c o r r e c t l y
it ( " generates config correctly " ) {
let createdTs : Int64 = 1680064059
let nowTs : Int64 = Int64 ( Date ( ) . timeIntervalSince1970 )
let seed : Data = Data ( hex : " 0123456789abcdef0123456789abcdef " )
@ -304,3 +541,69 @@ class ConfigContactsSpec {
}
}
}
// MARK: - C o n v e n i e n c e
private static func createContact (
for index : Int ,
in conf : UnsafeMutablePointer < config_object > ? ,
maxing properties : [ ContactProperty ] = [ ]
) throws -> contacts_contact {
let postPrefixId : String = " 050000000000000000000000000000000000000000000000000000000000000000 "
let sessionId : String = ( " 05 \( index ) a " + postPrefixId . suffix ( postPrefixId . count - " 05 \( index ) a " . count ) )
var cSessionId : [ CChar ] = sessionId . cArray . nullTerminated ( )
var contact : contacts_contact = contacts_contact ( )
guard contacts_get_or_construct ( conf , & contact , & cSessionId ) else {
throw SessionUtilError . getOrConstructFailedUnexpectedly
}
// S e t t h e v a l u e s t o t h e m a x i m u m d a t a t h a t c a n f i t
properties . forEach { property in
switch property {
case . approved : contact . approved = true
case . approved_me : contact . approved_me = true
case . blocked : contact . blocked = true
case . created : contact . created = Int64 . max
case . notifications : contact . notifications = CONVO_NOTIFY_MENTIONS_ONLY
case . mute_until : contact . mute_until = Int64 . max
case . name :
contact . name = String (
data : Data (
repeating : " A " . data ( using : . utf8 ) ! [ 0 ] ,
count : SessionUtil . libSessionMaxNameByteLength
) ,
encoding : . utf8
) . toLibSession ( )
case . nickname :
contact . nickname = String (
data : Data (
repeating : " A " . data ( using : . utf8 ) ! [ 0 ] ,
count : SessionUtil . libSessionMaxNameByteLength
) ,
encoding : . utf8
) . toLibSession ( )
case . profile_pic :
contact . profile_pic = user_profile_pic (
url : String (
data : Data (
repeating : " A " . data ( using : . utf8 ) ! [ 0 ] ,
count : SessionUtil . libSessionMaxProfileUrlByteLength
) ,
encoding : . utf8
) . toLibSession ( ) ,
key : " qwerty78901234567890123456789012 " . data ( using : . utf8 ) ! . toLibSession ( )
)
}
}
return contact
}
}
fileprivate extension Array where Element = = ConfigContactsSpec . ContactProperty {
static var allProperties : [ ConfigContactsSpec . ContactProperty ] = ConfigContactsSpec . ContactProperty . allCases
}