Skip to content

Commit

Permalink
feat: various features and stability fixes, added easy way to display…
Browse files Browse the repository at this point in the history
… logs and hook the logger in, added order for events, circular dependency check for middlewares, etc..
  • Loading branch information
theodorDiaconu committed Oct 6, 2024
1 parent 0e99f3e commit d8683a8
Show file tree
Hide file tree
Showing 19 changed files with 496 additions and 172 deletions.
205 changes: 135 additions & 70 deletions README.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions src/__tests__/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,3 +12,4 @@ import "./run.overrides.test";
import "./run.middleware.test";
import "./globalEvents.test";
import "./errors.test";
import "./typesafety.test";
45 changes: 44 additions & 1 deletion src/__tests__/models/Logger.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,8 +133,51 @@ describe("Logger", () => {

await logger[level]("Test log message");

expect(logSpy).toHaveBeenCalledWith(level, "Test log message");
expect(logSpy).toHaveBeenCalledWith(
level,
"Test log message",
undefined // the context parameter
);
});
}
});

it("should auto-print logs based on autoPrintLogsAfter option", async () => {
const autoPrintLevel: LogLevels = "warn";
logger.setPrintThreshold(autoPrintLevel);
const consoleLogSpy = jest.spyOn(console, "log").mockImplementation();

const levels: Array<LogLevels> = [
"trace",
"debug",
"info",
"warn",
"error",
"critical",
];

for (const level of levels) {
logger.setPrintThreshold(level);
await logger.log(level, `Test ${level} message`);

expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining(`Test ${level} message`)
);
}

// ensure events with a higher level thatn auto print level are printed, and lower levels are not
logger.setPrintThreshold("error");
await logger.log("info", "xx Test info message");
await logger.log("error", "xx Test error message");

expect(consoleLogSpy).toHaveBeenCalledWith(
expect.stringContaining("xx Test error message")
);

expect(consoleLogSpy).not.toHaveBeenCalledWith(
expect.stringContaining("xx Test info message")
);

consoleLogSpy.mockRestore();
});
});
9 changes: 5 additions & 4 deletions src/__tests__/models/ResourceInitializer.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,19 @@ import { ResourceInitializer } from "../../models/ResourceInitializer";
import { Store } from "../../models/Store";
import { EventManager } from "../../models/EventManager";
import { defineResource } from "../../define";
import { Logger } from "../../models";

