Skip to content

Commit

Permalink
Merge pull request #65 from obeliss-nlesc/feature/server_testing
Browse files Browse the repository at this point in the history
Feature/server testing
  • Loading branch information
recap authored Jun 7, 2024
2 parents be012d8 + ab579aa commit f7c3121
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 61 deletions.
2 changes: 1 addition & 1 deletion agreement.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ class Agreement {
this.urls = urls
this.server = server
this.state = "new"
this.timeout = timeout || 30
this.timeout = timeout || 10
}

startTimeout(fn) {
Expand Down
1 change: 1 addition & 0 deletions config.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
{
"name": "public_goods_game",
"enabled": true,
"agreementTimeout": 1,
"scheduler": {
"type": "GatScheduler",
"params": {
Expand Down
5 changes: 5 additions & 0 deletions server.js
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@ function startReadyGames(experiments, agreementIds, usersDb) {
gameUsersIds,
conditionObject.users.map((u) => u.redirectedUrl),
conditionObject.server,
experiment.agreementTimeout,
)
console.log(
`New agreement: ${agreement.agreementId} ${JSON.stringify(gameUsersIds)}.`,
Expand Down Expand Up @@ -395,6 +396,7 @@ async function main() {
if (!SchedulerPlugins.classExists(e.scheduler.type)) {
throw new Error(`Class ${e.scheduler.type} not found!`)
}
experiments[e.name]["agreementTimeout"] = e.agreementTimeout
// Instantiate a scheduler class and pass the queue to
// be managed by the scheduler
experiments[e.name]["scheduler"] = new (SchedulerPlugins.getClass(
Expand Down Expand Up @@ -615,17 +617,20 @@ async function main() {
// }
if (user.state === "waitAgreement") {
// User is waiting in an agreement
// console.log(`${userId} in state ${user.state}.`)
return
}
if (user.state === "agreed") {
// User is waiting in an agreement
// console.log(`${userId} in state ${user.state}.`)
return
}
if (user.state === "inoTreePages") {
//console.log(`User ${userId} already already redirected`)
socket.emit("gameStart", { room: user.redirectedUrl.toString() })
return
}
// console.log(`${userId} in state ${user.state}.`)

// console.log(`NEw user: ${user.userId} ${user.state}`)
user.addListenerForState("queued", async (user, state) => {
Expand Down
13 changes: 13 additions & 0 deletions server_test/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
## Load test server

Run the server

```bash
npm run dev-newDb
```

Run testing script with N users

```bash
node test_user_flow.js N
```
34 changes: 34 additions & 0 deletions server_test/scan_db.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
const userdb = require("../data/userdb.json")

const maxUsers = 999

const userIds = []

for (let i = 0; i < maxUsers; i++) {
const base = 1000
userIds.push(base + i)
}

usersInDb = userdb.map((u) => {
return u.userId
})

const A = new Set(userIds)
const B = new Set(usersInDb)

const symmetricDifference = (setA, setB) => {
const difference = new Set([...setA].filter((x) => !setB.has(x)))
;[...setB].forEach((x) => {
if (!setA.has(x)) {
difference.add(x)
}
})
return difference
}

// Example usage
// const set1 = new Set([1, 2, 3, 4]);
// const set2 = new Set([3, 4, 5, 6]);

const symDiff = symmetricDifference(A, B)
console.log(symDiff) // Output: Set { 1, 2, 5, 6 }
22 changes: 0 additions & 22 deletions server_test/test_db_writes.js

This file was deleted.

26 changes: 3 additions & 23 deletions server_test/test_user_flow.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ function randomBetween(min, max) {
return Math.floor(Math.random() * (max - min) + min)
}

const maxUsers = 300
const maxUsers = parseInt(process.argv[2]) ? parseInt(process.argv[2]) : 100
const experimentId = "public_goods_game"
const url = "http://localhost:8060"
const virtUsers = {}
Expand All @@ -19,28 +19,8 @@ for (let i = 0; i < maxUsers; i++) {

Object.values(virtUsers).forEach((vu) => {
vu.connect().then(() => {
// true: random agree or not agree
// false: always agree
vu.attemptQueueFlow(true)
})
})

// setInterval(() => {
// const ids = Object.values(virtUsers).filter(vu => {
// return true
// if (vu.state == "redirected"){
// return true
// }
// }).map(vu => {
// return {
// userId: vu.userId,
// url: vu.redirectUrl
// }
// })
//
// const idsString = JSON.stringify(ids, null, 2)
// //console.log("saving...", idsString)
// fs.writeFile('redirected_users.json', idsString, (err) => {
// if (err) {
// console.log('Error ', err)
// }
// })
// }, 2000)
48 changes: 33 additions & 15 deletions server_test/virtual-user-ws.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,15 @@ class VirtualUser {
this.socket = io(this.serverUrl)
this.socket.on("connect", () => {
this.flag = 0
// console.log(`[${this.userId}] connected to ${this.serverUrl}`)
// console.log(`[${this.userId}] connected.`)
this.state = "connected"
resolve()
})
this.socket.on("connect_error", (err) => {
// console.log(`[${this.userId}] error connection.`)
this.sate = "error"
// reject(err)
})
})
}
#setupSocketEvents() {
Expand All @@ -30,10 +36,12 @@ class VirtualUser {
return
}
this.socket.on("wait", (data) => {
// console.log(`[${this.userId}] received wait.`)
this.flag = 0
this.state = "queued"
})
this.socket.on("queueUpdate", (data) => {
// console.log(`[${this.userId}] received queueUpdate.`)
this.flag = 0
// Nothing to do
})
Expand All @@ -55,52 +63,62 @@ class VirtualUser {
if (this.state == "redirected") {
return
}
// console.log(`${this.userId} emmiting landingPage`)
this.socket.emit("landingPage", {
experimentId: this.experimentId,
userId: this.userId,
})
// console.log(`${this.userId} emmiting newUser`)
this.socket.emit("newUser", {
experimentId: this.experimentId,
userId: this.userId,
})
const that = this

const intervatl = setInterval(() => {
if (that.state == "redirected" || !that.socket) {
// If user is stuck (server overload) restart flow
if (this.state == "connected" || this.state == "error") {
this.socket.close()
clearInterval(intervatl)
// console.log(`[${this.userId}] reconnecting.`)
this.socket = io(this.serverUrl)
this.attemptQueueFlow(true)
return
}
if (that.flag > 0) {
that.socket.emit("newUser", {
experimentId: that.experimentId,
userId: that.userId,
// If redirected clear interval and quit user flow
if (this.state == "redirected" || !this.socket) {
clearInterval(intervatl)
return
}
// Poke server to force requeue if for some reason user
// dropped out of queue
if (this.flag > 0) {
this.socket.emit("newUser", {
experimentId: this.experimentId,
userId: this.userId,
})
} else {
that.flag += 1
// console.log(`[${this.userId}] in stuck interval.`)
this.flag += 1
}
}, 5000)
}

#flipCoin() {
// Generate a random number between 0 and 1
const randomNumber = Math.random()
// Return "Heads" if the number is less than 0.5, otherwise return "Tails"
return randomNumber < 0.5
}

attemptQueueFlow(random) {
this.#setupSocketEvents()

this.socket.on("agree", (data) => {
// console.log(`[${this.userId}] received agree.`)
this.flag = 0
// Choose to agree or not
if (random && this.#flipCoin()) {
// console.log(`${this.userId} ignoring agreement`)
// do not agree and wait for timeout
return
}
// console.log(`${this.userId} accepting agreement`)
const uuid = data.uuid
// Choose to agree or not
this.socket.emit("userAgreed", {
experimentId: this.experimentId,
userId: this.userId,
Expand All @@ -110,9 +128,9 @@ class VirtualUser {
})

this.socket.on("reset", (data) => {
// console.log(`[${this.userId}] received reset.`)
this.flag = 0
this.state = "startedPage"
// console.log(`${this.userId} emmiting newUser`)
this.socket.emit("newUser", {
experimentId: this.experimentId,
userId: this.userId,
Expand Down

0 comments on commit f7c3121

Please sign in to comment.