Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(monkeys): Interact with "external" users from the test [WPB-5129] #2219

Merged
merged 1 commit into from
Dec 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
122 changes: 104 additions & 18 deletions monkeys/schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -145,23 +145,56 @@
"description": "Should the application dump the users created into a file",
"type": "boolean"
},
"users": {
"description": "All users that are going to be used in the test. If set the parameters to create users will be ignored",
"type": "array",
"items": {
"type": "object",
"required": [
"email",
"id"
],
"properties": {
"email": {
"description": "Email of the user",
"type": "string"
},
"id": {
"description": "UUID of the user",
"type": "string"
"presetTeam": {
"description": "Team configuration and its users to use instead of creating them",
"type": "object",
"required": [
"id",
"owner",
"users"
],
"properties": {
"id": {
"description": "The team id",
"type": "string"
},
"owner": {
"description": "The creator of the group. It will be used to fetch the team's users, so it must have permission in the API to do so",
"type": "object",
"required": [
"email",
"id"
],
"properties": {
"email": {
"description": "Email of the user",
"type": "string"
},
"id": {
"description": "UUID of the user",
"type": "string"
}
}
},
"users": {
"description": "All users that are going to be used in the test. If set the parameters to create users will be ignored",
"type": "array",
"items": {
"type": "object",
"required": [
"email",
"id"
],
"properties": {
"email": {
"description": "Email of the user",
"type": "string"
},
"id": {
"description": "UUID of the user",
"type": "string"
}
}
}
}
}
Expand Down Expand Up @@ -225,7 +258,9 @@
"ADD_USERS_TO_CONVERSATION",
"LEAVE_CONVERSATION",
"DESTROY_CONVERSATION",
"SEND_REQUEST"
"SEND_REQUEST",
"HANDLE_EXTERNAL_REQUEST",
"SEND_EXTERNAL_REQUEST"
]
}
},
Expand Down Expand Up @@ -254,6 +289,12 @@
},
{
"$ref": "#/$defs/SendRequest"
},
{
"$ref": "#/$defs/SendExternalRequest"
},
{
"$ref": "#/$defs/HandleExternalRequest"
}
]
}
Expand Down Expand Up @@ -426,6 +467,51 @@
"type": "boolean"
}
}
},
"SendExternalRequest": {
"description": "Picks a random user and send a connection request to an user not on the scope of the monkeys",
"type": "object",
"required": [
"userCount",
"originTeam",
"targetTeam"
],
"properties": {
"userCount": {
"description": "How many users should send requests",
"$ref": "#/$defs/UserCount"
},
"originTeam": {
"description": "Users from which team should the requests originate",
"type": "string"
},
"targetTeam": {
"description": "Users from which team should be targeted",
"type": "string"
}
}
},
"HandleExternalRequest": {
"description": "Searches for a monkey with pending connection requests and accepts or rejects it. At this moment it considers also connection requests from other monkeys",
"type": "object",
"required": [
"userCount",
"shouldAccept"
],
"properties": {
"userCount": {
"description": "How many users should handle requests",
"$ref": "#/$defs/UserCount"
},
"shouldAccept": {
"description": "If the request should be accepted or not",
"type": "boolean"
},
"greetMessage": {
"description": "If accepted, this custom message will be sent to the other user. If not informed a random message will be sent.",
"type": "string"
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ object ActionScheduler {
suspend fun start(testCase: String, actions: List<ActionConfig>, coreLogic: CoreLogic, monkeyPool: MonkeyPool) {
actions.forEach { actionConfig ->
CoroutineScope(Dispatchers.Default).launch {
while (this.isActive) {
val actionName = actionConfig.type::class.serializer().descriptor.serialName
val actionName = actionConfig.type::class.serializer().descriptor.serialName
do {
val tags = listOf(Tag.of("testCase", testCase))
try {
logger.i("Running action $actionName: ${actionConfig.description} ${actionConfig.count} times")
Expand All @@ -58,7 +58,8 @@ object ActionScheduler {
MetricsCollector.count("c_errors", tags.plusElement(Tag.of("action", actionName)))
}
delay(actionConfig.repeatInterval.toLong())
}
} while (this.isActive && actionConfig.repeatInterval > 0u)
logger.i("Task for action $actionName finished")
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class MonkeyApplication : CliktCommand(allowMultipleSubcommands = true) {
if (logOutputFile != null) {
CoreLogger.init(KaliumLogger.Config(logLevel, listOf(fileLogger)))
} else {
CoreLogger.init(KaliumLogger.Config(logLevel, emptyList()))
CoreLogger.init(KaliumLogger.Config(logLevel))
}
MonkeyLogger.init(KaliumLogger.Config(logLevel, listOf(monkeyFileLogger)))
logger.i("Initializing Metrics Endpoint")
Expand All @@ -81,22 +81,20 @@ class MonkeyApplication : CliktCommand(allowMultipleSubcommands = true) {

private suspend fun runMonkeys(
testData: TestData
) = with(testData) {
) {
val users = TestDataImporter.generateUserData(testData)
testData.testCases.forEachIndexed { index, testCase ->
val monkeyPool = MonkeyPool(users, testCase.name)
return testData.testCases.forEachIndexed { index, testCase ->
val coreLogic = coreLogic("$HOME_DIRECTORY/.kalium/${testCase.name.replace(' ', '_')}")
// the first one creates the preset groups
logger.i("Logging in and out all users to create key packages")
val monkeyPool = MonkeyPool(users, testCase.name)
// the first one creates the preset groups and logs everyone in so keypackages are created
if (index == 0) {
logger.i("Creating initial key packages for clients (logging everyone in and out). This can take a while...")
monkeyPool.warmUp(coreLogic)
logger.i("Creating prefixed groups")
testData.conversationDistribution.forEach { (prefix, config) ->
ConversationPool.createPrefixedConversations(
coreLogic,
prefix,
config.groupCount,
config.userCount,
config.protocol,
monkeyPool
coreLogic, prefix, config.groupCount, config.userCount, config.protocol, monkeyPool
)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ abstract class Action {
is ActionType.Reconnect -> ReconnectAction(config.type)
is ActionType.SendMessage -> SendMessageAction(config.type)
is ActionType.SendRequest -> SendRequestAction(config.type)
is ActionType.HandleExternalRequest -> HandleExternalRequestAction(config.type)
is ActionType.SendExternalRequest -> SendExternalRequestAction(config.type)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.monkeys.actions

import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.monkeys.conversation.Monkey
import com.wire.kalium.monkeys.importer.ActionType
import com.wire.kalium.monkeys.pool.MonkeyPool

private val DIRECT_MESSAGES = arrayOf(
"""
Hey there,

I hope you're doing well. I've got a bit of a craving for bananas, and I was wondering if you might be able to share a few with me?
It would mean a lot. 😊

Thanks a bunch,
A friendly monkey 🍌🐵
""".trimIndent(), """
Yo,

I'm in need of some bananas, my friend. Can you hook me up? I'd appreciate it big time.

Respect,
A neutral monkey 🍌
""".trimIndent(), """
Listen up,

I ain't messin' around. I want them bananas, and I want 'em now. You better deliver or there'll be consequences.

No games,
An evil monkey 🍌👿💀
Comment on lines +27 to +47
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is too good.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All AI generated 🤣

""".trimIndent()
)

class HandleExternalRequestAction(val config: ActionType.HandleExternalRequest) : Action() {
override suspend fun execute(coreLogic: CoreLogic, monkeyPool: MonkeyPool) {
val monkeys = monkeyPool.randomMonkeysWithConnectionRequests(config.userCount)
monkeys.forEach { (monkey, pendingConnections) ->
if (config.shouldAccept) {
val otherUser =
Monkey.external(pendingConnections.random().otherUser?.id ?: error("Cannot get other user id from connection request"))
monkey.acceptRequest(otherUser)
monkey.sendDirectMessageTo(otherUser, config.greetMessage.ifBlank { DIRECT_MESSAGES.random() })
} else {
monkey.rejectRequest(Monkey.external(pendingConnections.random().connection.qualifiedToId))
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ class LeaveConversationAction(val config: ActionType.LeaveConversation) : Action
targets.forEach { conv ->
val leavers = conv.randomMonkeys(this.config.userCount)
// conversation admin should never leave the group
leavers.filter { it.user != conv.creator.user }.forEach {
leavers.filter { it.monkeyType.userData() != conv.creator.monkeyType.userData() }.forEach {
it.leaveConversation(conv.conversation.id)
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
/*
* Wire
* Copyright (C) 2023 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
package com.wire.kalium.monkeys.actions

import com.wire.kalium.logic.CoreLogic
import com.wire.kalium.monkeys.importer.ActionType
import com.wire.kalium.monkeys.pool.MonkeyPool

class SendExternalRequestAction(val config: ActionType.SendExternalRequest) : Action() {
override suspend fun execute(coreLogic: CoreLogic, monkeyPool: MonkeyPool) {
val monkeys = monkeyPool.randomLoggedInMonkeysFromTeam(config.originTeam, config.userCount)
val usersFromTeam = monkeyPool.externalUsersFromTeam(config.targetTeam)
monkeys.forEach { monkey ->
monkey.sendRequest(usersFromTeam.random())
}
}
}
Loading
Loading