alepha
Version:
Easy-to-use modern TypeScript framework for building many kind of applications.
194 lines (169 loc) • 4.57 kB
text/typescript
import { $inject, $module, Alepha } from "alepha";
import { describe, expect, it } from "vitest";
describe("$module", () => {
it("should create a module with services", () => {
class VeryRandomService {}
class RandomService {
very = $inject(VeryRandomService);
}
const CoreModule = $module({
name: "core",
services: [RandomService, VeryRandomService],
});
class DatabaseService {}
const DatabaseModule = $module({
name: "database",
services: [DatabaseService],
});
class ServerProvider {}
const ServerModule = $module({
name: "server",
imports: [CoreModule, DatabaseModule],
services: [ServerProvider],
});
const alepha = Alepha.create().with(ServerModule);
expect(alepha.graph()).toEqual({
VeryRandomService: { from: ["RandomService", "core"], module: "core" },
RandomService: { from: ["core"], module: "core" },
DatabaseService: { from: ["database"], module: "database" },
ServerProvider: { from: ["server"], module: "server" },
AlsProvider: {
from: ["StateManager", "Alepha"],
module: "alepha.core",
},
EventManager: {
from: ["StateManager", "Alepha"],
module: "alepha.core",
},
StateManager: {
from: ["Alepha"],
module: "alepha.core",
},
Json: {
from: ["JsonSchemaCodec"],
module: "alepha.core",
},
JsonSchemaCodec: {
from: ["StateManager", "CodecManager"],
module: "alepha.core",
},
KeylessJsonSchemaCodec: {
from: ["CodecManager"],
module: "alepha.core",
},
CodecManager: {
from: ["Alepha"],
module: "alepha.core",
},
SchemaValidator: {
from: ["StateManager", "CodecManager"],
module: "alepha.core",
},
});
});
it("should auto inject all services and respect substitutions", async ({
expect,
}) => {
let stack = "";
class A {
constructor() {
stack += "A";
}
a = "a";
}
class A2 {
constructor() {
stack += "2";
}
a = "2";
}
class B {
constructor() {
stack += "B";
}
}
const MyModule = $module({
name: "my.module",
services: [A, B],
});
// substitute BEFORE the module registers — services[] is now eagerly injected
// at registration time, so substitutions must be in place first.
const alepha = Alepha.create()
.with({
provide: A,
use: A2,
})
.with(MyModule);
// services[] is auto-injected at module registration time;
// A is substituted by A2, B is instantiated directly. No A constructor call.
expect(stack).toBe("2B");
await alepha.start();
expect(stack).toBe("2B");
expect(alepha.inject(A).a).toBe("2");
});
it("should not instantiate variants, only the substituted one", async ({
expect,
}) => {
let stack = "";
class Provider {
kind = "base";
}
class MemoryImpl extends Provider {
constructor() {
super();
stack += "M";
}
kind = "memory";
}
class ProductionImpl extends Provider {
constructor() {
super();
stack += "P";
}
kind = "prod";
}
const MyModule = $module({
name: "my.variant.module",
services: [Provider],
variants: [MemoryImpl, ProductionImpl],
register: (alepha) => {
alepha.with({ provide: Provider, use: MemoryImpl });
},
});
const alepha = Alepha.create().with(MyModule);
await alepha.start();
// only MemoryImpl should be constructed, ProductionImpl must never run
expect(stack).toBe("M");
expect(alepha.inject(Provider).kind).toBe("memory");
});
it("should run register() before imports, then auto-inject services", async ({
expect,
}) => {
const order: string[] = [];
class Inner {
constructor() {
order.push("inner-ctor");
}
}
const InnerModule = $module({
name: "inner",
services: [Inner],
});
class Own {
constructor() {
order.push("own-ctor");
}
}
const OuterModule = $module({
name: "outer",
imports: [InnerModule],
services: [Own],
register: () => {
order.push("register");
},
});
const alepha = Alepha.create().with(OuterModule);
await alepha.start();
expect(order).toEqual(["register", "inner-ctor", "own-ctor"]);
});
});