diff --git a/__tests__/languages/nim.test.ts b/__tests__/languages/nim.test.ts new file mode 100644 index 0000000..acaa7ba --- /dev/null +++ b/__tests__/languages/nim.test.ts @@ -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 }); +}); diff --git a/package-lock.json b/package-lock.json index 21ee312..f301000 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "hackbot", - "version": "2.3.1", + "version": "2.4.0", "lockfileVersion": 1, "requires": true, "dependencies": { @@ -4590,9 +4590,9 @@ } }, "ts-node": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-8.9.0.tgz", - "integrity": "sha512-rwkXfOs9zmoHrV8xE++dmNd6ZIS+nmHHCxcV53ekGJrxFLMbp+pizpPS07ARvhwneCIECPppOwbZHvw9sQtU4w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.0.0.tgz", + "integrity": "sha512-/TqB4SnererCDR/vb4S/QvSZvzQMJN8daAslg7MeaiHvD8rDZsSfXmNeNumyZZzMned72Xoq/isQljYSt8Ynfg==", "requires": { "arg": "^4.1.0", "diff": "^4.0.1", diff --git a/package.json b/package.json index 00d0ccf..b54c3c2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "hackbot", - "version": "2.3.1", + "version": "2.4.0", "description": "Discord bot for the Cascades Tech Club Discord server.", "repository": { "type": "git", @@ -37,7 +37,7 @@ "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" }, diff --git a/src/runners/nim.ts b/src/runners/nim.ts new file mode 100644 index 0000000..4869dfb --- /dev/null +++ b/src/runners/nim.ts @@ -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, + }; + }); + } +};