- Consolidate import paths into scoped modules for better structure. - Refactor decorators (`Inject`, `Register`) for improved type safety. - Add `clear` method to the DI container for easier test cleanup. - Introduce lazy initialization for registered instances. - Add comprehensive unit tests for decorators and DI container. - Standardize error handling and naming conventions for exceptions. Signed-off-by: Max P. <Mail@MPassarello.de>
230 lines
6.3 KiB
TypeScript
230 lines
6.3 KiB
TypeScript
// deno-coverage-ignore-file
|
|
// deno-lint-ignore-file no-explicit-any
|
|
import {
|
|
assertEquals,
|
|
assertInstanceOf,
|
|
assertStrictEquals,
|
|
assertThrows,
|
|
} from "https://deno.land/std@0.224.0/assert/mod.ts";
|
|
import { TSinjex } from "../src/classes/mod.ts";
|
|
import { Inject, Register } from "../src/decorators/mod.ts";
|
|
import { DependencyResolutionError } from "../src/interfaces/mod.ts";
|
|
|
|
const container = TSinjex.getInstance() as TSinjex;
|
|
|
|
Deno.test("should inject dependency when necessary is true", () => {
|
|
container.clear();
|
|
container.register("MockDependencyIdentifier", { value: "test-value" });
|
|
|
|
class TestClass {
|
|
@Inject("MockDependencyIdentifier")
|
|
private _dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
}
|
|
|
|
const instance = new TestClass();
|
|
assertEquals(instance.getDependency().value, "test-value");
|
|
});
|
|
|
|
Deno.test("should inject dependency and run initializer", () => {
|
|
container.clear();
|
|
container.register("MockDependencyIdentifier", { value: "test-value" });
|
|
|
|
class TestClass {
|
|
@Inject("MockDependencyIdentifier", (x: any) => {
|
|
x.value = "test-value-init";
|
|
return x;
|
|
})
|
|
dependency!: any;
|
|
public getDependency() {
|
|
return this.dependency;
|
|
}
|
|
}
|
|
|
|
const instance = new TestClass();
|
|
assertEquals(instance.getDependency().value, "test-value-init");
|
|
});
|
|
|
|
Deno.test("should throw error if initializer fails and dependency is necessary", () => {
|
|
container.clear();
|
|
container.register("InitThrowDependencie", { value: "test-value" });
|
|
|
|
class TestClass {
|
|
@Inject<TestClass, any, any>("InitThrowDependencie", () => {
|
|
throw new Error("Initializer error");
|
|
}, true)
|
|
dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this.dependency;
|
|
}
|
|
}
|
|
|
|
assertThrows(
|
|
() => {
|
|
const instance = new TestClass();
|
|
instance.getDependency()();
|
|
},
|
|
Error,
|
|
"Initializer error",
|
|
);
|
|
});
|
|
|
|
Deno.test("should throw DependencyResolutionError if dependency not found", () => {
|
|
container.clear();
|
|
|
|
class TestClass {
|
|
@Inject("NonExistentDependencyIdentifier")
|
|
private _dependency!: any;
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
}
|
|
|
|
assertThrows(() => {
|
|
const instance = new TestClass();
|
|
instance.getDependency()();
|
|
}, DependencyResolutionError);
|
|
});
|
|
|
|
Deno.test("should replace the property with the resolved dependency", () => {
|
|
container.clear();
|
|
container.register("MockDependencyIdentifier", { value: "test-value" });
|
|
|
|
class TestClass {
|
|
@Inject("MockDependencyIdentifier")
|
|
private _dependency!: any;
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
public isDependencyTypeofFunction() {
|
|
return typeof this._dependency === "function";
|
|
}
|
|
}
|
|
|
|
const instance = new TestClass();
|
|
assertEquals(instance.getDependency().value, "test-value");
|
|
assertEquals(instance.isDependencyTypeofFunction(), false);
|
|
assertEquals(instance.getDependency().value, "test-value");
|
|
});
|
|
|
|
Deno.test("Register Decorator: should register a dependency", () => {
|
|
container.clear();
|
|
|
|
@Register("MockDependencyIdentifier")
|
|
class TestClass {
|
|
private readonly _dependency!: any;
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
}
|
|
|
|
assertStrictEquals(
|
|
container.resolve("MockDependencyIdentifier"),
|
|
TestClass,
|
|
);
|
|
});
|
|
|
|
Deno.test("RegisterInstance: should register an instance of a dependency", () => {
|
|
container.clear();
|
|
|
|
@Register("InstanceIdentifier", (x) => new x())
|
|
class TestClass {
|
|
private readonly _dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
|
|
public mark: string = "instance";
|
|
}
|
|
|
|
const resolved = container.resolve<TestClass>("InstanceIdentifier");
|
|
assertEquals(resolved?.mark, "instance");
|
|
});
|
|
|
|
Deno.test("RegisterInstance: should run init function during registration", () => {
|
|
container.clear();
|
|
|
|
@Register("InstanceIdentifier", (x: new () => TestClass) => {
|
|
const instance = new x();
|
|
instance.mark = "init";
|
|
return instance;
|
|
})
|
|
class TestClass {
|
|
private readonly _dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
|
|
public mark: string = "instance";
|
|
}
|
|
|
|
const resolved = container.resolve<TestClass>("InstanceIdentifier");
|
|
assertEquals(resolved?.mark, "init");
|
|
});
|
|
|
|
Deno.test("RegisterInstance: instance should persist modifications", () => {
|
|
container.clear();
|
|
|
|
@Register("InstanceIdentifier", (x) => new x())
|
|
class TestClass {
|
|
private readonly _dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
|
|
public mark: string = "instance";
|
|
public test: string = "test";
|
|
}
|
|
|
|
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
|
|
if (instance1 == null) {
|
|
throw new Error("Instance1 is null");
|
|
}
|
|
instance1.test = "test2";
|
|
|
|
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
|
|
if (instance2 == null) {
|
|
throw new Error("Instance2 is null");
|
|
}
|
|
assertEquals(instance2.test, "test2");
|
|
});
|
|
|
|
Deno.test("RegisterInstance: init function should persist modifications", () => {
|
|
container.clear();
|
|
|
|
@Register("InstanceIdentifier", (x: new () => TestClass) => {
|
|
const instance = new x();
|
|
instance.mark = "init";
|
|
return instance;
|
|
})
|
|
class TestClass {
|
|
private readonly _dependency!: any;
|
|
|
|
public getDependency() {
|
|
return this._dependency;
|
|
}
|
|
|
|
public mark: string = "instance";
|
|
public test: string = "test";
|
|
}
|
|
|
|
const instance1 = container.resolve<TestClass>("InstanceIdentifier");
|
|
if (instance1 == null) {
|
|
throw new Error("Instance1 is null");
|
|
}
|
|
instance1.test = "test2";
|
|
|
|
const instance2 = container.resolve<TestClass>("InstanceIdentifier");
|
|
if (instance2 == null) {
|
|
throw new Error("Instance2 is null");
|
|
}
|
|
assertEquals(instance2.test, "test2");
|
|
});
|