Comments, multi-thread update, bye Mongo
This commit is contained in:
parent
f78b30a522
commit
c1b50e4584
16 changed files with 326 additions and 68 deletions
|
@ -0,0 +1,11 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Load-Balanced Server (PRODUCTION)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||
<option name="checkedMode" value="false" />
|
||||
<option name="envs">
|
||||
<entry key="ANGEL_ENV" value="production" />
|
||||
</option>
|
||||
<option name="filePath" value="$PROJECT_DIR$/bin/multi_server.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -0,0 +1,11 @@
|
|||
<component name="ProjectRunConfigurationManager">
|
||||
<configuration default="false" name="Multithreaded Server (PRODUCTION)" type="DartCommandLineRunConfigurationType" factoryName="Dart Command Line Application" singleton="true">
|
||||
<option name="checkedMode" value="false" />
|
||||
<option name="envs">
|
||||
<entry key="ANGEL_ENV" value="production" />
|
||||
</option>
|
||||
<option name="filePath" value="$PROJECT_DIR$/bin/scaled_server.dart" />
|
||||
<option name="workingDirectory" value="$PROJECT_DIR$" />
|
||||
<method />
|
||||
</configuration>
|
||||
</component>
|
|
@ -7,6 +7,5 @@ import 'common.dart';
|
|||
import 'dart:isolate';
|
||||
|
||||
main(args, SendPort sendPort) async {
|
||||
runZoned(startServer(args, clustered: true, sendPort: sendPort),
|
||||
onError: onError);
|
||||
runZoned(startServer(args, sendPort: sendPort), onError: onError);
|
||||
}
|
||||
|
|
|
@ -5,7 +5,11 @@ import 'package:angel_diagnostics/angel_diagnostics.dart';
|
|||
import 'package:angel_hot/angel_hot.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
startServer(args, {bool clustered: false, SendPort sendPort}) {
|
||||
/// Start a single instance of this application.
|
||||
///
|
||||
/// If a [sendPort] is provided, then the URL of the mounted server will be sent through the port.
|
||||
/// Use this if you are starting multiple instances of your server.
|
||||
startServer(args, {SendPort sendPort}) {
|
||||
return () async {
|
||||
var app = await createServer();
|
||||
var dateFormat = new DateFormat("y-MM-dd");
|
||||
|
@ -13,26 +17,36 @@ startServer(args, {bool clustered: false, SendPort sendPort}) {
|
|||
InternetAddress host;
|
||||
int port;
|
||||
|
||||
if (!clustered) {
|
||||
host = new InternetAddress(app.properties['host']);
|
||||
port = app.properties['port'];
|
||||
} else {
|
||||
host = InternetAddress.LOOPBACK_IP_V4;
|
||||
port = 0;
|
||||
}
|
||||
// Load the right host and port from application config.
|
||||
host = new InternetAddress(app.properties['host']);
|
||||
|
||||
// Listen on port 0 if we are using the load balancer.
|
||||
port = sendPort != null ? 0 : app.properties['port'];
|
||||
|
||||
// Log requests and errors to a log file.
|
||||
await app.configure(logRequests(logFile));
|
||||
HttpServer server;
|
||||
|
||||
// Use `package:angel_hot` in any case, EXCEPT if starting in production mode.
|
||||
//
|
||||
// With hot-reloading, our server will automatically reload in-place on file changes,
|
||||
// for a faster development cycle. :)
|
||||
if (Platform.environment['ANGEL_ENV'] == 'production')
|
||||
server = await app.startServer(host, port);
|
||||
else {
|
||||
var hot = new HotReloader(() async {
|
||||
// If we are hot-reloading, we need to provide a callback
|
||||
// to use to start a fresh instance on-the-fly.
|
||||
var app = await createServer();
|
||||
await app.configure(logRequests(logFile));
|
||||
return app;
|
||||
}, [new Directory('config'), new Directory('lib')]);
|
||||
},
|
||||
// Paths we might want to listen for changes on...
|
||||
[
|
||||
new Directory('config'),
|
||||
new Directory('lib'),
|
||||
new Directory('views')
|
||||
]);
|
||||
server = await hot.startServer(host, port);
|
||||
}
|
||||
|
||||
|
|
|
@ -2,7 +2,9 @@
|
|||
/// This is intended to replace Nginx in your web stack.
|
||||
/// Either use this or another reverse proxy, but there is no real
|
||||
/// reason to use them together.
|
||||
library angel.multiserver;
|
||||
///
|
||||
/// In most cases, you should run `scaled_server.dart` instead of this file.
|
||||
library angel.multi_server;
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:angel_compress/angel_compress.dart';
|
||||
|
@ -10,6 +12,10 @@ import 'package:angel_multiserver/angel_multiserver.dart';
|
|||
|
||||
final Uri cluster = Platform.script.resolve('cluster.dart');
|
||||
|
||||
/// The number of isolates to spawn. You might consider starting one instance
|
||||
/// per processor core on your machine.
|
||||
final int nInstances = Platform.numberOfProcessors;
|
||||
|
||||
main() async {
|
||||
var app = new LoadBalancer();
|
||||
// Or, for SSL:
|
||||
|
@ -21,8 +27,8 @@ main() async {
|
|||
// Cache static assets - just to lower response time
|
||||
await app.configure(cacheResponses(filters: [new RegExp(r'images/\.*')]));
|
||||
|
||||
// Start up 5 instances of our main application
|
||||
await app.spawnIsolates(cluster, count: 5);
|
||||
// Start up multiple instances of our main application.
|
||||
await app.spawnIsolates(cluster, count: nInstances);
|
||||
|
||||
app.onCrash.listen((_) async {
|
||||
// Boot up a new instance on crash
|
||||
|
@ -33,4 +39,5 @@ main() async {
|
|||
var port = 3000;
|
||||
var server = await app.startServer(host, port);
|
||||
print('Listening at http://${server.address.address}:${server.port}');
|
||||
print('Load-balancing $nInstances instance(s)');
|
||||
}
|
||||
|
|
67
bin/scaled_server.dart
Normal file
67
bin/scaled_server.dart
Normal file
|
@ -0,0 +1,67 @@
|
|||
#!/usr/bin/env dart
|
||||
|
||||
/// Most Angel applications will not need to use the load balancer.
|
||||
/// Instead, you can start up a multi-threaded cluster.
|
||||
library angel.scaled_server;
|
||||
|
||||
import 'dart:io';
|
||||
import 'dart:isolate';
|
||||
import 'package:angel_compress/angel_compress.dart';
|
||||
import 'package:angel_multiserver/angel_multiserver.dart';
|
||||
import 'package:angel/angel.dart';
|
||||
|
||||
/// The number of isolates to spawn. You might consider starting one instance
|
||||
/// per processor core on your machine.
|
||||
final int nInstances = Platform.numberOfProcessors;
|
||||
|
||||
main() {
|
||||
var startupPort = new ReceivePort();
|
||||
List<String> startupMessages = [];
|
||||
|
||||
// Start up multiple instances of our application.
|
||||
for (int i = 0; i < nInstances; i++) {
|
||||
Isolate.spawn(isolateMain, [i, startupPort.sendPort]);
|
||||
}
|
||||
|
||||
int nStarted = 0;
|
||||
|
||||
// Listen for notifications of application startup...
|
||||
startupPort.listen((String startupMessage) {
|
||||
startupMessages.add(startupMessage);
|
||||
|
||||
if (++nStarted == nInstances) {
|
||||
// Keep track of how many instances successfully started up,
|
||||
// and print a success message when they all boot.
|
||||
startupMessages.forEach(print);
|
||||
print('Spawned $nInstances instance(s) of Angel.');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void isolateMain(List args) {
|
||||
int instanceId = args[0];
|
||||
SendPort startupPort = args[1];
|
||||
|
||||
createServer().then((app) async {
|
||||
// Response compression via GZIP.
|
||||
//
|
||||
// See the documentation here:
|
||||
// https://github.com/angel-dart/compress
|
||||
app.responseFinalizers.add(gzip());
|
||||
|
||||
// Cache static assets - just to lower response time.
|
||||
//
|
||||
// See the documentation here:
|
||||
// https://github.com/angel-dart/multiserver
|
||||
//
|
||||
// Here is an example of response caching:
|
||||
// https://github.com/angel-dart/multiserver/blob/master/example/cache.dart
|
||||
await app.configure(cacheResponses(filters: [new RegExp(r'images/\.*')]));
|
||||
|
||||
var server = await app.startServer(
|
||||
InternetAddress.ANY_IP_V4, app.properties['port'] ?? 3000);
|
||||
|
||||
// Send a notification back to the main isolate
|
||||
startupPort.send('Instance #$instanceId listening at http://${server.address.address}:${server.port}');
|
||||
});
|
||||
}
|
|
@ -9,8 +9,13 @@ import 'src/services/services.dart' as services;
|
|||
|
||||
/// Creates and configures the server instance.
|
||||
Future<Angel> createServer() async {
|
||||
Angel app = new Angel();
|
||||
/// Passing `startShared` to the constructor allows us to start multiple
|
||||
/// instances of our application concurrently, listening on a single port.
|
||||
///
|
||||
/// This effectively lets us multi-thread the application.
|
||||
var app = new Angel.custom(startShared);
|
||||
|
||||
/// Set up our application, using three plug-ins defined with this project.
|
||||
await app.configure(configuration.configureServer);
|
||||
await app.configure(services.configureServer);
|
||||
await app.configure(routes.configureServer);
|
||||
|
|
|
@ -3,26 +3,27 @@ library angel.config;
|
|||
|
||||
import 'dart:io';
|
||||
import 'package:angel_common/angel_common.dart';
|
||||
// import 'package:angel_multiserver/angel_multiserver.dart';
|
||||
import 'package:mongo_dart/mongo_dart.dart';
|
||||
import 'plugins/plugins.dart' as plugins;
|
||||
|
||||
/// This is a perfect place to include configuration and load plug-ins.
|
||||
configureServer(Angel app) async {
|
||||
await app.configure(loadConfigurationFile());
|
||||
var db = new Db(app.mongo_db);
|
||||
await db.open();
|
||||
app.container.singleton(db);
|
||||
|
||||
await app.configure(mustache(new Directory('views')));
|
||||
await plugins.configureServer(app);
|
||||
|
||||
|
||||
// Uncomment this to enable session synchronization across instances.
|
||||
// This will add the overhead of querying a database at the beginning
|
||||
// and end of every request. Thus, it should only be activated if necessary.
|
||||
// Load configuration from the `config/` directory.
|
||||
//
|
||||
// For applications of scale, it is better to steer clear of session use
|
||||
// entirely.
|
||||
// await app.configure(new MongoSessionSynchronizer(db.collection('sessions')));
|
||||
// See: https://github.com/angel-dart/configuration
|
||||
await app.configure(loadConfigurationFile());
|
||||
|
||||
// Configure our application to render Mustache templates from the `views/` directory.
|
||||
//
|
||||
// See: https://github.com/angel-dart/mustache
|
||||
await app.configure(mustache(new Directory('views')));
|
||||
|
||||
// Apply another plug-ins, i.e. ones that *you* have written.
|
||||
//
|
||||
// Typically, the plugins in `lib/src/config/plugins/plugins.dart` are plug-ins
|
||||
// that add functionality specific to your application.
|
||||
//
|
||||
// If you write a plug-in that you plan to use again, or are
|
||||
// using one created by the community, include it in
|
||||
// `lib/src/config/config.dart`.
|
||||
await plugins.configureServer(app);
|
||||
}
|
||||
|
|
|
@ -2,6 +2,26 @@ library angel.models.user;
|
|||
|
||||
import 'package:angel_framework/common.dart';
|
||||
|
||||
// Model classes in Angel, as a convention, should extend the `Model`
|
||||
// class.
|
||||
//
|
||||
// Angel doesn't box you into using a specific ORM. In fact, you might not
|
||||
// need one at all.
|
||||
//
|
||||
// The out-of-the-box configuration for Angel is to assume
|
||||
// all data you handle is a Map. You might consider leaving it this way, and just
|
||||
// (de)serializing data when you need typing support.
|
||||
//
|
||||
// If you use a `TypedService`, then Angel will perform (de)serialization for you automatically:
|
||||
// https://github.com/angel-dart/angel/wiki/TypedService
|
||||
//
|
||||
// You also have the option of using a source-generated serialization library.
|
||||
// Consider `package:owl` (not affiliated with Angel):
|
||||
// https://github.com/agilord/owl
|
||||
//
|
||||
// The `Model` class has no server-side dependency, and thus you can use it as-is, cross-platform.
|
||||
// This is good for full-stack applications, as you do not have to maintain duplicate class files. ;)
|
||||
|
||||
class User extends Model {
|
||||
@override
|
||||
String id;
|
||||
|
|
|
@ -1,43 +1,92 @@
|
|||
/// This app's route configuration.
|
||||
library angel.routes;
|
||||
|
||||
import 'dart:io';
|
||||
import 'package:angel_common/angel_common.dart';
|
||||
import 'controllers/controllers.dart' as controllers;
|
||||
|
||||
/// Adds global middleware to the application.
|
||||
///
|
||||
/// Use these to apply functionality to requests before business logic is run.
|
||||
///
|
||||
/// More on the request lifecycle:
|
||||
/// https://github.com/angel-dart/angel/wiki/Request-Lifecycle
|
||||
configureBefore(Angel app) async {
|
||||
app.before.add(cors());
|
||||
}
|
||||
|
||||
/// Put your app routes here!
|
||||
///
|
||||
/// See the wiki for information about routing, requests, and responses:
|
||||
/// * https://github.com/angel-dart/angel/wiki/Basic-Routing
|
||||
/// * https://github.com/angel-dart/angel/wiki/Requests-&-Responses
|
||||
configureRoutes(Angel app) async {
|
||||
// Render `views/hello.mustache` when a user visits the application root.
|
||||
app.get('/', (req, ResponseContext res) => res.render('hello'));
|
||||
}
|
||||
|
||||
/// Configures fallback middleware.
|
||||
///
|
||||
/// Use these to run generic actions on requests that were not terminated by
|
||||
/// earlier request handlers.
|
||||
///
|
||||
/// Note that these middleware might not always run.
|
||||
///
|
||||
/// More on the request lifecycle: https://github.com/angel-dart/angel/wiki/Request-Lifecycle
|
||||
configureAfter(Angel app) async {
|
||||
// Uncomment this to proxy over pub serve while in development:
|
||||
// Uncomment this to proxy over `pub serve` while in development.
|
||||
// This is a useful feature for full-stack applications, especially if you
|
||||
// are using Angular2.
|
||||
//
|
||||
// For documentation, see `package:angel_proxy`:
|
||||
// https://github.com/angel-dart/proxy
|
||||
//
|
||||
// await app.configure(new PubServeLayer());
|
||||
|
||||
// Static server at /web or /build/web, depending on if in production
|
||||
// Mount static server at /web or /build/web, depending on if
|
||||
// you are running in production mode. `Cache-Control` headers will also be enabled.
|
||||
//
|
||||
// In production, `Cache-Control` headers will also be enabled.
|
||||
// Read the following two sources for documentation:
|
||||
// * https://medium.com/the-angel-framework/serving-static-files-with-the-angel-framework-2ddc7a2b84ae
|
||||
// * https://github.com/angel-dart/static
|
||||
await app.configure(new CachingVirtualDirectory());
|
||||
|
||||
// Set our application up to handle different errors.
|
||||
//
|
||||
// Read the following two sources for documentation:
|
||||
// * https://github.com/angel-dart/angel/wiki/Error-Handling
|
||||
// * https://github.com/angel-dart/errors
|
||||
var errors = new ErrorHandler(handlers: {
|
||||
// Display a 404 page if no resource is found.
|
||||
404: (req, res) async =>
|
||||
res.render('error', {'message': 'No file exists at ${req.path}.'}),
|
||||
|
||||
// On generic errors, give information about why the application failed.
|
||||
//
|
||||
// An `AngelHttpException` instance will be present in `req.properties`
|
||||
// as `error`.
|
||||
500: (req, res) async => res.render('error', {'message': req.error.message})
|
||||
});
|
||||
|
||||
// Use a fatal error handler to attempt to resolve any issues that
|
||||
// result in Angel not being able to send the user a response.
|
||||
errors.fatalErrorHandler = (AngelFatalError e) async {
|
||||
var req = await RequestContext.from(e.request, app);
|
||||
var res = new ResponseContext(e.request.response, app);
|
||||
res.render('error', {'message': 'Internal Server Error: ${e.error}'});
|
||||
await app.sendResponse(e.request, req, res);
|
||||
try {
|
||||
// Manually create a request and response context.
|
||||
var req = await RequestContext.from(e.request, app);
|
||||
var res = new ResponseContext(e.request.response, app);
|
||||
|
||||
// *Attempt* to render an error page.
|
||||
res.render('error', {'message': 'Internal Server Error: ${e.error}'});
|
||||
await app.sendResponse(e.request, req, res);
|
||||
} catch (_) {
|
||||
// If execution fails here, there is nothing we can do.
|
||||
stderr..writeln('Fatal error: ${e.error}')..writeln(e.stack);
|
||||
}
|
||||
};
|
||||
|
||||
// Throw a 404 if no route matched the request
|
||||
app.after.add(errors.throwError());
|
||||
// Throw a 404 if no route matched the request.
|
||||
app.after.add(() => throw new AngelHttpException.notFound());
|
||||
|
||||
// Handle errors when they occur, based on outgoing status code.
|
||||
// By default, requests will go through the 500 handler, unless
|
||||
|
@ -45,19 +94,45 @@ configureAfter(Angel app) async {
|
|||
// registered.
|
||||
app.after.add(errors.middleware());
|
||||
|
||||
// Pass AngelHttpExceptions through handler as well
|
||||
await app.configure(errors);
|
||||
|
||||
// Compress via GZIP
|
||||
// Ideally you'll run this on a `multiserver` instance, but if not,
|
||||
// feel free to knock yourself out!
|
||||
// Pass AngelHttpExceptions through handler as well.
|
||||
//
|
||||
// app.responseFinalizers.add(gzip());
|
||||
// Again, here is the error handling documentation:
|
||||
// * https://github.com/angel-dart/angel/wiki/Error-Handling
|
||||
// * https://github.com/angel-dart/errors
|
||||
await app.configure(errors);
|
||||
}
|
||||
|
||||
/// Adds response finalizers to the application.
|
||||
///
|
||||
/// These run after every request.
|
||||
///
|
||||
/// See more on the request lifecycle here:
|
||||
/// https://github.com/angel-dart/angel/wiki/Request-Lifecycle
|
||||
configureFinalizers(Angel app) async {}
|
||||
|
||||
/// Adds routes to our application.
|
||||
///
|
||||
/// See the wiki for information about routing, requests, and responses:
|
||||
/// * https://github.com/angel-dart/angel/wiki/Basic-Routing
|
||||
/// * https://github.com/angel-dart/angel/wiki/Requests-&-Responses
|
||||
configureServer(Angel app) async {
|
||||
// The order in which we run these plug-ins is relatively significant.
|
||||
// Try not to change it.
|
||||
|
||||
// Add global middleware.
|
||||
await configureBefore(app);
|
||||
|
||||
// Typically, you want to mount controllers first, after any global middleware.
|
||||
await app.configure(controllers.configureServer);
|
||||
|
||||
// Next, you can add any supplemental routes.
|
||||
await configureRoutes(app);
|
||||
|
||||
// Add handlers to run after requests are handled.
|
||||
//
|
||||
// See the request lifecycle docs to find out why these two
|
||||
// are separate:
|
||||
// https://github.com/angel-dart/angel/wiki/Request-Lifecycle
|
||||
await configureAfter(app);
|
||||
await configureFinalizers(app);
|
||||
}
|
||||
|
|
|
@ -2,10 +2,16 @@
|
|||
library angel.services;
|
||||
|
||||
import 'package:angel_common/angel_common.dart';
|
||||
import 'package:mongo_dart/mongo_dart.dart';
|
||||
import 'user.dart' as user;
|
||||
|
||||
/// Configure our application to use *services*.
|
||||
/// Services must be wired to the app via `app.use`.
|
||||
///
|
||||
/// They provide many benefits, such as instant REST API generation,
|
||||
/// and respond to both REST and WebSockets.
|
||||
///
|
||||
/// Read more here:
|
||||
/// https://github.com/angel-dart/angel/wiki/Service-Basics
|
||||
configureServer(Angel app) async {
|
||||
Db db = app.container.make(Db);
|
||||
await app.configure(user.configureServer(db));
|
||||
await app.configure(user.configureServer());
|
||||
}
|
||||
|
|
|
@ -1,16 +1,29 @@
|
|||
import 'package:angel_common/angel_common.dart';
|
||||
import 'package:angel_framework/hooks.dart' as hooks;
|
||||
import 'package:crypto/crypto.dart' show sha256;
|
||||
import 'package:mongo_dart/mongo_dart.dart';
|
||||
import 'package:random_string/random_string.dart' as rs;
|
||||
import '../models/user.dart';
|
||||
import '../validators/user.dart';
|
||||
export '../models/user.dart';
|
||||
|
||||
configureServer(Db db) {
|
||||
/// Sets up a service mounted at `api/users`.
|
||||
///
|
||||
/// In the real world, you will want to hook this up to a database.
|
||||
/// However, for the sake of the boilerplate, an in-memory service is used,
|
||||
/// so that users are not tied into using just one database. :)
|
||||
configureServer() {
|
||||
return (Angel app) async {
|
||||
app.use('/api/users',
|
||||
new TypedService<User>(new MongoService(db.collection('users'))));
|
||||
// A TypedService can be used to serialize and deserialize data to a class, somewhat like an ORM.
|
||||
//
|
||||
// See here: https://github.com/angel-dart/angel/wiki/TypedService
|
||||
app.use('/api/users', new TypedService<User>(new MapService()));
|
||||
|
||||
// Configure hooks for the user service.
|
||||
// Hooks can be used to add additional functionality, or change the behavior
|
||||
// of services, and run on any service, regardless of which database you are using.
|
||||
//
|
||||
// If you have not already, *definitely* read the service hook documentation:
|
||||
// https://github.com/angel-dart/angel/wiki/Hooks
|
||||
|
||||
var service = app.service('api/users') as HookedService;
|
||||
|
||||
|
|
|
@ -1,5 +1,9 @@
|
|||
import 'package:angel_validate/angel_validate.dart';
|
||||
|
||||
// Validators can be used on the server, in the browser, and even in Flutter.
|
||||
//
|
||||
// It is highly recommended that you read the documentation:
|
||||
// https://github.com/angel-dart/validate
|
||||
final Validator USER = new Validator({
|
||||
'email': [isString, isNotEmpty, isEmail],
|
||||
'username': [isString, isNotEmpty],
|
||||
|
|
14
pubspec.yaml
14
pubspec.yaml
|
@ -1,17 +1,21 @@
|
|||
name: angel
|
||||
description: An easily-extensible web server framework in Dart.
|
||||
publish_to: none
|
||||
publish_to: none # Ensure we don't accidentally publish our private code! ;)
|
||||
environment:
|
||||
sdk: ">=1.19.0"
|
||||
homepage: https://github.com/angel-dart/angel
|
||||
dependencies:
|
||||
angel_common: ^1.0.0
|
||||
angel_configuration: ^1.0.0
|
||||
angel_hot: ^1.0.0-rc.1
|
||||
angel_multiserver: ^1.0.0
|
||||
angel_common: ^1.0.0 # Bundles the most commonly-used Angel packages.
|
||||
angel_configuration: ^1.0.0 # Included in `angel_common`, but also exposes a transformer
|
||||
angel_hot: ^1.0.0-rc.1 # Hot-reloading support. :)
|
||||
angel_multiserver: ^1.0.0 # Helpful for applications running a scale.
|
||||
dev_dependencies:
|
||||
grinder: ^0.8.0
|
||||
http: ^0.11.3
|
||||
test: ^0.12.13
|
||||
transformers:
|
||||
# Injects data from application configuration into Dart code.
|
||||
#
|
||||
# Documentation:
|
||||
# https://github.com/angel-dart/configuration
|
||||
- angel_configuration
|
||||
|
|
|
@ -1,27 +1,42 @@
|
|||
import 'dart:io';
|
||||
import 'package:angel/angel.dart';
|
||||
import 'package:angel_framework/angel_framework.dart';
|
||||
import 'package:angel_test/angel_test.dart';
|
||||
import 'package:test/test.dart';
|
||||
|
||||
// Angel also includes facilities to make testing easier.
|
||||
//
|
||||
// `package:angel_test` ships a client that can test
|
||||
// both plain HTTP and WebSockets.
|
||||
//
|
||||
// Tests do not require your server to actually be mounted on a port,
|
||||
// so they will run faster than they would in other frameworks, where you
|
||||
// would have to first bind a socket, and then account for network latency.
|
||||
//
|
||||
// See the documentation here:
|
||||
// https://github.com/angel-dart/test
|
||||
//
|
||||
// If you are unfamiliar with Dart's advanced testing library, you can read up
|
||||
// here:
|
||||
// https://github.com/dart-lang/test
|
||||
|
||||
main() async {
|
||||
Angel app;
|
||||
TestClient client;
|
||||
|
||||
setUp(() async {
|
||||
app = await createServer();
|
||||
var app = await createServer();
|
||||
client = await connectTo(app);
|
||||
});
|
||||
|
||||
tearDown(() async {
|
||||
await client.close();
|
||||
app = null;
|
||||
});
|
||||
|
||||
test('index users', () async {
|
||||
final response = await client.get('/api/users');
|
||||
// Request a resource at the given path.
|
||||
var response = await client.get('/api/users');
|
||||
|
||||
// By default, we locked this away from the Internet...
|
||||
// Expect a 403 response.
|
||||
expect(response, hasStatus(HttpStatus.FORBIDDEN));
|
||||
});
|
||||
}
|
||||
|
|
|
@ -1,3 +1,9 @@
|
|||
// Grinder is not part of Angel, but you may consider using it
|
||||
// to run tasks, a la Gulp.
|
||||
//
|
||||
// See its documentation here:
|
||||
// https://github.com/google/grinder.dart
|
||||
|
||||
import 'package:grinder/grinder.dart';
|
||||
|
||||
main(args) => grind(args);
|
||||
|
|
Loading…
Reference in a new issue