import { expectTypeOf, test } from 'vitest' import { z } from 'zod/v4' import { BoundInjectionToken, FactoryInjectionToken, InjectionToken, } from '../token/injection-token.mjs' interface FooService { makeFoo(): string } const simpleObjectSchema = z.object({ foo: z.string(), }) const simpleOptionalObjectSchema = z .object({ foo: z.string(), }) .optional() test('InjectionToken.create with class', () => { class MyService { getValue() { return 42 } } const token = InjectionToken.create(MyService) expectTypeOf(token).toMatchTypeOf>() }) test('InjectionToken.create with class and schema', () => { class MyService { constructor(public config: z.infer) {} getValue() { return 42 } } const token = InjectionToken.create(MyService, simpleObjectSchema) expectTypeOf(token).toMatchTypeOf< InjectionToken >() }) test('InjectionToken.create with class and optional schema', () => { class MyService { constructor(public config?: z.infer) {} getValue() { return 42 } } const token = InjectionToken.create(MyService, simpleOptionalObjectSchema) expectTypeOf(token).toMatchTypeOf< InjectionToken >() }) test('InjectionToken.create with string name', () => { const token = InjectionToken.create('FooService') expectTypeOf(token).toMatchTypeOf>() }) test('InjectionToken.create with symbol name', () => { const token = InjectionToken.create(Symbol.for('FooService')) expectTypeOf(token).toMatchTypeOf>() }) test('InjectionToken.create with string name and schema', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) expectTypeOf(token).toMatchTypeOf< InjectionToken >() }) test('InjectionToken.bound creates BoundInjectionToken', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) const boundToken = InjectionToken.bound(token, { foo: 'bar' }) expectTypeOf(boundToken).toMatchTypeOf< BoundInjectionToken >() }) test('InjectionToken.bound requires correct argument type', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) // @ts-expect-error Should fail with wrong argument type InjectionToken.bound(token, { wrong: 'key' }) // @ts-expect-error Should fail with missing required property InjectionToken.bound(token, {}) }) test('InjectionToken.factory creates FactoryInjectionToken', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) const factoryToken = InjectionToken.factory(token, async () => ({ foo: 'bar', })) expectTypeOf(factoryToken).toMatchTypeOf< FactoryInjectionToken >() }) test('InjectionToken.factory requires correct return type', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) // @ts-expect-error Should fail with wrong return type InjectionToken.factory(token, async () => ({ wrong: 'key' })) }) test('InjectionToken.refineType changes BoundInjectionToken type', () => { interface RefinedService { doSomething(): void } const token = InjectionToken.create( 'Service', simpleObjectSchema, ) const boundToken = InjectionToken.bound(token, { foo: 'bar' }) const refinedToken = InjectionToken.refineType(boundToken) expectTypeOf(refinedToken).toMatchTypeOf< BoundInjectionToken >() }) test('BoundInjectionToken has value property with correct type', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) const boundToken = InjectionToken.bound(token, { foo: 'bar' }) expectTypeOf(boundToken.value).toMatchTypeOf<{ foo: string }>() }) test('FactoryInjectionToken has factory property', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) const factoryToken = InjectionToken.factory(token, async () => ({ foo: 'bar', })) expectTypeOf(factoryToken.factory).toBeFunction() }) test('InjectionToken properties', () => { const token = InjectionToken.create( 'FooService', simpleObjectSchema, ) expectTypeOf(token.id).toBeString() expectTypeOf(token.name).toMatchTypeOf any)>() expectTypeOf(token.toString()).toBeString() })