-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathtemplates.test.ts
189 lines (156 loc) · 5.55 KB
/
templates.test.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
import { describe, expect, test } from 'vitest';
import { createHelpers } from 'yeoman-test';
import { spawn } from 'child_process';
import * as dotenv from 'dotenv';
import path from 'path';
import url from 'url';
const __dirname = url.fileURLToPath(new URL('../', import.meta.url));
const DEFAULT_TIMEOUT = 60_000; // 1 minute
dotenv.config();
const { BOT_TOKEN } = process.env;
if (!BOT_TOKEN) {
throw new Error('You must specify a valid bot token in the BOT_TOKEN environment variable');
}
const defaultAnswers = {
botName: 'test-run-bot',
botType: 'typescript',
botToken: BOT_TOKEN,
openWithCode: false,
};
describe('Check if the templates work', () => {
const targetRoot = path.join(__dirname, 'test-temp');
const moduleRoot = path.join(__dirname, 'app');
const resultRoot = path.join(targetRoot, defaultAnswers.botName);
test('Should generate and run TypeScript Discord Bot', async () => {
const context = createHelpers({}).run(moduleRoot);
context.targetDirectory = targetRoot;
context.cleanTestDirectory(true);
await context
.onGenerator(generator => {
generator.destinationRoot(targetRoot);
})
.withAnswers(defaultAnswers)
.then(async () => {
const result = await runBot('node', ['./dist/src/index.js'], resultRoot, DEFAULT_TIMEOUT);
expect(result).toContain(BOT_OUTPUT_START);
});
context.cleanup();
}, 120_000);
test('Should generate and run JavaScript Discord Bot', async () => {
const context = createHelpers({}).run(moduleRoot);
context.targetDirectory = targetRoot;
context.cleanTestDirectory(true);
await context
.onGenerator(generator => {
generator.destinationRoot(targetRoot);
})
.withAnswers({ ...defaultAnswers, botType: 'javascript' })
.then(async () => {
const result = await runBot('node', ['./src/index.js'], resultRoot, DEFAULT_TIMEOUT);
expect(result).toContain(BOT_OUTPUT_START);
});
context.cleanup();
}, 120_000);
test.skip('Should generate and run Python Discord Bot', async () => {
const context = createHelpers({}).run(moduleRoot);
context.targetDirectory = targetRoot;
context.cleanTestDirectory(true);
await context
.onGenerator(generator => {
generator.destinationRoot(targetRoot);
})
.withAnswers({ ...defaultAnswers, botType: 'python' })
.then(async () => {
// install dependencies
console.info('Installing Python dependencies...');
const result = await new Promise<string>((resolve) => {
spawn('pip3', ['install', '-r', 'requirements.txt'], { cwd: resultRoot, timeout: DEFAULT_TIMEOUT })
.on('close', async () => {
console.info('Done installing Python dependencies.');
resolve(await runBot('python', ['main.py'], resultRoot, DEFAULT_TIMEOUT));
})
.on('error', (err) => {
console.error('There was an error while installing the Python dependecies', err);
resolve('PYTHON DEPENDENCY ERROR');
})
.stderr.on('data', (data) => {
console.error('There was an error while installing the Python dependecies', data);
resolve('PYTHON DEPENDENCY ERROR');
});
});
expect(result).toContain(BOT_OUTPUT_START);
});
context.cleanup();
}, 120_000);
test('Should generate and run Rust Discord Bot', async () => {
const context = createHelpers({}).run(moduleRoot);
context.targetDirectory = targetRoot;
context.cleanTestDirectory(true);
await context
.onGenerator(generator => {
generator.destinationRoot(targetRoot);
})
.withAnswers({ ...defaultAnswers, botType: 'rust' })
.withArguments(['skip-build'])
.then(async () => {
const result = await runBot('cargo', ['run','--release','--quiet'], resultRoot, 120_000); // 2 minutes
expect(result).toContain(BOT_OUTPUT_START);
});
context.cleanup();
}, 180_000); // 3 minutes
});
/** Run a command in a child process and return the output
* @param {string} command The command to run
* @param {string[]} args The arguments to pass to the command
* @param {string} root The root directory to run the command in
* @param {number} timeoutTime The time in milliseconds to wait before timing out
* @returns {Promise<string>} The output of the command
*/
async function runBot(command: string, args: string[], root: string, timeoutTime: number): Promise<string> {
const childProcess = spawn(command, args, { cwd: root, timeout: timeoutTime });
const result = await new Promise<string>((resolve) => {
const output: string[] = [];
let currentOutputTimeout: NodeJS.Timeout;
// timeout
const safetyTimeout = setTimeout(() => {
resolve('BOT RUN TIMEOUT');
}, timeoutTime);
const restartOutputTimeout = () => {
currentOutputTimeout = setTimeout(() => {
childProcess.kill('SIGINT');
resolve(output.join('\n'));
}, 7_000);
};
const clearTimeouts = () => {
clearTimeout(safetyTimeout);
clearTimeout(currentOutputTimeout);
};
const onData = (data: string) => {
clearTimeouts();
// if the bot has successfully started, resolve the promise
if (data.toString().includes(BOT_OUTPUT_START)) {
resolve(data.toString());
}
// restart output timeout
restartOutputTimeout();
output.push(data.toString());
};
// output
childProcess.stdout.on('data', (data) => {
onData(data.toString());
});
// output error
childProcess.stderr.on('data', (data) => {
onData(data.toString());
});
// process close
childProcess.on('close', () => {
clearTimeouts();
resolve(output.join('\n'));
});
});
console.info('Killing the bot process with result:', result);
childProcess.kill('SIGINT');
return result;
}
const BOT_OUTPUT_START = 'Bot is logged in as';