From fcddf442a00b89a5cb69caf65a2b4cbb7154f967 Mon Sep 17 00:00:00 2001 From: Sebastian Malton Date: Wed, 14 Dec 2022 09:34:44 -0500 Subject: [PATCH] Add unit tests and fix handling empty runAfter array Signed-off-by: Sebastian Malton --- src/common/runnable/run-many-for.test.ts | 313 +++++++++++++++++++++++ src/common/runnable/run-many-for.ts | 5 +- 2 files changed, 316 insertions(+), 2 deletions(-) diff --git a/src/common/runnable/run-many-for.test.ts b/src/common/runnable/run-many-for.test.ts index c80e206af9..cef234edcd 100644 --- a/src/common/runnable/run-many-for.test.ts +++ b/src/common/runnable/run-many-for.test.ts @@ -8,6 +8,8 @@ import { createContainer, getInjectable, getInjectionToken } from "@ogre-tools/i import type { Runnable } from "./run-many-for"; import { runManyFor } from "./run-many-for"; import { getPromiseStatus } from "../test-utils/get-promise-status"; +import { runInAction } from "mobx"; +import { flushPromises } from "../test-utils/flush-promises"; describe("runManyFor", () => { describe("given no hierarchy, when running many", () => { @@ -340,4 +342,315 @@ describe("runManyFor", () => { ]); }); }); + + describe("given multiple runAfters", () => { + let runMock: AsyncFnMock<(...args: unknown[]) => void>; + let finishingPromise: Promise; + + beforeEach(async () => { + const rootDi = createContainer("irrelevant"); + + runMock = asyncFn<(...args: unknown[]) => void>(); + + const someInjectionToken = getInjectionToken({ + id: "some-injection-token", + }); + + const runnableOneInjectable = getInjectable({ + id: "runnable-1", + instantiate: () => ({ + id: "runnable-1", + run: () => runMock("runnable-1"), + }), + injectionToken: someInjectionToken, + }); + + const runnableTwoInjectable = getInjectable({ + id: "runnable-2", + instantiate: () => ({ + id: "runnable-2", + run: () => runMock("runnable-2"), + runAfter: [], // shouldn't block being called + }), + injectionToken: someInjectionToken, + }); + + const runnableThreeInjectable = getInjectable({ + id: "runnable-3", + instantiate: (di) => ({ + id: "runnable-3", + run: () => runMock("runnable-3"), + runAfter: di.inject(runnableOneInjectable), + }), + injectionToken: someInjectionToken, + }); + + const runnableFourInjectable = getInjectable({ + id: "runnable-4", + instantiate: (di) => ({ + id: "runnable-4", + run: () => runMock("runnable-4"), + runAfter: [di.inject(runnableThreeInjectable)], // should be the same as an single item + }), + injectionToken: someInjectionToken, + }); + + const runnableFiveInjectable = getInjectable({ + id: "runnable-5", + instantiate: (di) => ({ + id: "runnable-5", + run: () => runMock("runnable-5"), + runAfter: di.inject(runnableThreeInjectable), + }), + injectionToken: someInjectionToken, + }); + + const runnableSixInjectable = getInjectable({ + id: "runnable-6", + instantiate: (di) => ({ + id: "runnable-6", + run: () => runMock("runnable-6"), + runAfter: [ + di.inject(runnableFourInjectable), + di.inject(runnableFiveInjectable), + ], + }), + injectionToken: someInjectionToken, + }); + + const runnableSevenInjectable = getInjectable({ + id: "runnable-7", + instantiate: (di) => ({ + id: "runnable-7", + run: () => runMock("runnable-7"), + runAfter: [ + di.inject(runnableFiveInjectable), + di.inject(runnableSixInjectable), + ], + }), + injectionToken: someInjectionToken, + }); + + runInAction(() => { + rootDi.register( + runnableOneInjectable, + runnableTwoInjectable, + runnableThreeInjectable, + runnableFourInjectable, + runnableFiveInjectable, + runnableSixInjectable, + runnableSevenInjectable, + ); + }); + + const runMany = runManyFor(rootDi); + const runSome = runMany(someInjectionToken); + + finishingPromise = runSome(); + + await flushPromises(); + }); + + it("should run 'runnable-1'", () => { + expect(runMock).toBeCalledWith("runnable-1"); + }); + + it("should run 'runnable-2'", () => { + expect(runMock).toBeCalledWith("runnable-2"); + }); + + describe("when 'runnable-1' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-1"]); + }); + + it("should run 'runnable-3'", () => { + expect(runMock).toBeCalledWith("runnable-3"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(3); + }); + }); + + describe("when 'runnable-3' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-3"]); + }); + + it("should run 'runnable-4'", () => { + expect(runMock).toBeCalledWith("runnable-4"); + }); + + it("should run 'runnable-5'", () => { + expect(runMock).toBeCalledWith("runnable-5"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(5); + }); + }); + + describe("when 'runnable-4' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-4"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(5); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(5); + }); + }); + + describe("when 'runnable-5' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-5"]); + }); + + it("should run 'runnable-6'", () => { + expect(runMock).toBeCalledWith("runnable-6"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(6); + }); + }); + + describe("when 'runnable-6' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-6"]); + }); + + it("should run 'runnable-7'", () => { + expect(runMock).toBeCalledWith("runnable-7"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(7); + }); + + describe("when 'runnable-7' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-7"]); + }); + + it("should resolve the runMany promise call", async () => { + await finishingPromise; + }); + }); + }); + }); + }); + }); + + describe("when 'runnable-5' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-5"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(5); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(5); + }); + }); + + describe("when 'runnable-4' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-4"]); + }); + + it("should run 'runnable-6'", () => { + expect(runMock).toBeCalledWith("runnable-6"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(6); + }); + }); + + describe("when 'runnable-6' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-6"]); + }); + + it("should run 'runnable-7'", () => { + expect(runMock).toBeCalledWith("runnable-7"); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(7); + }); + + describe("when 'runnable-7' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-7"]); + }); + + it("should resolve the runMany promise call", async () => { + await finishingPromise; + }); + }); + }); + }); + }); + }); + }); + }); + + describe("when 'runnable-2' resolves", () => { + beforeEach(async () => { + await runMock.resolveSpecific(["runnable-2"]); + }); + + it("shouldn't call any more runnables", () => { + expect(runMock).toBeCalledTimes(2); + }); + }); + }); }); diff --git a/src/common/runnable/run-many-for.ts b/src/common/runnable/run-many-for.ts index 4087faf618..d82ce8d2bd 100644 --- a/src/common/runnable/run-many-for.ts +++ b/src/common/runnable/run-many-for.ts @@ -8,6 +8,7 @@ import { getOrInsertSetFor, isDefined } from "../utils"; import { observable, when } from "mobx"; import * as uuid from "uuid"; import assert from "assert"; +import type { Asyncify } from "type-fest"; export interface Runnable { id: string; @@ -17,7 +18,7 @@ export interface Runnable { type Run = (parameter: Param) => Promise | void; -export type RunMany = (injectionToken: InjectionToken, void>) => Run; +export type RunMany = (injectionToken: InjectionToken, void>) => Asyncify>; const computedNextEdge = (traversed: string[], graph: Map>, currentId: string, seenIds: Set) => { seenIds.add(currentId); @@ -44,7 +45,7 @@ const verifyRunnablesAreDAG = (injectionToken: InjectionToken