Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2577636
send-test.cpp
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Award Token
Flag For Later
Size
11 KB
Referenced Files
None
Subscribers
None
send-test.cpp
View Options
/*
* This file is part of libkazv.
* SPDX-FileCopyrightText: 2021-2023 tusooa <tusooa@kazv.moe>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
#include
<libkazv-config.hpp>
#include
<lager/event_loop/boost_asio.hpp>
#include
<catch2/catch_all.hpp>
#include
<boost/asio.hpp>
#include
<asio-promise-handler.hpp>
#include
<cursorutil.hpp>
#include
<sdk-model.hpp>
#include
<client/client.hpp>
#include
"client-test-util.hpp"
#include
"factory.hpp"
using
namespace
Kazv
::
Factory
;
TEST_CASE
(
"Send a message"
,
"[client][send]"
)
{
ClientModel
loggedInModel
=
makeClient
({});
auto
[
resModel
,
dontCareEffect
]
=
ClientModel
::
update
(
loggedInModel
,
SendMessageAction
{
"!foo:tusooa.xyz"
,
json
{
{
"type"
,
"m.room.message"
},
{
"content"
,
{{
"foo"
,
"bar"
}}},
}});
assert1Job
(
resModel
);
for1stJob
(
resModel
,
[]
(
const
auto
&
job
)
{
REQUIRE
(
job
.
jobId
()
==
"SendMessage"
);
REQUIRE
(
job
.
dataStr
(
"roomId"
)
==
"!foo:tusooa.xyz"
);
REQUIRE
(
job
.
dataStr
(
"txnId"
)
!=
""
);
});
}
TEST_CASE
(
"Send a message with specified txnId"
,
"[client][send]"
)
{
ClientModel
loggedInModel
=
makeClient
({});
auto
[
resModel
,
dontCareEffect
]
=
ClientModel
::
update
(
loggedInModel
,
SendMessageAction
{
"!foo:tusooa.xyz"
,
json
{
{
"type"
,
"m.room.message"
},
{
"content"
,
{{
"foo"
,
"bar"
}}},
},
"some-txn-id"
});
assert1Job
(
resModel
);
for1stJob
(
resModel
,
[]
(
const
auto
&
job
)
{
REQUIRE
(
job
.
jobId
()
==
"SendMessage"
);
REQUIRE
(
job
.
dataStr
(
"roomId"
)
==
"!foo:tusooa.xyz"
);
REQUIRE
(
job
.
dataStr
(
"txnId"
)
==
"some-txn-id"
);
});
}
TEST_CASE
(
"Refuse to send unencrypted event to encrypted room"
,
"[client][send]"
)
{
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
ClientModel
m
=
makeClient
(
withCrypto
(
makeCrypto
({}))
|
withRoom
(
makeRoom
(
withRoomId
(
"!exampleroomid:example.com"
)
|
withRoomEncrypted
(
true
)
))
);
auto
store
=
createTestClientStoreFrom
(
m
,
ph
);
store
.
dispatch
(
SendMessageAction
{
"!exampleroomid:example.com"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"bar"
}}},
{
"type"
,
"m.room.message"
}}},
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
REQUIRE
(
status
.
dataStr
(
"errorCode"
)
==
"MOE_KAZV_MXC_SENDING_UNENCRYPTED_EVENT_TO_ENCRYPTED_ROOM"
);
});
io
.
run
();
}
TEST_CASE
(
"Ok to send unencrypted state event to encrypted room"
,
"[client][send]"
)
{
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
ClientModel
m
=
makeClient
(
withCrypto
(
makeCrypto
({}))
|
withRoom
(
makeRoom
(
withRoomId
(
"!exampleroomid:example.com"
)
|
withRoomEncrypted
(
true
)
))
);
auto
store
=
createTestClientStoreFrom
(
m
,
ph
);
store
.
dispatch
(
SendMessageAction
{
"!exampleroomid:example.com"
,
Event
{
json
{
{
"content"
,
{{
"foo"
,
"bar"
}}},
{
"type"
,
"m.room.xxx"
},
{
"state_key"
,
""
},
}},
})
.
then
([](
auto
status
)
{
REQUIRE
(
status
.
success
());
});
io
.
run
();
}
TEST_CASE
(
"Refuse to send unencrypted m.room_key event"
,
"[client][send]"
)
{
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
ClientModel
m
=
makeClient
(
withCrypto
(
makeCrypto
({}))
|
withRoom
(
makeRoom
(
withRoomId
(
"!exampleroomid:example.com"
)
|
withRoomEncrypted
(
true
)
))
);
auto
store
=
createTestClientStoreFrom
(
m
,
ph
);
store
.
dispatch
(
SendToDeviceMessageAction
{
Event
{
json
{{
"content"
,
{{
"foo"
,
"bar"
}}},
{
"type"
,
"m.room_key"
}}},
{},
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
REQUIRE
(
status
.
dataStr
(
"errorCode"
)
==
"MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED"
);
});
io
.
run
();
}
TEST_CASE
(
"SendMultipleToDeviceMessagesAction"
,
"[client][send]"
)
{
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
ClientModel
m
=
makeClient
(
withCrypto
(
makeCrypto
({}))
|
withRoom
(
makeRoom
(
withRoomId
(
"!exampleroomid:example.com"
)
|
withRoomEncrypted
(
true
)
))
);
auto
store
=
createTestClientStoreFrom
(
m
,
ph
);
SECTION
(
"Reject unencrypted m.room_key event"
)
{
store
.
dispatch
(
SendMultipleToDeviceMessagesAction
{
{{
"@foo:example.com"
,
{{
"device1"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"bar"
}}},
{
"type"
,
"m.room_key"
}}}}},
}},
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
REQUIRE
(
status
.
dataStr
(
"errorCode"
)
==
"MOE_KAZV_MXC_SENDING_ROOM_KEY_EVENT_UNENCRYPTED"
);
});
}
SECTION
(
"Make a job"
)
{
store
.
dispatch
(
SendMultipleToDeviceMessagesAction
{
{{
"@foo:example.com"
,
{{
"device1"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"1"
}}},
{
"type"
,
"moe.kazv.mxc.some-type"
}}}},
{
"device2"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"2"
}}},
{
"type"
,
"moe.kazv.mxc.some-type"
}}}}},
}},
})
.
then
([
&
store
](
auto
status
)
{
REQUIRE
(
status
.
success
());
auto
next
=
store
.
reader
().
make
().
get
();
assert1Job
(
next
);
for1stJob
(
next
,
[](
const
BaseJob
&
job
)
{
auto
expectedBody
=
json
{
{
"messages"
,
{
{
"@foo:example.com"
,
{
{
"device1"
,
{{
"foo"
,
"1"
}}},
{
"device2"
,
{{
"foo"
,
"2"
}}},
}},
}},
};
REQUIRE
(
json
::
parse
(
std
::
get
<
Bytes
>
(
job
.
requestBody
()))
==
expectedBody
);
REQUIRE
(
job
.
url
().
find
(
"/sendToDevice/moe.kazv.mxc.some-type/"
)
!=
std
::
string
::
npos
);
});
});
}
SECTION
(
"Make no job if there is nothing to send"
)
{
store
.
dispatch
(
SendMultipleToDeviceMessagesAction
{
{{
"@foo:example.com"
,
{},
}},
})
.
then
([
&
store
](
auto
status
)
{
REQUIRE
(
status
.
success
());
auto
next
=
store
.
reader
().
make
().
get
();
REQUIRE
(
next
.
nextJobs
.
empty
());
});
}
SECTION
(
"Reject if events are not of the same type"
)
{
store
.
dispatch
(
SendMultipleToDeviceMessagesAction
{
{{
"@foo:example.com"
,
{{
"device1"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"1"
}}},
{
"type"
,
"moe.kazv.mxc.some-type-2"
}}}},
{
"device2"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"2"
}}},
{
"type"
,
"moe.kazv.mxc.some-type"
}}}}},
}},
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
REQUIRE
(
status
.
dataStr
(
"errorCode"
)
==
"MOE_KAZV_MXC_TYPE_NOT_SAME"
);
});
}
SECTION
(
"Reject if some event is not well-formed"
)
{
store
.
dispatch
(
SendMultipleToDeviceMessagesAction
{
{{
"@foo:example.com"
,
{{
"device1"
,
Event
{
json
{{
"type"
,
"moe.kazv.mxc.some-type"
}}}},
{
"device2"
,
Event
{
json
{{
"content"
,
{{
"foo"
,
"2"
}}},
{
"type"
,
"moe.kazv.mxc.some-type"
}}}}},
}},
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
REQUIRE
(
status
.
dataStr
(
"errorCode"
)
==
"MOE_KAZV_MXC_INVALID_EVENT_FORMAT"
);
});
}
io
.
run
();
}
TEST_CASE
(
"UpdateLocalEchoStatusAction: update one does not exist"
,
"[client][send]"
)
{
ClientModel
loggedInModel
=
makeClient
(
withRoom
(
makeRoom
(
withRoomId
(
"!foo:tusooa.xyz"
)
|
withRoomEncrypted
(
true
)
))
);
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
auto
store
=
createTestClientStoreFrom
(
loggedInModel
,
ph
);
auto
client
=
Client
(
store
.
reader
().
map
([](
auto
c
)
{
return
SdkModel
{
c
};
}),
store
,
std
::
nullopt
);
auto
room
=
client
.
room
(
"!foo:tusooa.xyz"
);
store
.
dispatch
(
UpdateLocalEchoStatusAction
{
"!foo:tusooa.xyz"
,
"some-txn-id"
,
LocalEchoDesc
::
Failed
,
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
});
io
.
run
();
auto
localEchoes
=
room
.
localEchoes
().
make
().
get
();
REQUIRE
(
localEchoes
.
size
()
==
0
);
}
TEST_CASE
(
"UpdateLocalEchoStatusAction: update one that exists"
,
"[client][send]"
)
{
ClientModel
loggedInModel
=
makeClient
(
withRoom
(
makeRoom
(
withRoomId
(
"!foo:tusooa.xyz"
)
|
withAttr
(
&
RoomModel
::
localEchoes
,
{{
"some-txn-id"
,
Event
()}})
))
);
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
auto
store
=
createTestClientStoreFrom
(
loggedInModel
,
ph
);
auto
client
=
Client
(
store
.
reader
().
map
([](
auto
c
)
{
return
SdkModel
{
c
};
}),
store
,
std
::
nullopt
);
auto
room
=
client
.
room
(
"!foo:tusooa.xyz"
);
store
.
dispatch
(
UpdateLocalEchoStatusAction
{
"!foo:tusooa.xyz"
,
"some-txn-id"
,
LocalEchoDesc
::
Failed
,
})
.
then
([](
auto
status
)
{
REQUIRE
(
status
.
success
());
});
io
.
run
();
auto
localEchoes
=
room
.
localEchoes
().
make
().
get
();
REQUIRE
(
localEchoes
.
size
()
==
1
);
REQUIRE
(
localEchoes
[
0
].
txnId
==
"some-txn-id"
);
REQUIRE
(
localEchoes
[
0
].
status
==
LocalEchoDesc
::
Failed
);
}
TEST_CASE
(
"UpdateLocalEchoStatusAction: room does not exist"
,
"[client][send]"
)
{
ClientModel
loggedInModel
=
makeClient
({});
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
auto
store
=
createTestClientStoreFrom
(
loggedInModel
,
ph
);
auto
client
=
Client
(
store
.
reader
().
map
([](
auto
c
)
{
return
SdkModel
{
c
};
}),
store
,
std
::
nullopt
);
auto
room
=
client
.
room
(
"!foo:tusooa.xyz"
);
store
.
dispatch
(
UpdateLocalEchoStatusAction
{
"!foo:tusooa.xyz"
,
"some-txn-id"
,
LocalEchoDesc
::
Failed
,
})
.
then
([](
auto
status
)
{
REQUIRE
(
!
status
.
success
());
});
io
.
run
();
auto
localEchoes
=
room
.
localEchoes
().
make
().
get
();
REQUIRE
(
localEchoes
.
size
()
==
0
);
}
TEST_CASE
(
"Room::removeLocalEcho()"
,
"[client][send]"
)
{
auto
roomModel
=
makeRoom
();
roomModel
.
localEchoes
=
roomModel
.
localEchoes
.
push_back
({
"txn-id-1"
,
Event
()});
ClientModel
loggedInModel
=
makeClient
(
withRoom
(
roomModel
));
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
auto
store
=
createTestClientStoreFrom
(
loggedInModel
,
ph
);
auto
client
=
Client
(
store
.
reader
().
map
([](
auto
c
)
{
return
SdkModel
{
c
};
}),
store
,
std
::
nullopt
);
auto
room
=
client
.
room
(
roomModel
.
roomId
);
room
.
removeLocalEcho
(
"txn-id-1"
)
.
then
([](
auto
status
)
{
REQUIRE
(
status
.
success
());
});
io
.
run
();
auto
localEchoes
=
room
.
localEchoes
().
make
().
get
();
REQUIRE
(
localEchoes
.
size
()
==
0
);
}
TEST_CASE
(
"Room::removeLocalEcho(): removing a non-existent local echo"
,
"[client][send]"
)
{
auto
roomModel
=
makeRoom
();
roomModel
.
localEchoes
=
roomModel
.
localEchoes
.
push_back
({
"txn-id-1"
,
Event
()});
ClientModel
loggedInModel
=
makeClient
(
withRoom
(
roomModel
));
boost
::
asio
::
io_context
io
;
SingleTypePromiseInterface
<
EffectStatus
>
ph
{
AsioPromiseHandler
{
io
.
get_executor
()}};
auto
store
=
createTestClientStoreFrom
(
loggedInModel
,
ph
);
auto
client
=
Client
(
store
.
reader
().
map
([](
auto
c
)
{
return
SdkModel
{
c
};
}),
store
,
std
::
nullopt
);
auto
room
=
client
.
room
(
roomModel
.
roomId
);
room
.
removeLocalEcho
(
"txn-id-2"
)
.
then
([](
auto
status
)
{
REQUIRE
(
status
.
success
());
});
io
.
run
();
auto
localEchoes
=
room
.
localEchoes
().
make
().
get
();
REQUIRE
(
localEchoes
.
size
()
==
1
);
}
File Metadata
Details
Attached
Mime Type
text/x-c
Expires
Tue, Jun 24, 11:16 AM (1 d, 8 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
234529
Default Alt Text
send-test.cpp (11 KB)
Attached To
Mode
rL libkazv
Attached
Detach File
Event Timeline
Log In to Comment