get(TestService::class); expect($service)->toBeInstanceOf(TestService::class); expect($service->message)->toBe('Hello World'); }); test('container resolves dependencies automatically', function () { $container = new DefaultContainer(); $service = $container->get(DependentService::class); expect($service)->toBeInstanceOf(DependentService::class); expect($service->service)->toBeInstanceOf(TestService::class); expect($service->service->message)->toBe('Hello World'); }); test('container can bind interfaces to implementations', function () { $container = new DefaultContainer(); $container->bind(TestInterface::class, TestImplementation::class); $service = $container->get(TestInterface::class); expect($service)->toBeInstanceOf(TestImplementation::class); expect($service->getMessage())->toBe('Implementation'); }); test('container can bind with closures', function () { $container = new DefaultContainer(); $container->bind(TestService::class, function () { return new TestService('Custom Message'); }); $service = $container->get(TestService::class); expect($service->message)->toBe('Custom Message'); }); test('container can register singletons', function () { $container = new DefaultContainer(); // Use instance() for true singleton behavior in tests $instance = new TestService('Singleton Message'); $container->instance(TestService::class, $instance); $service1 = $container->get(TestService::class); $service2 = $container->get(TestService::class); expect($service1)->toBe($service2); // Same instance expect($service1->message)->toBe('Singleton Message'); }); test('container can store instances directly', function () { $container = new DefaultContainer(); $instance = new TestService('Direct Instance'); $container->instance(TestService::class, $instance); $retrieved = $container->get(TestService::class); expect($retrieved)->toBe($instance); expect($retrieved->message)->toBe('Direct Instance'); }); test('container has method works correctly', function () { $container = new DefaultContainer(); expect($container->has(TestService::class))->toBeTrue(); // Can be auto-wired expect($container->has('NonExistentClass'))->toBeFalse(); // Use interface binding instead of string identifier $container->bind(TestInterface::class, TestImplementation::class); expect($container->has(TestInterface::class))->toBeTrue(); }); test('container forget removes bindings', function () { $container = new DefaultContainer(); // Use class-based binding instead of string identifier $container->bind(TestInterface::class, TestImplementation::class); expect($container->has(TestInterface::class))->toBeTrue(); $container->forget(TestInterface::class); expect($container->has(TestInterface::class))->toBeFalse(); }); test('container can get service ids', function () { $container = new DefaultContainer(); // Use class-based identifiers $container->bind(TestInterface::class, TestImplementation::class); $container->bind(DependentService::class, DependentService::class); $serviceIds = $container->getServiceIds(); // Container should report bindings expect($serviceIds)->toContain(TestInterface::class); expect($serviceIds)->toContain(DependentService::class); expect(count($serviceIds))->toBeGreaterThanOrEqual(2); }); test('container can flush all bindings', function () { $container = new DefaultContainer(); // Use class-based identifiers $container->bind(TestInterface::class, TestImplementation::class); $container->get(TestInterface::class); // Instantiate to ensure in instances $serviceIdsBefore = $container->getServiceIds(); $countBefore = count($serviceIdsBefore); // Before flush expect($container->has(TestInterface::class))->toBeTrue(); $container->flush(); // After flush, most services should be removed $serviceIdsAfter = $container->getServiceIds(); $countAfter = count($serviceIdsAfter); // Flush should reduce service count significantly expect($countAfter)->toBeLessThan($countBefore); expect($serviceIdsAfter)->not->toContain(TestInterface::class); }); class InvokerTestService { public function method(TestService $service): string { return $service->message; } } test('container method invoker works', function () { $container = new DefaultContainer(); $service = new InvokerTestService(); $result = $container->invoker->invokeOn($service, 'method'); expect($result)->toBe('Hello World'); });