add more tests, polish up ioc

This commit is contained in:
Jacob Nguyen
2024-05-15 01:44:23 -05:00
parent ec45f80be6
commit 16a84e85d1
13 changed files with 62 additions and 29 deletions

View File

@@ -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;

View File

@@ -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))
}
},
/**

View File

@@ -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<string,unknown>) => object) {
const insert = fn(this.deps());
return this.addSingleton(key, insert);
}

View File

@@ -40,7 +40,7 @@ export async function importModule<T>(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<T>(absPath: string) {
export async function* readRecursive(dir: string): AsyncGenerator<string> {
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;
}
}

View File

@@ -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<Module>, ) {
return pipe(map(arrayifySource),

View File

@@ -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<Module>(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) {

View File

@@ -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> = T & { name: string; description: string };
export interface Module {

View File

@@ -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<Module>(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)
})
})

0
test/mockules/!ignd.ts Normal file
View File

View File

0
test/mockules/failed.ts Normal file
View File

6
test/mockules/module.ts Normal file
View File

@@ -0,0 +1,6 @@
import { CommandType, commandModule } from '../../src/'
export default commandModule({
type: CommandType.Both,
description: "",
execute: (Ctx, args) => {}
})

6
test/mockules/ug/pass.ts Normal file
View File

@@ -0,0 +1,6 @@
import { CommandType, commandModule } from '../../../src/'
export default commandModule({
type: CommandType.Both,
description: "",
execute: (Ctx, args) => {}
})