Created
May 6, 2021 03:59
-
-
Save nathanpeck/54ad0aee1638b5b57569ac6e38d81949 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var request = require('request'); | |
var async = require('async'); | |
var faker = require('faker'); | |
var _ = require('lodash'); | |
if (!process.env.PEAK_PERIOD_MINS) { | |
process.env.PEAK_PERIOD_MINS = '30'; | |
} | |
var PEAK_PERIOD_MINS = parseInt(process.env.PEAK_PERIOD_MINS, 10) * 60 * 1000; | |
var MAX_CONCURRENT = parseInt(process.env.MAX_CONCURRENT, 10) || 100; | |
var MIN_CONCURRENT = parseInt(process.env.MIN_CONCURRENT, 10) || 0; | |
var EXTERNAL_URL = process.env.EXTERNAL_URL; | |
console.log(`Running forever, peaking every ${process.env.PEAK_PERIOD_MINS} minutes starting from ` + | |
`${MIN_CONCURRENT} concurrent agents, and peaking at ${MAX_CONCURRENT} ` + | |
'concurrent agents'); | |
var client = request.defaults({ | |
baseUrl: EXTERNAL_URL, | |
json: true | |
}); | |
function scaleBetween(unscaledNum, minAllowed, maxAllowed, min, max) { | |
return (maxAllowed - minAllowed) * (unscaledNum - min) / (max - min) + minAllowed; | |
} | |
function randomId() { | |
return (new Date().getTime() / 1000 | 0).toString(16) + 'xxxxxxxxxxxxxxxx'.replace(/[x]/g, function() { | |
return (Math.random() * 16 | 0).toString(16); | |
}).toLowerCase(); | |
} | |
// The core behavior of the agent once it has finished setting up. | |
function agentLoop(auth) { | |
return function(number, done) { | |
async.auto({ | |
hashtag: function(next) { | |
// Check the #ijustsignedup hashtag to find other people follow | |
client({ | |
method: 'GET', | |
uri: '/api/timeline/hashtag/ijustsignedup', | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('>>>>> Got unexpected status code from timeline service: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
return next(null, body); | |
}); | |
}, | |
followSomeone: ['hashtag', function(context, next) { | |
// 1 in N chance per loop to follow someone new. | |
if (_.random(6) !== 6) { | |
return next(); | |
} | |
var toFollow = _.sample(Object.keys(context.hashtag.users)); | |
client({ | |
method: 'POST', | |
uri: `/api/subscription/users/${toFollow}/subscribers/me`, | |
auth: { | |
bearer: auth.token | |
} | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('>>>>> Got unexpected status code from subscription service: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
return next(); | |
}); | |
}], | |
sendMessage: function(next) { | |
client({ | |
method: 'POST', | |
uri: '/api/message', | |
auth: { | |
bearer: auth.token | |
}, | |
body: { | |
payload: { | |
text: 'load test message ' + number | |
} | |
} | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('Sending message got status code: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
setTimeout(next, 1000); | |
}); | |
}, | |
checkTimeline: ['sendMessage', function(context, next) { | |
// Check messages | |
client({ | |
method: 'GET', | |
uri: `/api/timeline/users/${auth.id}/to`, | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('>>>>> Got unexpected status code from timeline service: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
return next(null, body); | |
}); | |
}], | |
checkOwnMessages: ['sendMessage', function(context, next) { | |
// Check messages | |
client({ | |
method: 'GET', | |
uri: `/api/timeline/users/${auth.id}/by`, | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('>>>>> Got unexpected status code from timeline service: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
return next(null, body); | |
}); | |
}] | |
}, function() { | |
setTimeout(done, 4000); | |
}); | |
}; | |
} | |
function agent(done) { | |
var firstName = faker.name.firstName(); | |
var lastName = faker.name.lastName(); | |
var password = randomId(); | |
var auth; | |
var name = firstName + ' ' + lastName; | |
var username = firstName + '_' + lastName; | |
username = username.toLowerCase() + '_' + Date.now(); | |
var avatar = faker.image.avatar(); | |
async.auto({ | |
website: function(next) { | |
client.get('/', function() { | |
setTimeout(next, 1000); | |
}); | |
}, | |
createAccount: ['website', function(context, next) { | |
client({ | |
method: 'POST', | |
uri: '/api/user', | |
body: { | |
username: username, | |
name: name, | |
password: password, | |
avatar: avatar | |
} | |
}, function(err, resp, body) { | |
console.log(`welcome ${name}, aka @${username} - (${concurrent} concurrent agents)`); | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('Hitting website got status code: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
setTimeout(next, 2000); | |
}); | |
}], | |
authenticate: ['createAccount', function(context, next) { | |
client({ | |
method: 'POST', | |
uri: '/api/auth/challenge', | |
body: { | |
username: username, | |
password: password | |
} | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('Creating account got status code: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
auth = body; | |
setTimeout(next, 2000); | |
}); | |
}], | |
selfDetails: ['authenticate', function(context, next) { | |
client({ | |
method: 'GET', | |
uri: '/api/user/users/me', | |
auth: { | |
bearer: auth.token | |
} | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('Authenticating got status code: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
next(); | |
}); | |
}], | |
// Send a message to the #ijustsignedup hashtag so that other | |
// bots can find this bot to follow it. | |
broadcastSignup: ['selfDetails', function(context, next) { | |
client({ | |
method: 'POST', | |
uri: '/api/message', | |
auth: { | |
bearer: auth.token | |
}, | |
body: { | |
payload: { | |
text: 'hello world! I am ' + name + ' and #ijustsignedup' | |
} | |
} | |
}, function(err, resp, body) { | |
if (err) { | |
console.error('Networking error', err); | |
return next('fail'); | |
} | |
if (resp.statusCode !== 200) { | |
console.error('Sending message got status code: ' + resp.statusCode, body); | |
return next('fail'); | |
} | |
next(); | |
}); | |
}], | |
mainLoop: ['broadcastSignup', function(context, next) { | |
async.timesSeries(30, agentLoop(auth), next); | |
}], | |
}, done); | |
} | |
var concurrent = 0; | |
function maybeLaunchAgent() { | |
var pointInTime = Date.now() % PEAK_PERIOD_MINS * 2; | |
if (pointInTime > PEAK_PERIOD_MINS) { | |
// We are in the downslope period | |
pointInTime = (PEAK_PERIOD_MINS * 2) - pointInTime; | |
} | |
var targetConcurrent = scaleBetween(pointInTime, MAX_CONCURRENT, MIN_CONCURRENT, PEAK_PERIOD_MINS, 0); | |
if (concurrent < targetConcurrent) { | |
concurrent++; | |
// Launch another agent. | |
agent(function() { | |
concurrent--; | |
}); | |
} | |
setTimeout(maybeLaunchAgent, 1000); // Launch agents at a rate of once per second | |
} | |
maybeLaunchAgent(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment