From 16a84e85d1a885e7c0f042dcc043501fe8d51ab4 Mon Sep 17 00:00:00 2001 From: Jacob Nguyen <76754747+jacoobes@users.noreply.github.com> Date: Wed, 15 May 2024 01:44:23 -0500 Subject: [PATCH] add more tests, polish up ioc --- src/core/interfaces.ts | 1 + src/core/ioc/base.ts | 6 ++--- src/core/ioc/container.ts | 6 ++--- src/core/module-loading.ts | 13 +++++++---- src/handlers/event-utils.ts | 4 ++-- src/handlers/ready.ts | 4 ++-- src/types/core-modules.ts | 6 ----- test/core/module-loading.test.ts | 39 ++++++++++++++++++++++++------- test/mockules/!ignd.ts | 0 test/mockules/!ignored/ignored.ts | 0 test/mockules/failed.ts | 0 test/mockules/module.ts | 6 +++++ test/mockules/ug/pass.ts | 6 +++++ 13 files changed, 62 insertions(+), 29 deletions(-) create mode 100644 test/mockules/!ignd.ts create mode 100644 test/mockules/!ignored/ignored.ts create mode 100644 test/mockules/failed.ts create mode 100644 test/mockules/module.ts create mode 100644 test/mockules/ug/pass.ts diff --git a/src/core/interfaces.ts b/src/core/interfaces.ts index 70b9b8a..6e02d30 100644 --- a/src/core/interfaces.ts +++ b/src/core/interfaces.ts @@ -17,6 +17,7 @@ export interface Disposable { dispose(): unknown; } + export interface Emitter { addListener(eventName: string | symbol, listener: AnyFunction): this; removeListener(eventName: string | symbol, listener: AnyFunction): this; diff --git a/src/core/ioc/base.ts b/src/core/ioc/base.ts index 6137a6f..cea5577 100644 --- a/src/core/ioc/base.ts +++ b/src/core/ioc/base.ts @@ -13,7 +13,7 @@ export function disposeAll(logger: Logging|undefined) { type Insertable = - | ((container: UnpackedDependencies) => unknown) + | ((container: UnpackedDependencies) => object) | object const dependencyBuilder = (container: Container, excluded: string[] ) => { return { @@ -25,9 +25,7 @@ const dependencyBuilder = (container: Container, excluded: string[] ) => { if(typeof v !== 'function') { container.addSingleton(key, v) } else { - //TODO fixme - //@ts-ignore - container.addWiredSingleton(key, (cntr: UnpackedDependencies) => v(cntr)) + container.addWiredSingleton(key, (cntr) => v(cntr as UnpackedDependencies)) } }, /** diff --git a/src/core/ioc/container.ts b/src/core/ioc/container.ts index 7ba9828..92aa4f5 100644 --- a/src/core/ioc/container.ts +++ b/src/core/ioc/container.ts @@ -1,4 +1,4 @@ -import * as __Services from '../structures/default-services'; +import type { UnpackedDependencies } from '../../types/utility'; /** * A semi-generic container that provides error handling, emitter, and module store. @@ -46,8 +46,8 @@ export class Container { return false; } - addWiredSingleton(key: string, fn: (c: Container) => object) { - const insert = fn(this); + addWiredSingleton(key: string, fn: (c: Record) => object) { + const insert = fn(this.deps()); return this.addSingleton(key, insert); } diff --git a/src/core/module-loading.ts b/src/core/module-loading.ts index 9a3c438..d4c6074 100644 --- a/src/core/module-loading.ts +++ b/src/core/module-loading.ts @@ -40,7 +40,7 @@ export async function importModule(absPath: string) { let commandModule: Module = fileModule.default; - assert(commandModule , `No export @ ${absPath}. Forgot to ignore with "!"? (!${path.basename(absPath)})?`); + assert(commandModule , `No default export @ ${absPath}`); if ('default' in commandModule) { commandModule = commandModule.default as Module; } @@ -56,10 +56,15 @@ export async function importModule(absPath: string) { export async function* readRecursive(dir: string): AsyncGenerator { - const files = await readdir(dir, { recursive: true, withFileTypes: true }); + const files = await readdir(dir, { withFileTypes: true }); + for (const file of files) { - const fullPath = path.join(file.parentPath, file.name); - if(!file.name.startsWith('!') && !file.isDirectory()) { + const fullPath = path.join(dir, file.name); + if (file.isDirectory()) { + if (!file.name.startsWith('!')) { + yield* readRecursive(fullPath); + } + } else if (!file.name.startsWith('!')) { yield fullPath; } } diff --git a/src/handlers/event-utils.ts b/src/handlers/event-utils.ts index 40d88bc..5d9cb51 100644 --- a/src/handlers/event-utils.ts +++ b/src/handlers/event-utils.ts @@ -22,7 +22,7 @@ import { PayloadType, SernError } from '../core/structures/enums' import { Err, Ok, Result } from 'ts-results-es'; import type { Awaitable, UnpackedDependencies } from '../types/utility'; import type { ControlPlugin } from '../types/core-plugin'; -import type { CommandMeta, CommandModule, Module, Processed } from '../types/core-modules'; +import type { CommandModule, Module, Processed } from '../types/core-modules'; import { EventEmitter } from 'node:events'; import * as assert from 'node:assert'; import { Context } from '../core/structures/context'; @@ -32,13 +32,13 @@ import { inspect } from 'node:util' import { disposeAll } from '../core/ioc/base'; import { arrayifySource, callPlugin, everyPluginOk, filterMapTo, handleError } from '../core/operators'; +import { resultPayload, isAutocomplete, treeSearch } from '../core/functions' function contextArgs(wrappable: Message | BaseInteraction, messageArgs?: string[]) { const ctx = Context.wrap(wrappable); const args = ctx.isMessage() ? ['text', messageArgs!] : ['slash', ctx.options]; return [ctx, args] as [Context, Args]; } -import { resultPayload, isAutocomplete, treeSearch } from '../core/functions' function intoPayload(module: Processed, ) { return pipe(map(arrayifySource), diff --git a/src/handlers/ready.ts b/src/handlers/ready.ts index 74474c6..620197c 100644 --- a/src/handlers/ready.ts +++ b/src/handlers/ready.ts @@ -16,8 +16,8 @@ export default async function(dir: string, deps : UnpackedDependencies) { log?.info({ message: "Client signaled ready, registering modules" }); for await (const path of Files.readRecursive(dir)) { const { module } = await Files.importModule(path); - const validModuleType = module.type >= 0 && module.type <= 1 << 10; - if(!validModuleType) { + const validType = module.type >= 0 && module.type <= 1 << 10; + if(!validType) { throw Error(`Found ${module.name} at ${module.meta.absPath}, which has an incorrect \`type\``); } for(const plugin of module.plugins) { diff --git a/src/types/core-modules.ts b/src/types/core-modules.ts index ed71086..9890573 100644 --- a/src/types/core-modules.ts +++ b/src/types/core-modules.ts @@ -21,12 +21,6 @@ import { AnyCommandPlugin, AnyEventPlugin, ControlPlugin, InitPlugin } from './c import { Awaitable, Args, SlashOptions, SernEventsMapping } from './utility'; -export interface CommandMeta { - fullPath: string; - id: string; - isClass: boolean; -} - export type Processed = T & { name: string; description: string }; export interface Module { diff --git a/test/core/module-loading.test.ts b/test/core/module-loading.test.ts index 59521d4..d4adb29 100644 --- a/test/core/module-loading.test.ts +++ b/test/core/module-loading.test.ts @@ -1,13 +1,9 @@ import { describe, it, expect } from 'vitest' -import { faker } from '@faker-js/faker' +import path from 'node:path' import * as Files from '../../src/core/module-loading' +import { Module } from '../../src/types/core-modules' +import { AssertionError } from 'node:assert' describe('module-loading', () => { - it('should properly extract filename from file, nested once', () => { - const extension = faker.system.fileExt() - const name = faker.system.fileName({ extensionCount: 0 }) - const filename = Files.fmtFileName(name+'.'+extension); - expect(filename).toBe(name) - }) it('should get the filename of the commandmodule (linux, esm)', () => { const fname = "///home/pooba/Projects/sern/halibu/dist/commands/ping.js" const callsiteinfo = Files.parseCallsite(fname) @@ -23,5 +19,32 @@ describe('module-loading', () => { const callsiteinfo = Files.parseCallsite(fname) expect(callsiteinfo.name).toEqual("ping"); }) - + + it('should import a commandModule properly', async () => { + const { module } = await Files.importModule(path.resolve("test", 'mockules', "module.ts")); + expect(module.name).toBe('module') + }) + it('should throw when failed commandModule import', async () => { + try { + await Files.importModule(path.resolve('test', 'mockules', 'failed.ts')) + } catch(e) { + expect(e instanceof AssertionError) + } + }) + it('should throw when failed commandModule import', async () => { + try { + await Files.importModule(path.resolve('test', 'mockules', 'failed.ts')) + } catch(e) { + expect(e instanceof AssertionError) + } + }) + + it('reads all modules in mockules', async () => { + const ps = [] as string[] + for await (const fpath of Files.readRecursive(path.resolve('test', 'mockules'))) { + ps.push(fpath) + } + expect(ps.length === 4) + }) + }) diff --git a/test/mockules/!ignd.ts b/test/mockules/!ignd.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/mockules/!ignored/ignored.ts b/test/mockules/!ignored/ignored.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/mockules/failed.ts b/test/mockules/failed.ts new file mode 100644 index 0000000..e69de29 diff --git a/test/mockules/module.ts b/test/mockules/module.ts new file mode 100644 index 0000000..7a422b5 --- /dev/null +++ b/test/mockules/module.ts @@ -0,0 +1,6 @@ +import { CommandType, commandModule } from '../../src/' +export default commandModule({ + type: CommandType.Both, + description: "", + execute: (Ctx, args) => {} +}) diff --git a/test/mockules/ug/pass.ts b/test/mockules/ug/pass.ts new file mode 100644 index 0000000..9cdadf0 --- /dev/null +++ b/test/mockules/ug/pass.ts @@ -0,0 +1,6 @@ +import { CommandType, commandModule } from '../../../src/' +export default commandModule({ + type: CommandType.Both, + description: "", + execute: (Ctx, args) => {} +})