import { describe, expect, test } from "bun:test"; import "reflect-metadata"; import { AlveoFactory, CircularDependencyError, forwardRef, Inject, Injectable, Module, } from "../index"; describe("Alveo Circular Dependencies", () => { test("should detect circular dependencies without forwardRef", async () => { @Injectable() class A { constructor(_b: unknown) {} } @Injectable() class B { constructor(_a: A) {} } // Manually define metadata to simulate circular dep without forwardRef Reflect.defineMetadata("design:paramtypes", [Object], A); Reflect.defineMetadata("design:paramtypes", [A], B); Reflect.defineMetadata("alveo:inject", [B], A); @Module({ providers: [A, B], }) class RootModule {} expect(AlveoFactory.create(RootModule)).rejects.toThrow( CircularDependencyError, ); }); test("should resolve circular dependencies with forwardRef", async () => { @Injectable() class ServiceA { constructor( @Inject(forwardRef(() => ServiceB)) public readonly serviceB: { name: () => string }, ) {} name() { return "A"; } } @Injectable() class ServiceB { constructor( @Inject(forwardRef(() => ServiceA)) public readonly serviceA: { name: () => string }, ) {} name() { return "B"; } } @Module({ providers: [ServiceA, ServiceB], }) class RootModule {} const app = await AlveoFactory.create(RootModule); const a = await app.get(ServiceA); const b = await app.get(ServiceB); expect(a.serviceB.name()).toBe("B"); expect(b.serviceA.name()).toBe("A"); }); });