add(laravel): adding support for laravel inspired serviceproviders

This commit is contained in:
Patrick Stewart 2024-09-28 23:01:12 -07:00
parent 1a5489a993
commit 914949fdac
18 changed files with 3923 additions and 77 deletions

View file

@ -1,86 +1,32 @@
# Protevus Framework
# Protevus Platform Core
[![Protevus Framework](../../angel3_logo.png)](https://github.com/dart-backend/angel)
<p align="center"><img src="https://raw.githubusercontent.com/protevus/branding/main/protevus-logo-bg.png" alt="Protevus Logo" width="400"></p>
![Pub Version (including pre-releases)](https://img.shields.io/pub/v/angel3_framework?include_prereleases)
[![Pub Version (including pre-releases)](https://img.shields.io/pub/v/protevus_core?include_prereleases)](https://pub.dev/packages/protevus_core)
[![Null Safety](https://img.shields.io/badge/null-safety-brightgreen)](https://dart.dev/null-safety)
[![Gitter](https://img.shields.io/gitter/room/angel_dart/discussion)](https://gitter.im/angel_dart/discussion)
[![License](https://img.shields.io/github/license/dart-backend/angel)](https://github.com/dart-backend/angel/tree/master/packages/framework/LICENSE)
[![melos](https://img.shields.io/badge/maintained%20with-melos-f700ff.svg?style=flat-square)](https://github.com/invertase/melos)
[![License](https://img.shields.io/github/license/protevus/platform)](https://github.com/protevus/platform/blob/main/packages/core/LICENSE)
Protevus framework is a high-powered HTTP server with support for dependency injection, sophisticated routing, authentication, ORM, graphql etc. It is designed to keep the core minimal but extensible through a series of plugin packages. It won't dictate which features, databases or web templating engine to use. This flexibility enable Protevus framework to grow with your application as new features can be added to handle the new use cases.
Protevus Platform Core is a high-powered HTTP server framework for Dart, designed to be minimal yet extensible. It provides a robust foundation for building scalable and flexible web applications.
This package is the core package of [Protevus](https://github.com/dart-backend/angel). For more information, visit us at [Protevus Website](https://angel3-framework.web.app).
## Features
## Installation and Setup
- Dependency injection
- Sophisticated routing
- Authentication support
- ORM integration
- GraphQL support
- Extensible plugin architecture
- HTTP/2 support
- WebSocket capabilities
- File handling and MIME type detection
- Environment configuration management
- Performance optimization with caching mechanisms
### (Option 1) Create a new project by cloning from boilerplate templates
## Installation
1. Download and install [Dart](https://dart.dev/get-dart)
Add `protevus_core` to your `pubspec.yaml`:
2. Clone one of the following starter projects:
* [Protevus Basic Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-basic)
* [Protevus ORM Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-orm)
* [Protevus ORM MySQL Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-orm-mysql)
* [Protevus Graphql Template](https://github.com/dukefirehawk/boilerplates/tree/v7/angel3-graphql)
3. Run the project in development mode (*hot-reloaded* is enabled on file changes).
```bash
dart --observe bin/dev.dart
```yaml
dependencies:
protevus_core: ^latest_version
```
4. Run the project in production mode (*hot-reloaded* is disabled).
```bash
dart bin/prod.dart
```
5. Run as docker. Edit and build the image with the provided `Dockerfile` file.
### (Option 2) Create a new project with Protevus CLI
1. Download and install [Dart](https://dart.dev/get-dart)
2. Install the [Protevus CLI](https://pub.dev/packages/angel3_cli):
```bash
dart pub global activate angel3_cli
```
3. On terminal, create a new project:
```bash
angel3 init hello
```
4. Run the project in development mode (*hot-reloaded* is enabled on file changes).
```bash
dart --observe bin/dev.dart
```
5. Run the project in production mode (*hot-reloaded* is disabled).
```bash
dart bin/prod.dart
```
6. Run as docker. Edit and build the image with the provided `Dockerfile` file.
## Performance Benchmark
The performance benchmark can be found at
[TechEmpower Framework Benchmarks Round 21](https://www.techempower.com/benchmarks/#section=data-r21&test=composite)
### Migrating from Angel to Protevus
Check out [Migrating to Protevus](https://angel3-docs.dukefirehawk.com/migration/angel-2.x.x-to-angel3/migration-guide-3)
## Donation & Support
If you like this project and interested in supporting its development, you can make a donation using the following services:
* [![GitHub](https://img.shields.io/static/v1?label=Sponsor&message=%E2%9D%A4&logo=GitHub&color=%23fe8e86)](https://github.com/sponsors/dukefirehawk)
* [paypal](https://paypal.me/dukefirehawk?country.x=MY&locale.x=en_US) service

View file

@ -0,0 +1,3 @@
export 'src/provider/platform_with_providers.dart';
export 'src/provider/service_provider.dart';
export 'src/provider/service_provider_config.dart';

View file

@ -0,0 +1,93 @@
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
import 'package:platform_container/mirrors.dart';
import 'package:logging/logging.dart';
import 'service_provider.dart';
import 'service_provider_manager.dart';
import 'service_provider_config.dart';
import 'provider_discovery.dart';
class PlatformWithProviders {
final Application app;
late ServiceProviderManager _serviceProviderManager;
late ProviderDiscovery _discovery;
late Container container;
PlatformWithProviders(this.app,
{ServiceProviderManager? serviceProviderManager}) {
container = Container(MirrorsReflector());
app.configure(configureContainer);
_serviceProviderManager =
serviceProviderManager ?? ServiceProviderManager(app, _discovery);
}
Future<void> configureContainer(Application app) async {
// Register the container itself
container.registerSingleton((c) => container);
// Configure the container here
// For example, you might want to register some services:
// container.registerSingleton((c) => SomeService());
// You might need to set up dependency injection for routes here
// app.use((req, res) => container.make<SomeService>().handleRequest(req, res));
}
Future<void> registerProvidersFromConfig(ServiceProviderConfig config) async {
for (var providerType in config.providers) {
var provider = _discovery.createInstance(providerType);
if (provider != null) {
if (config.deferredProviders.containsKey(providerType)) {
provider.setDeferred(config.deferredProviders[providerType]!);
}
if (config.providerConfigs.containsKey(providerType)) {
provider.configure(config.providerConfigs[providerType]!);
}
await _serviceProviderManager.register(provider);
}
}
}
Future<void> registerServiceProvider(ServiceProvider provider) async {
await _serviceProviderManager.register(provider);
}
void unregisterServiceProvider(Type providerType) {
_serviceProviderManager.unregister(providerType);
}
T? getServiceProvider<T extends ServiceProvider>() {
return _serviceProviderManager.getProvider<T>();
}
Future<void> bootServiceProviders() async {
await _serviceProviderManager.bootAll();
}
Future<void> bootDeferredServiceProvider(Type providerType) async {
await _serviceProviderManager.bootDeferredProvider(providerType);
}
void setServiceProviderLogLevel(Level level) {
_serviceProviderManager.setLogLevel(level);
}
Future<void> discoverServiceProviders() async {
await _serviceProviderManager.discoverProviders();
}
void registerProviderType(Type type, ServiceProvider Function() factory) {
if (_discovery is ManualProviderDiscovery) {
(_discovery as ManualProviderDiscovery)
.registerProviderType(type, factory);
} else {
throw UnsupportedError(
'Provider type registration is only supported with ManualProviderDiscovery');
}
}
T? getService<T>() {
return app.container.make<T>();
}
}

View file

@ -0,0 +1,54 @@
import 'dart:mirrors' as mirrors;
import 'service_provider.dart';
abstract class ProviderDiscovery {
List<Type> discoverProviders();
ServiceProvider? createInstance(Type type);
}
class MirrorProviderDiscovery implements ProviderDiscovery {
@override
List<Type> discoverProviders() {
var providers = <Type>[];
mirrors.currentMirrorSystem().libraries.values.forEach((lib) {
for (var declaration in lib.declarations.values) {
if (declaration is mirrors.ClassMirror &&
declaration.isSubclassOf(mirrors.reflectClass(ServiceProvider)) &&
!declaration.isAbstract) {
providers.add(declaration.reflectedType);
}
}
});
return providers;
}
@override
ServiceProvider? createInstance(Type type) {
var classMirror = mirrors.reflectClass(type);
if (classMirror.declarations.containsKey(const Symbol(''))) {
var ctor =
classMirror.declarations[const Symbol('')] as mirrors.MethodMirror?;
if (ctor != null && ctor.isConstructor && ctor.parameters.isEmpty) {
return classMirror.newInstance(const Symbol(''), []).reflectee
as ServiceProvider;
}
}
return null;
}
}
class ManualProviderDiscovery implements ProviderDiscovery {
final List<Type> _registeredTypes = [];
final Map<Type, ServiceProvider Function()> _factories = {};
void registerProviderType(Type type, ServiceProvider Function() factory) {
_registeredTypes.add(type);
_factories[type] = factory;
}
@override
List<Type> discoverProviders() => List.unmodifiable(_registeredTypes);
@override
ServiceProvider? createInstance(Type type) => _factories[type]?.call();
}

View file

@ -0,0 +1,39 @@
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
abstract class ServiceProvider {
late Application app;
late Container container;
late final Map<String, dynamic> _config;
bool _isEnabled = true;
bool _isDeferred = false;
ServiceProvider([Map<String, dynamic>? config]) : _config = config ?? {};
Map<String, dynamic> get config => _config;
bool get isEnabled => _isEnabled;
bool get isDeferred => _isDeferred;
void configure(Map<String, dynamic> options) {
_config.addAll(options);
}
void setEnabled(bool enabled) {
_isEnabled = enabled;
}
void setDeferred(bool deferred) {
_isDeferred = deferred;
}
void registerWithContainer(Container container) {
this.container = container;
}
Future<void> register();
Future<void> beforeBoot() async {}
Future<void> boot();
Future<void> afterBoot() async {}
List<Type> get dependencies => [];
}

View file

@ -0,0 +1,82 @@
import 'package:platform_core/core.dart';
import 'package:platform_core/src/provider/service_provider.dart';
class ServiceProviderConfig {
final List<Type> providers;
final Map<Type, bool> deferredProviders;
final Map<Type, Map<String, dynamic>> providerConfigs;
ServiceProviderConfig({
required this.providers,
this.deferredProviders = const {},
this.providerConfigs = const {},
});
factory ServiceProviderConfig.fromMap(Map<String, dynamic> map) {
final providersList =
(map['providers'] as List<dynamic>).cast<Map<String, dynamic>>();
final providers = <Type>[];
final deferredProviders = <Type, bool>{};
final providerConfigs = <Type, Map<String, dynamic>>{};
for (var providerMap in providersList) {
final type = _getTypeFromString(providerMap['type'] as String);
providers.add(type);
if (providerMap['deferred'] == true) {
deferredProviders[type] = true;
}
if (providerMap['config'] != null) {
providerConfigs[type] = (providerMap['config'] as Map<String, dynamic>);
}
}
return ServiceProviderConfig(
providers: providers,
deferredProviders: deferredProviders,
providerConfigs: providerConfigs,
);
}
static Type _getTypeFromString(String typeName) {
// This is a simple implementation. You might need to expand this
// to cover all your service provider types.
switch (typeName) {
case 'TestServiceProvider':
return TestServiceProvider;
case 'AnotherServiceProvider':
return AnotherServiceProvider;
// Add cases for all your service provider types
default:
throw UnimplementedError(
'Type $typeName is not implemented in _getTypeFromString');
}
}
}
// Example service provider classes (you should replace these with your actual service providers)
class TestServiceProvider extends ServiceProvider {
@override
Future<void> register() async {
// Implementation
}
@override
Future<void> boot() async {
// Implementation
}
}
class AnotherServiceProvider extends ServiceProvider {
@override
Future<void> register() async {
// Implementation
}
@override
Future<void> boot() async {
// Implementation
}
}

View file

@ -0,0 +1,131 @@
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
import 'package:logging/logging.dart';
import 'service_provider.dart';
import 'provider_discovery.dart';
class ServiceProviderManager {
final Application app;
final Container container;
final Map<Type, ServiceProvider> _providers = {};
final Set<Type> _booted = {};
final Logger _logger;
final ProviderDiscovery _discovery;
ServiceProviderManager(this.app, this._discovery)
: container = app.container,
_logger = Logger('ServiceProviderManager');
void setLogLevel(Level level) {
_logger.level = level;
}
Future<void> register(ServiceProvider provider) async {
provider.app = app;
provider.registerWithContainer(container);
_providers[provider.runtimeType] = provider;
try {
_logger.info('Registering ${provider.runtimeType}');
await provider.register();
_logger.info('Registered ${provider.runtimeType}');
} catch (e) {
_logger.severe('Failed to register ${provider.runtimeType}', e);
rethrow;
}
}
void unregister(Type providerType) {
if (_providers.containsKey(providerType)) {
_logger.info('Unregistering $providerType');
_providers.remove(providerType);
_booted.remove(providerType);
} else {
_logger.warning(
'Attempted to unregister non-existent provider: $providerType');
}
}
T? getProvider<T extends ServiceProvider>() {
return _providers[T] as T?;
}
Future<void> bootAll() async {
for (var providerType in _providers.keys) {
await _bootProvider(providerType);
}
}
Future<void> _bootProvider(Type providerType) async {
if (_booted.contains(providerType)) return;
var provider = _providers[providerType];
if (provider == null) {
_logger.severe('Provider not found: $providerType');
throw ProviderNotFoundException(providerType);
}
if (!provider.isEnabled) {
_logger.info('Skipping disabled provider: $providerType');
return;
}
if (provider.isDeferred) {
_logger.info('Skipping deferred provider: $providerType');
return;
}
for (var dependencyType in provider.dependencies) {
await _bootProvider(dependencyType);
}
try {
_logger.info('Booting ${provider.runtimeType}');
await provider.beforeBoot();
await provider.boot();
await provider.afterBoot();
_booted.add(providerType);
_logger.info('Booted ${provider.runtimeType}');
} catch (e) {
_logger.severe('Failed to boot ${provider.runtimeType}', e);
throw ProviderBootException(provider, e);
}
}
Future<void> bootDeferredProvider(Type providerType) async {
var provider = _providers[providerType];
if (provider == null || !provider.isDeferred) {
throw ProviderNotFoundException(providerType);
}
await _bootProvider(providerType);
}
Future<void> discoverProviders() async {
for (var type in _discovery.discoverProviders()) {
if (!_providers.containsKey(type)) {
var instance = _discovery.createInstance(type);
if (instance != null) {
await register(instance);
}
}
}
}
}
class ProviderNotFoundException implements Exception {
final Type providerType;
ProviderNotFoundException(this.providerType);
@override
String toString() => 'Provider not found: $providerType';
}
class ProviderBootException implements Exception {
final ServiceProvider provider;
final Object error;
ProviderBootException(this.provider, this.error);
@override
String toString() => 'Failed to boot ${provider.runtimeType}: $error';
}

View file

@ -0,0 +1,37 @@
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
import 'package:platform_container/mirrors.dart';
import 'package:platform_core/src/provider/platform_with_providers.dart';
class TestSetup {
late Application app;
late PlatformWithProviders platformWithProviders;
late Container _container;
Container get container => _container;
Future<void> initialize() async {
app = Application();
// Create a container
_container = Container();
// Set up MirrorsReflector
Reflector.global = MirrorsReflector();
// Initialize PlatformWithProviders
platformWithProviders = PlatformWithProviders(app);
// Configure the app to use our container
app.configure((angel) {
angel.container.registerSingleton<Container>((c) => _container);
});
// Allow some time for initialization
await Future.delayed(Duration(milliseconds: 100));
}
Future<void> tearDown() async {
await app.close();
}
}

View file

@ -78,6 +78,70 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.1"
build:
dependency: transitive
description:
name: build
sha256: "80184af8b6cb3e5c1c4ec6d8544d27711700bc3e6d2efad04238c7b5290889f0"
url: "https://pub.dev"
source: hosted
version: "2.4.1"
build_config:
dependency: transitive
description:
name: build_config
sha256: bf80fcfb46a29945b423bd9aad884590fb1dc69b330a4d4700cac476af1708d1
url: "https://pub.dev"
source: hosted
version: "1.1.1"
build_daemon:
dependency: transitive
description:
name: build_daemon
sha256: "79b2aef6ac2ed00046867ed354c88778c9c0f029df8a20fe10b5436826721ef9"
url: "https://pub.dev"
source: hosted
version: "4.0.2"
build_resolvers:
dependency: transitive
description:
name: build_resolvers
sha256: "339086358431fa15d7eca8b6a36e5d783728cf025e559b834f4609a1fcfb7b0a"
url: "https://pub.dev"
source: hosted
version: "2.4.2"
build_runner:
dependency: "direct dev"
description:
name: build_runner
sha256: "644dc98a0f179b872f612d3eb627924b578897c629788e858157fa5e704ca0c7"
url: "https://pub.dev"
source: hosted
version: "2.4.11"
build_runner_core:
dependency: transitive
description:
name: build_runner_core
sha256: e3c79f69a64bdfcd8a776a3c28db4eb6e3fb5356d013ae5eb2e52007706d5dbe
url: "https://pub.dev"
source: hosted
version: "7.3.1"
built_collection:
dependency: transitive
description:
name: built_collection
sha256: "376e3dd27b51ea877c28d525560790aee2e6fbb5f20e2f85d5081027d94e2100"
url: "https://pub.dev"
source: hosted
version: "5.1.1"
built_value:
dependency: transitive
description:
name: built_value
sha256: c7913a9737ee4007efedaffc968c049fd0f3d0e49109e778edc10de9426005cb
url: "https://pub.dev"
source: hosted
version: "8.9.2"
charcode:
dependency: "direct main"
description:
@ -86,6 +150,22 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.3.1"
checked_yaml:
dependency: transitive
description:
name: checked_yaml
sha256: feb6bed21949061731a7a75fc5d2aa727cf160b91af9a3e464c5e3a32e28b5ff
url: "https://pub.dev"
source: hosted
version: "2.0.3"
code_builder:
dependency: transitive
description:
name: code_builder
sha256: f692079e25e7869c14132d39f223f8eec9830eb76131925143b2129c4bb01b37
url: "https://pub.dev"
source: hosted
version: "4.10.0"
collection:
dependency: "direct main"
description:
@ -118,6 +198,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.0.5"
dart_style:
dependency: transitive
description:
name: dart_style
sha256: "7856d364b589d1f08986e140938578ed36ed948581fbc3bc9aef1805039ac5ab"
url: "https://pub.dev"
source: hosted
version: "2.3.7"
file:
dependency: "direct main"
description:
@ -150,6 +238,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
graphs:
dependency: transitive
description:
name: graphs
sha256: "741bbf84165310a68ff28fe9e727332eef1407342fca52759cb21ad8177bb8d0"
url: "https://pub.dev"
source: hosted
version: "2.3.2"
http:
dependency: "direct dev"
description:
@ -198,6 +294,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.7.1"
json_annotation:
dependency: transitive
description:
name: json_annotation
sha256: "1ce844379ca14835a50d2f019a3099f419082cfdd231cd86a142af94dd5c6bb1"
url: "https://pub.dev"
source: hosted
version: "4.9.0"
lints:
dependency: "direct dev"
description:
@ -246,6 +350,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.0.6"
mockito:
dependency: "direct dev"
description:
name: mockito
sha256: "6841eed20a7befac0ce07df8116c8b8233ed1f4486a7647c7fc5a02ae6163917"
url: "https://pub.dev"
source: hosted
version: "5.4.4"
node_preamble:
dependency: transitive
description:
@ -321,6 +433,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.4"
pubspec_parse:
dependency: transitive
description:
name: pubspec_parse
sha256: c799b721d79eb6ee6fa56f00c04b472dcd44a30d258fac2174a6ec57302678f8
url: "https://pub.dev"
source: hosted
version: "1.3.0"
quiver:
dependency: "direct main"
description:
@ -369,6 +489,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.0"
source_gen:
dependency: transitive
description:
name: source_gen
sha256: "14658ba5f669685cd3d63701d01b31ea748310f7ab854e471962670abcf57832"
url: "https://pub.dev"
source: hosted
version: "1.5.0"
source_map_stack_trace:
dependency: transitive
description:
@ -417,6 +545,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.1.2"
stream_transform:
dependency: transitive
description:
name: stream_transform
sha256: "14a00e794c7c11aa145a170587321aedce29769c08d7f58b1d141da75e3b1c6f"
url: "https://pub.dev"
source: hosted
version: "2.1.0"
string_scanner:
dependency: "direct main"
description:
@ -457,6 +593,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "0.6.5"
timing:
dependency: transitive
description:
name: timing
sha256: "70a3b636575d4163c477e6de42f247a23b315ae20e86442bebe32d3cabf61c32"
url: "https://pub.dev"
source: hosted
version: "1.0.1"
tuple:
dependency: "direct main"
description:

View file

@ -36,6 +36,8 @@ dev_dependencies:
io: ^1.0.0
test: ^1.24.0
lints: ^4.0.0
mockito: ^5.4.4
build_runner: ^2.0.0
# dependency_overrides:
# platform_container:
# path: ../container/angel_container

View file

@ -0,0 +1,55 @@
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:platform_core/core.dart';
import 'package:platform_container/container.dart';
import 'package:platform_core/src/provider/platform_with_providers.dart';
import 'package:platform_core/src/provider/service_provider.dart';
import 'package:platform_core/src/provider/service_provider_manager.dart';
import 'package:platform_core/src/provider/provider_discovery.dart';
@GenerateMocks([ServiceProviderManager, ProviderDiscovery])
import 'platform_with_providers_test.mocks.dart';
void main() {
group('PlatformWithProviders', () {
late PlatformWithProviders platformWithProviders;
late Application app;
late MockServiceProviderManager mockManager;
setUp(() {
app = Application();
mockManager = MockServiceProviderManager();
platformWithProviders =
PlatformWithProviders(app, serviceProviderManager: mockManager);
});
test('PlatformWithProviders initializes correctly', () {
expect(platformWithProviders, isNotNull);
expect(platformWithProviders.app, equals(app));
});
test('container is created', () {
expect(platformWithProviders.container, isNotNull);
expect(platformWithProviders.container, isA<Container>());
});
test('registerServiceProvider calls manager.register', () async {
final provider = MockServiceProvider();
await platformWithProviders.registerServiceProvider(provider);
verify(mockManager.register(provider)).called(1);
});
test('bootServiceProviders calls manager.bootAll', () async {
await platformWithProviders.bootServiceProviders();
verify(mockManager.bootAll()).called(1);
});
// Add more tests for other methods in PlatformWithProviders
});
}
class MockServiceProvider extends Mock implements ServiceProvider {}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,33 @@
import 'package:test/test.dart';
import 'package:platform_core/src/provider/provider_discovery.dart';
import 'package:platform_core/src/provider/service_provider.dart';
class TestServiceProvider extends ServiceProvider {
@override
Future<void> register() async {}
@override
Future<void> boot() async {}
}
void main() {
group('ManualProviderDiscovery', () {
late ManualProviderDiscovery discovery;
setUp(() {
discovery = ManualProviderDiscovery();
});
test('registerProviderType adds type and factory', () {
discovery.registerProviderType(
TestServiceProvider, () => TestServiceProvider());
expect(discovery.discoverProviders(), contains(TestServiceProvider));
expect(discovery.createInstance(TestServiceProvider),
isA<TestServiceProvider>());
});
});
// Note: Testing MirrorProviderDiscovery is challenging in a unit test environment
// due to its reliance on runtime reflection. Consider integration tests for this.
}

View file

@ -0,0 +1,27 @@
import 'package:test/test.dart';
import 'package:platform_core/src/provider/service_provider_config.dart';
void main() {
group('ServiceProviderConfig', () {
test('fromMap creates correct config', () {
final map = {
'providers': [
{
'type': 'TestServiceProvider',
'deferred': true,
'config': {'key': 'value'}
},
{'type': 'AnotherServiceProvider', 'deferred': false},
]
};
final config = ServiceProviderConfig.fromMap(map);
expect(config.providers.length, equals(2));
expect(config.deferredProviders.length, equals(1));
expect(config.providerConfigs.length, equals(1));
// Note: This test assumes you've implemented _getTypeFromString to handle these provider types
// You might need to adjust this part of the test based on your actual implementation
});
});
}

View file

@ -0,0 +1,87 @@
import 'package:test/test.dart';
import 'package:platform_core/core.dart';
import 'package:platform_core/src/provider/platform_with_providers.dart';
import 'package:platform_core/src/provider/service_provider.dart';
import 'package:platform_core/src/provider/service_provider_config.dart';
class TestService {
String getData() => 'Test Data';
}
class TestServiceProvider extends ServiceProvider {
@override
Future<void> register() async {
container.registerSingleton((container) => TestService());
}
@override
Future<void> boot() async {
await Future.delayed(Duration(milliseconds: 100));
}
}
class DeferredServiceProvider extends ServiceProvider {
@override
Future<void> register() async {
container.registerSingleton((container) => 'Deferred Service');
}
@override
Future<void> boot() async {
await Future.delayed(Duration(milliseconds: 100));
}
}
void main() {
late Application app;
late PlatformWithProviders platformWithProviders;
setUp(() async {
app = Application();
platformWithProviders = PlatformWithProviders(app);
// Allow some time for the platform to initialize
await Future.delayed(Duration(milliseconds: 100));
});
tearDown(() async {
await app.close();
});
group('Service Provider Integration Tests', () {
test('Manual provider registration works', () async {
await platformWithProviders
.registerServiceProvider(TestServiceProvider());
await platformWithProviders.bootServiceProviders();
var testService = platformWithProviders.container.make<TestService>();
expect(testService, isNotNull);
expect(testService.getData(), equals('Test Data'));
});
test('Deferred provider is not booted initially', () async {
var config = ServiceProviderConfig(
providers: [DeferredServiceProvider],
deferredProviders: {DeferredServiceProvider: true},
);
await platformWithProviders.registerProvidersFromConfig(config);
await platformWithProviders.bootServiceProviders();
expect(() => platformWithProviders.container.make<String>(),
throwsException);
});
test('Deferred provider can be booted on demand', () async {
var config = ServiceProviderConfig(
providers: [DeferredServiceProvider],
deferredProviders: {DeferredServiceProvider: true},
);
await platformWithProviders.registerProvidersFromConfig(config);
await platformWithProviders.bootServiceProviders();
await platformWithProviders
.bootDeferredServiceProvider(DeferredServiceProvider);
var deferredService = platformWithProviders.container.make<String>();
expect(deferredService, equals('Deferred Service'));
});
});
}

View file

@ -0,0 +1,93 @@
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';
import 'package:mockito/annotations.dart';
import 'package:platform_core/core.dart';
import 'package:platform_core/src/provider/service_provider_manager.dart';
import 'package:platform_core/src/provider/service_provider.dart';
import 'package:platform_core/src/provider/provider_discovery.dart';
import 'package:platform_container/container.dart' as container;
import 'service_provider_manager_test.mocks.dart';
//class MockAngel extends Mock implements Application {}
//class MockServiceProvider extends Mock implements ServiceProvider {}
//class MockProviderDiscovery extends Mock implements ProviderDiscovery {}
// Generate mocks
@GenerateMocks(
[Application, ServiceProvider, ProviderDiscovery, container.Container])
void main() {
group('ServiceProviderManager', () {
late ServiceProviderManager manager;
late Application mockAngel;
late MockProviderDiscovery mockDiscovery;
late MockContainer mockContainer;
setUp(() {
mockAngel = Application();
mockDiscovery = MockProviderDiscovery();
mockContainer = MockContainer();
when(mockAngel.container).thenReturn(mockContainer);
manager = ServiceProviderManager(mockAngel, mockDiscovery);
});
test('register adds provider and calls register method', () async {
final provider = MockServiceProvider();
await manager.register(provider);
verify(provider.registerWithContainer(any)).called(1);
verify(provider.register()).called(1);
});
test('bootAll calls boot for all providers', () async {
final provider1 = MockServiceProvider();
final provider2 = MockServiceProvider();
when(provider1.isEnabled).thenReturn(true);
when(provider2.isEnabled).thenReturn(true);
when(provider1.isDeferred).thenReturn(false);
when(provider2.isDeferred).thenReturn(false);
await manager.register(provider1);
await manager.register(provider2);
await manager.bootAll();
verify(provider1.beforeBoot()).called(1);
verify(provider1.boot()).called(1);
verify(provider1.afterBoot()).called(1);
verify(provider2.beforeBoot()).called(1);
verify(provider2.boot()).called(1);
verify(provider2.afterBoot()).called(1);
});
test('bootDeferredProvider boots only the specified provider', () async {
final provider1 = MockServiceProvider();
final provider2 = MockServiceProvider();
when(provider1.isDeferred).thenReturn(true);
when(provider2.isDeferred).thenReturn(true);
await manager.register(provider1);
await manager.register(provider2);
await manager.bootDeferredProvider(provider1.runtimeType);
verify(provider1.beforeBoot()).called(1);
verify(provider1.boot()).called(1);
verify(provider1.afterBoot()).called(1);
verifyNever(provider2.beforeBoot());
verifyNever(provider2.boot());
verifyNever(provider2.afterBoot());
});
test('unregister removes the provider', () async {
final provider = MockServiceProvider();
await manager.register(provider);
manager.unregister(provider.runtimeType);
expect(manager.getProvider<MockServiceProvider>(), isNull);
});
});
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,32 @@
import 'package:test/test.dart';
import 'package:mockito/mockito.dart';
import 'package:platform_core/src/provider/service_provider.dart';
class MockServiceProvider extends Mock implements ServiceProvider {}
void main() {
group('ServiceProvider', () {
late ServiceProvider provider;
setUp(() {
provider = MockServiceProvider();
});
test('setEnabled changes isEnabled', () {
when(provider.isEnabled).thenAnswer((_) => true);
provider.setEnabled(false);
verify(provider.setEnabled(false)).called(1);
});
test('setDeferred changes isDeferred', () {
when(provider.isDeferred).thenAnswer((_) => false);
provider.setDeferred(true);
verify(provider.setDeferred(true)).called(1);
});
test('configure adds to config', () {
provider.configure({'key': 'value'});
verify(provider.configure({'key': 'value'})).called(1);
});
});
}