@@ -1,5 +1,5 @@ | |||||
{ | { | ||||
"name": "@modal-sh/cli", | |||||
"name": "@modal-sh/minecraft-uuid-cli", | |||||
"version": "0.0.0", | "version": "0.0.0", | ||||
"files": [ | "files": [ | ||||
"dist", | "dist", | ||||
@@ -66,7 +66,7 @@ | |||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"@clack/prompts": "^0.6.3", | "@clack/prompts": "^0.6.3", | ||||
"@modal-sh/core": "workspace:*", | |||||
"@modal-sh/minecraft-uuid-core": "workspace:*", | |||||
"pino": "^8.14.1", | "pino": "^8.14.1", | ||||
"yargs": "^17.7.2" | "yargs": "^17.7.2" | ||||
} | } | ||||
@@ -1,22 +1,15 @@ | |||||
import { Cli } from './packages/cli-wrapper'; | import { Cli } from './packages/cli-wrapper'; | ||||
import { AdderController, AdderControllerImpl } from './modules/adder'; | |||||
import { UuidController, AdderControllerImpl } from './modules/uuid'; | |||||
export const addCommands = (cli: Cli) => { | export const addCommands = (cli: Cli) => { | ||||
const adderController: AdderController = new AdderControllerImpl(); | |||||
const uuidController: UuidController = new AdderControllerImpl(); | |||||
return cli | return cli | ||||
.command({ | .command({ | ||||
aliases: ['a'], | |||||
command: 'add', | |||||
parameters: ['<a> <b>'], | |||||
describe: 'Add two numbers', | |||||
handler: adderController.addNumbers, | |||||
}) | |||||
.command({ | |||||
aliases: ['s'], | |||||
command: 'subtract', | |||||
parameters: ['<a> <b>'], | |||||
describe: 'Subtract two numbers', | |||||
handler: adderController.subtractNumbers, | |||||
aliases: ['u'], | |||||
command: 'uuid', | |||||
parameters: ['<username>'], | |||||
describe: 'Get equivalent UUID of username in Minecraft.', | |||||
handler: uuidController.getUsernameUuid, | |||||
}); | }); | ||||
}; | }; |
@@ -1,82 +0,0 @@ | |||||
import { | |||||
AdderService, | |||||
AdderServiceImpl, | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
} from './adder.service'; | |||||
import { CommandHandler } from '../../packages/cli-wrapper'; | |||||
export interface AdderController { | |||||
addNumbers: CommandHandler; | |||||
subtractNumbers: CommandHandler; | |||||
} | |||||
export class AdderControllerImpl implements AdderController { | |||||
constructor( | |||||
private readonly adderService: AdderService = new AdderServiceImpl(), | |||||
) { | |||||
// noop | |||||
} | |||||
readonly addNumbers: CommandHandler = (params) => { | |||||
if (!params.interactive) { | |||||
const checkArgs = params.args as Record<string, unknown>; | |||||
if (typeof checkArgs.a === 'undefined') { | |||||
params.logger.error('Missing required argument: a'); | |||||
return -1; | |||||
} | |||||
if (typeof checkArgs.b === 'undefined') { | |||||
params.logger.error('Missing required argument: b'); | |||||
return -1; | |||||
} | |||||
} | |||||
const { a, b } = params.args; | |||||
try { | |||||
const response = this.adderService.addNumbers({ a: Number(a), b: Number(b) }); | |||||
params.logger.info(response); | |||||
} catch (errorRaw) { | |||||
const error = errorRaw as Error; | |||||
params.logger.error(error.message); | |||||
if (error instanceof InvalidArgumentTypeError) { | |||||
return -1; | |||||
} | |||||
if (error instanceof ArgumentOutOfRangeError) { | |||||
return -2; | |||||
} | |||||
return -3; | |||||
} | |||||
return 0; | |||||
} | |||||
readonly subtractNumbers: CommandHandler = (params) => { | |||||
if (!params.interactive) { | |||||
const checkArgs = params.args as Record<string, unknown>; | |||||
if (typeof checkArgs.a === 'undefined') { | |||||
params.logger.error('Missing required argument: a'); | |||||
return -1; | |||||
} | |||||
if (typeof checkArgs.b === 'undefined') { | |||||
params.logger.error('Missing required argument: b'); | |||||
return -1; | |||||
} | |||||
} | |||||
const { a, b } = params.args; | |||||
try { | |||||
const response = this.adderService.addNumbers({ a: Number(a), b: -(Number(b)) }); | |||||
params.logger.info(response); | |||||
} catch (errorRaw) { | |||||
const error = errorRaw as Error; | |||||
params.logger.error(error.message); | |||||
if (error instanceof InvalidArgumentTypeError) { | |||||
return -1; | |||||
} | |||||
if (error instanceof ArgumentOutOfRangeError) { | |||||
return -2; | |||||
} | |||||
return -3; | |||||
} | |||||
return 0; | |||||
} | |||||
} |
@@ -1,21 +0,0 @@ | |||||
import { | |||||
add, | |||||
AddFunctionOptions, | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
} from '@modal-sh/core'; | |||||
export interface AdderService { | |||||
addNumbers(options: AddFunctionOptions): number; | |||||
} | |||||
export class AdderServiceImpl implements AdderService { | |||||
addNumbers(options: AddFunctionOptions) { | |||||
return add(options); | |||||
} | |||||
} | |||||
export { | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
}; |
@@ -1,2 +0,0 @@ | |||||
export * from './adder.controller'; | |||||
export * from './adder.service'; |
@@ -0,0 +1,2 @@ | |||||
export * from './uuid.controller'; | |||||
export * from './uuid.service'; |
@@ -0,0 +1,42 @@ | |||||
import { | |||||
UuidService, | |||||
UuidServiceImpl, | |||||
InvalidArgumentTypeError, | |||||
} from './uuid.service'; | |||||
import { CommandHandler } from '@/packages/cli-wrapper'; | |||||
export interface UuidController { | |||||
getUsernameUuid: CommandHandler; | |||||
} | |||||
export class AdderControllerImpl implements UuidController { | |||||
constructor( | |||||
private readonly uuidService: UuidService = new UuidServiceImpl(), | |||||
) { | |||||
// noop | |||||
} | |||||
readonly getUsernameUuid: CommandHandler = async (params) => { | |||||
if (!params.interactive) { | |||||
const checkArgs = params.args as Record<string, unknown>; | |||||
if (typeof checkArgs.username === 'undefined') { | |||||
params.logger.error('Missing required argument: username'); | |||||
return -1; | |||||
} | |||||
} | |||||
const { username, onlineMode } = params.args; | |||||
try { | |||||
const response = await this.uuidService.getUsernameUuid({ username, onlineMode }); | |||||
params.logger.info(response); | |||||
} catch (errorRaw) { | |||||
const error = errorRaw as Error; | |||||
params.logger.error(error.message); | |||||
if (error instanceof InvalidArgumentTypeError) { | |||||
return -1; | |||||
} | |||||
return -3; | |||||
} | |||||
return 0; | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
import { | |||||
getUsernameUuid, | |||||
GetUsernameUuidOptions, | |||||
InvalidArgumentTypeError, | |||||
} from '@modal-sh/minecraft-uuid-core'; | |||||
export interface UuidService { | |||||
getUsernameUuid(options: GetUsernameUuidOptions): Promise<string>; | |||||
} | |||||
export class UuidServiceImpl implements UuidService { | |||||
getUsernameUuid(options: GetUsernameUuidOptions) { | |||||
return getUsernameUuid(options); | |||||
} | |||||
} | |||||
export { | |||||
InvalidArgumentTypeError, | |||||
}; |
@@ -1,7 +1,7 @@ | |||||
import { describe, it, expect, vi, beforeAll } from 'vitest'; | import { describe, it, expect, vi, beforeAll } from 'vitest'; | ||||
import { createCli } from '../src/cli'; | import { createCli } from '../src/cli'; | ||||
import { addCommands } from '../src/commands'; | import { addCommands } from '../src/commands'; | ||||
import { AdderServiceImpl, InvalidArgumentTypeError, ArgumentOutOfRangeError } from '../src/modules/adder'; | |||||
import { UuidServiceImpl, InvalidArgumentTypeError } from '../src/modules/uuid'; | |||||
import { Cli } from '../src/packages/cli-wrapper'; | import { Cli } from '../src/packages/cli-wrapper'; | ||||
vi.mock('process'); | vi.mock('process'); | ||||
@@ -17,39 +17,30 @@ describe('blah', () => { | |||||
}); | }); | ||||
it('returns result when successful', async () => { | it('returns result when successful', async () => { | ||||
const response = await cli.test().run(['add', '1', '2']); | |||||
const response = await cli.test().run(['uuid', 'username']); | |||||
expect(response.exitCode).toBe(0); | expect(response.exitCode).toBe(0); | ||||
}); | }); | ||||
it('returns error when given invalid inputs', async () => { | it('returns error when given invalid inputs', async () => { | ||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
vi.spyOn(UuidServiceImpl.prototype, 'getUsernameUuid').mockImplementationOnce(() => { | |||||
throw new InvalidArgumentTypeError('Invalid input'); | throw new InvalidArgumentTypeError('Invalid input'); | ||||
}); | }); | ||||
const response = await cli.test().run(['add', '1', '2']); | |||||
const response = await cli.test().run(['uuid', 'username']); | |||||
expect(response.exitCode).toBe(-1); | expect(response.exitCode).toBe(-1); | ||||
}); | }); | ||||
it('returns error when given out-of-range inputs', async () => { | |||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
throw new ArgumentOutOfRangeError('Out of range'); | |||||
}); | |||||
const response = await cli.test().run(['add', '1', '2']); | |||||
expect(response.exitCode).toBe(-2); | |||||
}); | |||||
it('returns error when an unexpected error occurs', async () => { | it('returns error when an unexpected error occurs', async () => { | ||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
vi.spyOn(UuidServiceImpl.prototype, 'getUsernameUuid').mockImplementationOnce(() => { | |||||
throw new Error('Unexpected error'); | throw new Error('Unexpected error'); | ||||
}); | }); | ||||
const response = await cli.test().run(['add', '1', '2']); | |||||
const response = await cli.test().run(['uuid', 'username']); | |||||
expect(response.exitCode).toBe(-3); | expect(response.exitCode).toBe(-3); | ||||
}); | }); | ||||
it('returns error when given insufficient arguments', async () => { | it('returns error when given insufficient arguments', async () => { | ||||
const response = await cli.test().run(['add', '1']); | |||||
const response = await cli.test().run(['uuid']); | |||||
expect(response.exitCode).toBe(-1); | expect(response.exitCode).toBe(-1); | ||||
}); | }); | ||||
@@ -58,10 +49,9 @@ describe('blah', () => { | |||||
const response = await cli | const response = await cli | ||||
.test() | .test() | ||||
.promptValue({ | .promptValue({ | ||||
a: '1', | |||||
b: '2', | |||||
username: 'username', | |||||
}) | }) | ||||
.run(['add', '-i']); | |||||
.run(['uuid', '-i']); | |||||
expect(response.exitCode).not.toBe(-1); | expect(response.exitCode).not.toBe(-1); | ||||
}); | }); | ||||
}); | }); | ||||
@@ -1,5 +1,5 @@ | |||||
{ | { | ||||
"name": "@modal-sh/core", | |||||
"name": "@modal-sh/minecraft-uuid-core", | |||||
"version": "0.0.0", | "version": "0.0.0", | ||||
"files": [ | "files": [ | ||||
"dist", | "dist", | ||||
@@ -19,7 +19,7 @@ | |||||
"pridepack": "2.4.4", | "pridepack": "2.4.4", | ||||
"tslib": "^2.5.0", | "tslib": "^2.5.0", | ||||
"typescript": "^4.9.5", | "typescript": "^4.9.5", | ||||
"vitest": "^0.28.1" | |||||
"vitest": "1.3.1" | |||||
}, | }, | ||||
"scripts": { | "scripts": { | ||||
"prepublishOnly": "pridepack clean && pridepack build", | "prepublishOnly": "pridepack clean && pridepack build", | ||||
@@ -1,10 +1,13 @@ | |||||
export interface AddFunctionOptions { | |||||
a: number; | |||||
b: number; | |||||
import { get } from 'https'; | |||||
import { createHash } from 'crypto'; | |||||
export interface GetUsernameUuidOptions { | |||||
username: string; | |||||
onlineMode?: boolean; | |||||
} | } | ||||
export interface AddFunction { | |||||
(options: AddFunctionOptions): number; | |||||
export interface GetUsernameUuid { | |||||
(options: GetUsernameUuidOptions): Promise<string>; | |||||
} | } | ||||
export class InvalidArgumentTypeError extends TypeError { | export class InvalidArgumentTypeError extends TypeError { | ||||
@@ -14,21 +17,55 @@ export class InvalidArgumentTypeError extends TypeError { | |||||
} | } | ||||
} | } | ||||
export class ArgumentOutOfRangeError extends RangeError { | |||||
constructor(message: string) { | |||||
super(message); | |||||
this.name = 'ArgumentOutOfRangeError'; | |||||
} | |||||
} | |||||
const formatUuidBytes = (bytes: Buffer) => { | |||||
return [ | |||||
bytes.slice(0, 4), | |||||
bytes.slice(4, 6), | |||||
bytes.slice(6, 8), | |||||
bytes.slice(8, 10), | |||||
bytes.slice(10) | |||||
] | |||||
.map((b) => b.toString('hex')) | |||||
.join('-'); | |||||
}; | |||||
export const getUsernameUuid: GetUsernameUuid = async (options: GetUsernameUuidOptions): Promise<string> => { | |||||
const { username, onlineMode = false } = options as unknown as Record<string, unknown>; | |||||
export const add: AddFunction = (options: AddFunctionOptions): number => { | |||||
const { a, b } = options as unknown as Record<string, unknown>; | |||||
if (typeof a !== 'number' || typeof b !== 'number') { | |||||
throw new InvalidArgumentTypeError('a and b must be numbers'); | |||||
if (typeof username !== 'string') { | |||||
throw new InvalidArgumentTypeError('Username must be a string'); | |||||
} | } | ||||
if (!Number.isFinite(a) || !Number.isFinite(b)) { | |||||
throw new ArgumentOutOfRangeError('a and b must be finite numbers'); | |||||
if (!onlineMode) { | |||||
const md5 = createHash('md5'); | |||||
md5.update(Buffer.from(`OfflinePlayer:${username}`, 'utf-8')); | |||||
const bytes = md5.digest(); | |||||
bytes[6] &= 0x0f; | |||||
bytes[6] |= 0x30; | |||||
bytes[8] &= 0x3f; | |||||
bytes[8] |= 0x80; | |||||
return formatUuidBytes(bytes); | |||||
} | } | ||||
return a + b; | |||||
return new Promise((resolve, reject) => { | |||||
get(`https://api.mojang.com/users/profiles/minecraft/${encodeURIComponent(username)}`, res => { | |||||
let uuid = ''; | |||||
res.on('data', (c) => { | |||||
const json = c.toString('utf-8'); | |||||
const data = JSON.parse(json); | |||||
const bytes = Buffer.from(data.id, 'hex'); | |||||
uuid = formatUuidBytes(bytes); | |||||
}); | |||||
res.on('end', () => { | |||||
resolve(uuid); | |||||
}); | |||||
}) | |||||
.on('error', reject); | |||||
}); | |||||
}; | }; |
@@ -1,16 +1,61 @@ | |||||
import { describe, it, expect } from 'vitest'; | |||||
import { add } from '../src'; | |||||
import {describe, it, expect, vi} from 'vitest'; | |||||
import { getUsernameUuid } from '../src'; | |||||
import { EventEmitter } from 'events'; | |||||
describe('blah', () => { | |||||
it('returns result', () => { | |||||
expect(add({ a: 1, b: 1 })).toEqual(2); | |||||
describe('minecraft-uuid', () => { | |||||
it('returns result (default offline mode)', async () => { | |||||
vi.mock('crypto', () => ({ | |||||
createHash: (algo) => { | |||||
return { | |||||
update: () => { | |||||
// noop | |||||
}, | |||||
digest: () => Buffer.alloc(16, 0), | |||||
}; | |||||
}, | |||||
})); | |||||
const uuid = await getUsernameUuid({ username: 'username' }); | |||||
expect(uuid).toEqual('00000000-0000-3000-8000-000000000000'); | |||||
}); | }); | ||||
it('throws when given invalid argument types', () => { | |||||
expect(() => add({ a: '1' as unknown as number, b: 1 })).toThrow(TypeError); | |||||
it('returns result (explicit offline mode)', async () => { | |||||
vi.mock('crypto', () => ({ | |||||
createHash: (algo) => { | |||||
return { | |||||
update: () => { | |||||
// noop | |||||
}, | |||||
digest: () => Buffer.alloc(16, 0), | |||||
}; | |||||
}, | |||||
})); | |||||
const uuid = await getUsernameUuid({ username: 'username', onlineMode: false }); | |||||
expect(uuid).toEqual('00000000-0000-3000-8000-000000000000'); | |||||
}); | }); | ||||
it('throws when given out-of-range arguments', () => { | |||||
expect(() => add({ a: Infinity, b: 1 })).toThrow(RangeError); | |||||
it('returns result (explicit online mode)', async () => { | |||||
vi.mock('https', () => ({ | |||||
get: (url, cb) => { | |||||
const emitter = new EventEmitter(); | |||||
if (typeof cb === 'function') { | |||||
(cb as unknown as (...args: unknown[]) => unknown)(emitter); | |||||
} | |||||
emitter.emit('data', Buffer.from(JSON.stringify({ | |||||
id: '82c683b548333d6b8a2606352cd6f327', | |||||
name: 'username' | |||||
}))); | |||||
emitter.emit('end'); | |||||
return emitter; | |||||
}, | |||||
})); | |||||
const uuid = await getUsernameUuid({ username: 'username', onlineMode: true }); | |||||
expect(uuid).toEqual('82c683b5-4833-3d6b-8a26-06352cd6f327'); | |||||
}); | }); | ||||
}); | }); |
@@ -1,5 +1,5 @@ | |||||
{ | { | ||||
"name": "@modal-sh/web-api", | |||||
"name": "@modal-sh/minecraft-uuid-web-api", | |||||
"version": "0.0.0", | "version": "0.0.0", | ||||
"files": [ | "files": [ | ||||
"dist", | "dist", | ||||
@@ -23,7 +23,7 @@ | |||||
"vitest": "^0.28.1" | "vitest": "^0.28.1" | ||||
}, | }, | ||||
"dependencies": { | "dependencies": { | ||||
"@modal-sh/core": "workspace:*", | |||||
"@modal-sh/minecraft-uuid-core": "workspace:*", | |||||
"fastify": "^4.12.0" | "fastify": "^4.12.0" | ||||
}, | }, | ||||
"scripts": { | "scripts": { | ||||
@@ -1,60 +0,0 @@ | |||||
import { RouteHandlerMethod } from 'fastify'; | |||||
import { | |||||
AdderService, | |||||
AdderServiceImpl, | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
} from '@/modules/adder/adder.service'; | |||||
import { constants } from 'http2'; | |||||
export interface AdderController { | |||||
addNumbers: RouteHandlerMethod; | |||||
subtractNumbers: RouteHandlerMethod; | |||||
} | |||||
export class AdderControllerImpl implements AdderController { | |||||
constructor( | |||||
private readonly adderService: AdderService = new AdderServiceImpl(), | |||||
) { | |||||
// noop | |||||
} | |||||
readonly addNumbers: RouteHandlerMethod = async (request, reply) => { | |||||
const { a, b } = request.body as { a: number; b: number }; | |||||
try { | |||||
const response = this.adderService.addNumbers({ a, b }); | |||||
reply.send(response); | |||||
} catch (errorRaw) { | |||||
if (errorRaw instanceof InvalidArgumentTypeError) { | |||||
request.log.info(errorRaw); | |||||
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message); | |||||
return; | |||||
} | |||||
if (errorRaw instanceof ArgumentOutOfRangeError) { | |||||
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message); | |||||
return; | |||||
} | |||||
const error = errorRaw as Error; | |||||
reply.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR).send(error.message); | |||||
} | |||||
} | |||||
readonly subtractNumbers: RouteHandlerMethod = async (request, reply) => { | |||||
const { a, b } = request.body as { a: number; b: number }; | |||||
try { | |||||
const response = this.adderService.addNumbers({ a, b: -b }); | |||||
reply.send(response); | |||||
} catch (errorRaw) { | |||||
if (errorRaw instanceof InvalidArgumentTypeError) { | |||||
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message); | |||||
return; | |||||
} | |||||
if (errorRaw instanceof ArgumentOutOfRangeError) { | |||||
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message); | |||||
return; | |||||
} | |||||
const error = errorRaw as Error; | |||||
reply.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR).send(error.message); | |||||
} | |||||
} | |||||
} |
@@ -1,21 +0,0 @@ | |||||
import { | |||||
add, | |||||
AddFunctionOptions, | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
} from '@modal-sh/core'; | |||||
export interface AdderService { | |||||
addNumbers(options: AddFunctionOptions): number; | |||||
} | |||||
export class AdderServiceImpl implements AdderService { | |||||
addNumbers(options: AddFunctionOptions) { | |||||
return add(options); | |||||
} | |||||
} | |||||
export { | |||||
ArgumentOutOfRangeError, | |||||
InvalidArgumentTypeError, | |||||
}; |
@@ -1,2 +0,0 @@ | |||||
export * from './adder.controller'; | |||||
export * from './adder.service'; |
@@ -0,0 +1,2 @@ | |||||
export * from './uuid.controller'; | |||||
export * from './uuid.service'; |
@@ -0,0 +1,35 @@ | |||||
import { RouteHandlerMethod } from 'fastify'; | |||||
import { | |||||
UuidService, | |||||
UuidServiceImpl, | |||||
InvalidArgumentTypeError, | |||||
} from '@/modules/uuid/uuid.service'; | |||||
import { constants } from 'http2'; | |||||
export interface UuidController { | |||||
getUsernameUuid: RouteHandlerMethod; | |||||
} | |||||
export class UuidControllerImpl implements UuidController { | |||||
constructor( | |||||
private readonly uuidService: UuidService = new UuidServiceImpl(), | |||||
) { | |||||
// noop | |||||
} | |||||
readonly getUsernameUuid: RouteHandlerMethod = async (request, reply) => { | |||||
const { username, onlineMode } = request.body as { username: string; onlineMode?: boolean }; | |||||
try { | |||||
const response = this.uuidService.getUsernameUuid({ username, onlineMode }); | |||||
reply.send(response); | |||||
} catch (errorRaw) { | |||||
if (errorRaw instanceof InvalidArgumentTypeError) { | |||||
request.log.info(errorRaw); | |||||
reply.status(constants.HTTP_STATUS_BAD_REQUEST).send(errorRaw.message); | |||||
return; | |||||
} | |||||
const error = errorRaw as Error; | |||||
reply.status(constants.HTTP_STATUS_INTERNAL_SERVER_ERROR).send(error.message); | |||||
} | |||||
} | |||||
} |
@@ -0,0 +1,19 @@ | |||||
import { | |||||
GetUsernameUuidOptions, | |||||
getUsernameUuid, | |||||
InvalidArgumentTypeError, | |||||
} from '@modal-sh/minecraft-uuid-core'; | |||||
export interface UuidService { | |||||
getUsernameUuid(options: GetUsernameUuidOptions): Promise<string>; | |||||
} | |||||
export class UuidServiceImpl implements UuidService { | |||||
async getUsernameUuid(options: GetUsernameUuidOptions) { | |||||
return getUsernameUuid(options); | |||||
} | |||||
} | |||||
export { | |||||
InvalidArgumentTypeError, | |||||
}; |
@@ -1,36 +1,23 @@ | |||||
import { FastifyInstance } from 'fastify'; | import { FastifyInstance } from 'fastify'; | ||||
import { AdderController, AdderControllerImpl } from '@/modules/adder'; | |||||
import { UuidController, UuidControllerImpl } from '@/modules/uuid'; | |||||
export const addRoutes = (server: FastifyInstance) => { | export const addRoutes = (server: FastifyInstance) => { | ||||
const adderController: AdderController = new AdderControllerImpl(); | |||||
const uuidController: UuidController = new UuidControllerImpl(); | |||||
return server | return server | ||||
.route({ | .route({ | ||||
method: 'POST', | method: 'POST', | ||||
url: '/add', | |||||
url: '/api/uuid', | |||||
schema: { | schema: { | ||||
body: { | body: { | ||||
type: 'object', | type: 'object', | ||||
required: ['username'], | |||||
properties: { | properties: { | ||||
a: { type: 'number' }, | |||||
b: { type: 'number' }, | |||||
username: { type: 'string' }, | |||||
onlineMode: { type: 'boolean' }, | |||||
} | } | ||||
} | } | ||||
}, | }, | ||||
handler: adderController.addNumbers, | |||||
}) | |||||
.route({ | |||||
method: 'POST', | |||||
url: '/subtract', | |||||
schema: { | |||||
body: { | |||||
type: 'object', | |||||
properties: { | |||||
a: { type: 'number' }, | |||||
b: { type: 'number' }, | |||||
} | |||||
} | |||||
}, | |||||
handler: adderController.subtractNumbers, | |||||
handler: uuidController.getUsernameUuid, | |||||
}); | }); | ||||
}; | }; |
@@ -11,14 +11,13 @@ import { constants } from 'http2'; | |||||
import { createServer } from '../src/server'; | import { createServer } from '../src/server'; | ||||
import { addRoutes } from '../src/routes'; | import { addRoutes } from '../src/routes'; | ||||
import { | import { | ||||
AdderServiceImpl, | |||||
ArgumentOutOfRangeError, | |||||
UuidServiceImpl, | |||||
InvalidArgumentTypeError, | InvalidArgumentTypeError, | ||||
} from '../src/modules/adder'; | |||||
} from '../src/modules/uuid'; | |||||
describe('Example', () => { | describe('Example', () => { | ||||
let server: FastifyInstance; | let server: FastifyInstance; | ||||
const body = { a: 1, b: 2 }; | |||||
const body = { username: 'username', onlineMode: false }; | |||||
beforeAll(() => { | beforeAll(() => { | ||||
server = createServer(); | server = createServer(); | ||||
@@ -32,7 +31,7 @@ describe('Example', () => { | |||||
it('returns result when successful', async () => { | it('returns result when successful', async () => { | ||||
const response = await server | const response = await server | ||||
.inject() | .inject() | ||||
.post('/') | |||||
.post('/api/uuid') | |||||
.body(body) | .body(body) | ||||
.headers({ | .headers({ | ||||
'Accept': 'application/json', | 'Accept': 'application/json', | ||||
@@ -41,28 +40,13 @@ describe('Example', () => { | |||||
}); | }); | ||||
it('returns error when given invalid inputs', async () => { | it('returns error when given invalid inputs', async () => { | ||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
vi.spyOn(UuidServiceImpl.prototype, 'getUsernameUuid').mockImplementationOnce(() => { | |||||
throw new InvalidArgumentTypeError('Invalid input'); | throw new InvalidArgumentTypeError('Invalid input'); | ||||
}); | }); | ||||
const response = await server | const response = await server | ||||
.inject() | .inject() | ||||
.post('/') | |||||
.body(body) | |||||
.headers({ | |||||
'Accept': 'application/json', | |||||
}); | |||||
expect(response.statusCode).toBe(constants.HTTP_STATUS_BAD_REQUEST); | |||||
}); | |||||
it('returns error when given out-of-range inputs', async () => { | |||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
throw new ArgumentOutOfRangeError('Out of range'); | |||||
}); | |||||
const response = await server | |||||
.inject() | |||||
.post('/') | |||||
.post('/api/uuid') | |||||
.body(body) | .body(body) | ||||
.headers({ | .headers({ | ||||
'Accept': 'application/json', | 'Accept': 'application/json', | ||||
@@ -71,14 +55,14 @@ describe('Example', () => { | |||||
}); | }); | ||||
it('returns error when an unexpected error occurs', async () => { | it('returns error when an unexpected error occurs', async () => { | ||||
vi.spyOn(AdderServiceImpl.prototype, 'addNumbers').mockImplementationOnce(() => { | |||||
vi.spyOn(UuidServiceImpl.prototype, 'getUsernameUuid').mockImplementationOnce(() => { | |||||
throw new Error('Unexpected error'); | throw new Error('Unexpected error'); | ||||
}); | }); | ||||
const response = await server | const response = await server | ||||
.inject() | .inject() | ||||
.post('/') | |||||
.body({ a: 1, b: 2 }) | |||||
.post('/api/uuid') | |||||
.body(body) | |||||
.headers({ | .headers({ | ||||
'Accept': 'application/json', | 'Accept': 'application/json', | ||||
}); | }); | ||||