describe("ResourceInitializer", () => {
let store: Store;
let eventManager: EventManager;
let logger: Logger;
let resourceInitializer: ResourceInitializer;

beforeEach(() => {
eventManager = new EventManager();
store = new Store(eventManager);
resourceInitializer = new ResourceInitializer(store, eventManager);
logger = new Logger(eventManager);
store = new Store(eventManager, logger);
resourceInitializer = new ResourceInitializer(store, eventManager, logger);
});

it("should initialize a resource and emit events", async () => {
Expand Down Expand Up @@ -71,7 +74,6 @@ describe("ResourceInitializer", () => {
mockConfig,
mockDependencies
);
expect(emitSpy).toHaveBeenCalledTimes(4);
expect(emitSpy).toHaveBeenCalledWith(mockResource.events.beforeInit, {
config: mockConfig,
});
Expand All @@ -98,7 +100,6 @@ describe("ResourceInitializer", () => {
);

expect(result).toBeUndefined();
expect(emitSpy).toHaveBeenCalledTimes(4);
expect(emitSpy).toHaveBeenCalledWith(mockResource.events.beforeInit, {
config: mockConfig,
});
Expand Down
6 changes: 4 additions & 2 deletions src/__tests__/models/Store.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,17 @@ import {
defineMiddleware,
defineEvent,
} from "../../define";
import { globalResources } from "../../globalResources";
import { Logger } from "../../models";

describe("Store", () => {
let eventManager: EventManager;
let store: Store;
let logger: Logger;

beforeEach(() => {
eventManager = new EventManager();
store = new Store(eventManager);
logger = new Logger(eventManager);
store = new Store(eventManager, logger);
});

it("should initialize the store with a root resource", () => {
Expand Down
7 changes: 5 additions & 2 deletions src/__tests__/models/TaskRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,19 @@ import { Store } from "../../models/Store";
import { EventManager } from "../../models/EventManager";
import { defineTask, defineResource, defineMiddleware } from "../../define";
import { ITask } from "../../defs";
import { Logger } from "../../models";

describe("TaskRunner", () => {
let store: Store;
let eventManager: EventManager;
let taskRunner: TaskRunner;
let logger: Logger;

beforeEach(() => {
eventManager = new EventManager();
store = new Store(eventManager);
taskRunner = new TaskRunner(store, eventManager);
logger = new Logger(eventManager);
store = new Store(eventManager, logger);
taskRunner = new TaskRunner(store, eventManager, logger);
});

it("should run an task without middleware", async () => {
Expand Down
31 changes: 0 additions & 31 deletions src/__tests__/run.hooks.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,36 +106,5 @@ describe("run", () => {

await run(app);
});

it("should have propper type safety", async () => {
const hookEvent = defineEvent<{ message: string }>({ id: "hook.event" });

const task = defineTask({
id: "task",
run: async () => "Task executed",
});

const testResource = defineResource({
id: "test.resource",
dependencies: { task },
init: async () => "Resource Value",
hooks: [
{
event: hookEvent,
run: (event, deps) => {
// @ts-expect-error
event.data.x;

event.data.message;
deps.task;
// @ts-expect-error
deps.task2;
},
},
],
});

expect(true).toBe(true);
});
});
});
26 changes: 26 additions & 0 deletions src/__tests__/run.middleware.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -220,4 +220,30 @@ describe("Middleware", () => {

expect(await run(app)).toBe("Middleware: Middleware: Sub initialized");
});

it("Should prevent circular dependencies when middleware depends on the same task", async () => {
const middleware = defineMiddleware({
id: "middleware",
dependencies: () => ({ task }),
run: async (_, { task }) => {
// example
},
});

const task = defineTask({
id: "task",
middleware: [middleware],
run: async () => "Task executed",
});

const app = defineResource({
id: "sub",
async init(_, {}) {
return "Sub initialized";
},
register: [middleware, task],
});

expect(run(app)).rejects.toThrowError(/Circular dependencies detected/);
});
});
127 changes: 127 additions & 0 deletions src/__tests__/typesafety.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
import { defineEvent, defineTask, defineResource } from "../define";
import { RegisterableItems } from "../defs";

describe("typesafety", () => {
it("tasks, resources: should have propper type safety for dependeices", async () => {
type InputTask = {
message: string;
};

const event = defineEvent<{ message: string }>({
id: "event",
});

const baseTask = defineTask({
id: "task",
run: async (input: InputTask) => "Task executed",
});

const task = defineTask({
id: "task",
dependencies: { baseTask, event },
run: async (input: InputTask, deps) => {
deps.event({ message: input.message });
// @ts-expect-error
deps.event({ messagex: input.message });

deps.baseTask({
message: "Hello, World!",
});

deps.baseTask({
// @ts-expect-error
messagex: 123,
});

// bc no arguments and its required
// @ts-expect-error
deps.baseTask();
},
});

type ResourceType = {
ok: boolean;
};

const dummyResource = defineResource({
id: "dummy.resource",
init: async (config: ResourceType) => "Resource Value",
});
const dummyResourceNoConfig = defineResource({
id: "dummy.resource",
init: async () => "Resource Value",
});

const testResource = defineResource({
id: "test.resource",
dependencies: { task, dummyResource, event },
init: async (_, deps) => {
const result = await deps.task({
message: "Hello, World!",
});

deps.event({ message: "Hello, World!" });
// @ts-expect-error
deps.event();
// @ts-expect-error
deps.event({ messagex: "Hello, World!" });

// @ts-expect-error
deps.dummyResource as number;

deps.dummyResource as string;

// @ts-expect-error
result === 1;

// @ts-expect-error
deps.task2;
},
register: () => [
dummyResourceNoConfig,
// @ts-expect-error
dummyResourceNoConfig.with("hello"),
// @ts-expect-error
dummyResourceNoConfig.with({ anyObject: true }),
dummyResource.with({ ok: true }),
// @ts-expect-error
dummyResource.with({ ok: 123 }),
// @ts-expect-error
dummyResource.with(),
],
});

expect(true).toBe(true);
});

it("events: should have propper type safety", async () => {
const hookEvent = defineEvent<{ message: string }>({ id: "hook.event" });

const task = defineTask({
id: "task",
run: async () => "Task executed",
});

const testResource = defineResource({
id: "test.resource",
dependencies: { task },
init: async () => "Resource Value",
hooks: [
{
event: hookEvent,
run: (event, deps) => {
// @ts-expect-error
event.data.x;

event.data.message;
deps.task;
// @ts-expect-error
deps.task2;
},
},
],
});

expect(true).toBe(true);
});
});
Loading

0 comments on commit d8683a8

Please sign in to comment.