Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:osu-cascades/hackbot into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
ctsstc committed Oct 21, 2020
2 parents d751c75 + 5032f2b commit d3e9e82
Show file tree
Hide file tree
Showing 13 changed files with 181 additions and 28 deletions.
2 changes: 1 addition & 1 deletion .nvmrc
Original file line number Diff line number Diff line change
@@ -1 +1 @@
v13.7
v14.13.1
1 change: 1 addition & 0 deletions .tool-versions
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
nodejs 14.13.1
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,9 @@
|Staging Build|[![Build Status](https://travis-ci.org/osu-cascades/hackbot.svg?branch=dev)](https://travis-ci.org/osu-cascades/hackbot)|
|Maintainability|[![Maintainability](https://api.codeclimate.com/v1/badges/96320fe592c30381915f/maintainability)](https://codeclimate.com/github/osu-cascades/hackbot)|
|Test Coverage|[![Test Coverage](https://api.codeclimate.com/v1/badges/96320fe592c30381915f/test_coverage)](https://codeclimate.com/github/osu-cascades/hackbot)|
|GreenKeeper|[![Greenkeeper badge](https://badges.greenkeeper.io/osu-cascades/hackbot.svg)](https://greenkeeper.io/)|
|Snyk|[![Known Vulnerabilities](https://snyk.io/test/github/osu-cascades/hackbot/badge.svg?targetFile=package.json)](https://snyk.io/test/github/osu-cascades/hackbot?targetFile=package.json)|



A Discord bot for the Cascades Tech Club [Discord](http://discordapp.com) server. To add a command, see the [Commands](#commands) section below.

Expand Down
5 changes: 4 additions & 1 deletion __tests__/__mocks__/axios.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,5 +4,8 @@ export default {
}),
request: jest.fn().mockResolvedValue({
data: {}
})
}),
post: jest.fn().mockResolvedValue({
data: {}
}),
};
21 changes: 21 additions & 0 deletions __tests__/commands/run.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Run from '@/commands/run';
import { message as mockMessage, MockedMessage } from '../mocks/discord';

let sendMock: MockedMessage;
beforeEach(() => {
sendMock = jest.fn();
mockMessage.channel.send = sendMock;
mockMessage.reply = sendMock;
});

test('Malformed message', () => {
mockMessage.content = "Wow this is nowhere near the correct content";
Run.execute([], mockMessage);
expect(sendMock).lastCalledWith("Sorry, I ran into some problems understanding your message. Here is the error stopping me.\nError: Unable to extract code from Wow this is nowhere near the correct content");
});

test('Unknown language', () => {
mockMessage.content = "```invalidLanguage\nblahblahblah```";
Run.execute([], mockMessage);
expect(sendMock).lastCalledWith("Unknown language: invalidLanguage")
});
35 changes: 35 additions & 0 deletions __tests__/languages/nim.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import Nim from '@/runners/nim';
import axiosMock from '../__mocks__/axios';

jest.mock('axios');

test('valid code', async () => {
const response = {
data: {
compileLog: "Hint: used config file '/playground/nim/config/nim.cfg' [Conf]\nHint: used config file '/playground/nim/config/config.nims' [Conf]\n....\nHint: gcc -c -w -fmax-errors=3 -I/playground/nim/lib -I/usercode -o /usercode/nimcache/stdlib_io.nim.c.o /usercode/nimcache/stdlib_io.nim.c [Exec]\nHint: gcc -c -w -fmax-errors=3 -I/playground/nim/lib -I/usercode -o /usercode/nimcache/stdlib_system.nim.c.o /usercode/nimcache/stdlib_system.nim.c [Exec]\nHint: gcc -c -w -fmax-errors=3 -I/playground/nim/lib -I/usercode -o /usercode/nimcache/@min.nim.c.o /usercode/nimcache/@min.nim.c [Exec]\nHint: [Link]\nHint: 22157 lines; 1.150s; 25.562MiB peakmem; Debug build; proj: /usercode/in.nim; out: /usercode/in [SuccessX]\n",
log: "Hello world\n"
}
};
const mockResponse = Promise.resolve(response);
axiosMock.post.mockResolvedValueOnce(mockResponse);
const result = await Nim.execute("code");
expect(result).toEqual({ success: true, output: "Hello world\n" });
});

test('invalid code', async () => {
const response = { data: { compileLog: "Hint: used config file '/playground/nim/config/nim.cfg' [Conf]\nHint: used config file '/playground/nim/config/config.nims' [Conf]\n....\n/usercode/in.nim(1, 1) Error: undeclared identifier: 'ejkfladso'\n", log: "\n" } };
const errorResult = "Error: undeclared identifier: 'ejkfladso'";
const mockResponse = Promise.resolve(response);
axiosMock.post.mockResolvedValueOnce(mockResponse);
const result = await Nim.execute("code");
expect(result).toEqual({ success: false, output: errorResult });
});

test('invalid response', async () => {
const response = { data: { error: "Server error" } };
const errorResult = "Nim LanguageRunner encountered an internal error";
const mockResponse = Promise.resolve(response);
axiosMock.post.mockResolvedValueOnce(mockResponse);
const result = await Nim.execute("code");
expect(result).toEqual({ success: false, output: errorResult });
});
21 changes: 21 additions & 0 deletions __tests__/languages/rust.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Rust from '@/runners/rust';
import axiosMock from '../__mocks__/axios';

jest.mock('axios');

test('valid code', async () => {
const code = "testCode";
let mockResponse = Promise.resolve({ data: { success: true, stdout: "test", stderr: " Compiling playground v0.0.1 (/playground)\n Finished dev [unoptimized + debuginfo] target(s) in 0.43s\n Running `target/debug/playground`\n" } });
axiosMock.post.mockResolvedValueOnce(mockResponse);
let result = await Rust.execute(code);
expect(result).toEqual({ success: true, output: "test" });
})

test('invalid code', async () => {
const code = "testCode";
const errorResult = "bad code";
let mockResponse = Promise.resolve({ data: { success: false, stdout: "", stderr: errorResult } });
axiosMock.post.mockResolvedValueOnce(mockResponse);
let result = await Rust.execute(code);
expect(result).toEqual({ success: false, output: errorResult });
})
31 changes: 31 additions & 0 deletions __tests__/library/languages.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import Languages from '@/library/languages';
import IRunner from '@/library/interfaces/iRunner';
import { ILanguageRunners } from '@/library/languageLoader';

describe('Languages', () => {
let mockLanguageRunner: IRunner;
let languageRunners: ILanguageRunners;
let languages: Languages;

beforeEach(() => {
mockLanguageRunner = {
execute: jest.fn()
};

languageRunners = {
testLang: mockLanguageRunner,
};

languages = new Languages(languageRunners);
});

test('.names returns language names', () => {
const languageNames = ['testLang'];
expect(languages.names).toEqual(languageNames);
});

test('Can fetch a command', () => {
const testLang = languages.get('testLang');
expect(testLang).toBe(mockLanguageRunner);
});
})
19 changes: 7 additions & 12 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 4 additions & 4 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "hackbot",
"version": "2.2.2",
"version": "2.4.0",
"description": "Discord bot for the Cascades Tech Club Discord server.",
"repository": {
"type": "git",
Expand All @@ -22,7 +22,7 @@
"author": "osu-cascades",
"license": "MIT",
"engines": {
"node": "13.7.x"
"node": "14.13.x"
},
"dependencies": {
"@types/axios": "^0.14.0",
Expand All @@ -37,13 +37,13 @@
"glob": "^7.1.3",
"lodash.camelcase": "^4.3.0",
"moment": "^2.22.2",
"ts-node": "^8.1.0",
"ts-node": "^9.0.0",
"tsconfig-paths": "^3.9.0",
"typescript": "^3.1.6"
},
"devDependencies": {
"jest": "^25.1.0",
"ts-jest": "^25.1.0",
"tsconfig-paths": "^3.9.0",
"tslint": "^5.11.0"
}
}
7 changes: 3 additions & 4 deletions src/commands/run.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import ICommand from '@/library/interfaces/iCommand';
import Languages from '@/library/languages';
import axios, { AxiosResponse } from 'axios';
import { Message } from 'discord.js';
import { parse } from 'querystring';

const languages = new Languages();

// Hack for implementing with static properties/methods
let Run: ICommand;
export default Run = class {


/* istanbul ignore next */
static get description(): string {
return 'Executes provided code using a LanguageRunner';
Expand All @@ -25,8 +24,8 @@ export default Run = class {
msg.channel.send(`Unknown language: ${parseResponse.language}`);
return;
}
} catch {
msg.channel.send("Sorry, I wasn't able to understand your formatting. Please try again.");
} catch (e) {
msg.channel.send("Sorry, I ran into some problems understanding your message. Here is the error stopping me.\n" + e);
return;
}

Expand Down
13 changes: 8 additions & 5 deletions src/library/languages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,8 @@ import LanguageLoader from './languageLoader';
export default class Languages {
public readonly all: ILanguageRunners;

constructor() {
const languagesPathGlob = './src/runners/*.ts';
const languageRunnerFiles = glob.sync(languagesPathGlob);
const languageRunners = LanguageLoader.getLanguageClasses(languageRunnerFiles);
this.all = languageRunners;
constructor(languages?: ILanguageRunners) {
this.all = languages || this.fetchLanguages();
}

get names() {
Expand All @@ -28,6 +25,12 @@ export default class Languages {
return longest.length;
}

private fetchLanguages(): ILanguageRunners {
const languagesPathGlob = './src/runners/*.ts';
const languageRunnerFiles = glob.sync(languagesPathGlob);
return LanguageLoader.getLanguageClasses(languageRunnerFiles);
}

}


42 changes: 42 additions & 0 deletions src/runners/nim.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import IRunner from '@/library/interfaces/iRunner';
import axios, { AxiosResponse } from 'axios';

let Nim: IRunner;
export default Nim = class {

public static execute(code: string): Promise<{ success: boolean, output: string }> {
return this.runCode(code)
.then((response) => {
// Credit to Bryce G. for this section
const success = /\[SuccessX\]/.test(response.compileLog);
const output = success ? response.log : (/Error:.+(?=\n)/.exec(response.compileLog) || [])[0];

return {
success,
output: output || "Nim LanguageRunner encountered an internal error",
};
})
.catch(() => {
return {
success: false,
output: "Nim LanguageRunner encountered an internal error",
};
});
}

// Sends code to the nim playground for execution
private static runCode(code: string): Promise<{ compileLog: string, log: string }> {
const url = "https://play.nim-lang.org/compile";
return axios.post(url, {
code,
compilationTarget: "c",
outputFormat: "raw",
version: "latest",
}).then((response: AxiosResponse) => {
return {
compileLog: response.data.compileLog,
log: response.data.log,
};
});
}
};

0 comments on commit d3e9e82

Please sign in to comment.