Skip to content

Commit

Permalink
Add integration tests starting a web server and performing roundtrips
Browse files Browse the repository at this point in the history
  • Loading branch information
thekid committed Jul 12, 2020
1 parent 116d27a commit bf4af43
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 2 deletions.
3 changes: 2 additions & 1 deletion .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,4 +20,5 @@ before_script:
- echo "vendor/autoload.php" > composer.pth

script:
- sh xp-run xp.unittest.TestRunner src/test/php
- sh xp-run xp.unittest.TestRunner src/test/php
- sh xp-run xp.unittest.TestRunner src/it/php
3 changes: 2 additions & 1 deletion class.pth
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
src/main/php
src/test/php
src/test/php
src/it/php
66 changes: 66 additions & 0 deletions src/it/php/web/unittest/IntegrationTest.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
<?php namespace web\unittest;

use unittest\TestCase;

#[@action(new StartServer('web.unittest.TestingServer', 'connected'))]
class IntegrationTest extends TestCase {
private static $connection;

/** @param peer.Socket $client */
public static function connected($client) {
self::$connection= $client;
}

/**
* Sends a request
*
* @param string $method
* @param string $uri
* @param [:string] $headers
* @param string $body
* @return void
*/
private function send($method, $uri, $headers= [], $body= '') {
self::$connection->write($method.' '.$uri." HTTP/1.0\r\n");
foreach ($headers as $name => $value) {
self::$connection->write($name.': '.$value."\r\n");
}
self::$connection->write("\r\n".$body);
}

#[@test, @values([
# [200, '200 OK'],
# [404, '404 Not Found'],
# [420, '420 Enhanced your calm'],
#])]
public function echo_status($code, $expected) {
$this->send('GET', '/status/'.$code);

$status= self::$connection->readLine();
$this->assertEquals("HTTP/1.0 $expected", $status);
}

#[@test]
public function raising_exception_yield_500() {
$this->send('GET', '/raise/exception');

$status= self::$connection->readLine();
$this->assertEquals("HTTP/1.0 500 Internal Server Error", $status);
}

#[@test]
public function unrouted_uris_yield_404() {
$this->send('GET', '/not-routed');

$status= self::$connection->readLine();
$this->assertEquals("HTTP/1.0 404 Not Found", $status);
}

#[@test]
public function malformed_protocol() {
self::$connection->write("EHLO example.org\r\n");

$status= self::$connection->readLine();
$this->assertEquals("HTTP/1.1 400 Bad Request", $status);
}
}
77 changes: 77 additions & 0 deletions src/it/php/web/unittest/StartServer.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
<?php namespace web\unittest;

use lang\{Runtime, XPClass};
use peer\Socket;
use unittest\{PrerequisitesNotMetError, Test, TestAction, TestClassAction};

class StartServer implements TestAction, TestClassAction {
private $server, $connected, $process, $client;

/**
* Constructor
*
* @param string $server Server process main class
* @param string $connected Name of connection callback
*/
public function __construct($server, $connected) {
$this->server= $server;
$this->connected= $connected;
}

/**
* Starts server
*
* @param lang.XPClass $c
* @return void
* @throws unittest.PrerequisitesNotMetError
*/
public function beforeTestClass(XPClass $c) {
$this->process= Runtime::getInstance()->newInstance(null, 'class', $this->server, []);
$this->process->in->close();

// Check if startup succeeded
$status= $this->process->out->readLine();
if (2 !== sscanf($status, '+ Service %[0-9.]:%d', $host, $port)) {
$this->afterTestClass($c);
throw new PrerequisitesNotMetError('Cannot start server: '.$status, null);
}

$this->client= new Socket($host, $port);
$c->getMethod($this->connected)->invoke(null, [$this->client]);
}

/**
* This method gets invoked before a test method is invoked, and before
* the setUp() method is called.
*
* @param unittest.Test $t
* @return void
* @throws unittest.PrerequisitesNotMetError
*/
public function beforeTest(Test $t) {
$this->client->connect();
}

/**
* This method gets invoked after the test method is invoked and regard-
* less of its outcome, after the tearDown() call has run.
*
* @param unittest.Test $t
* @return void
*/
public function afterTest(Test $t) {
$this->client->close();
}

/**
* Shuts down server
*
* @param lang.XPClass $c
* @return void
*/
public function afterTestClass(XPClass $c) {
$this->process->err->close();
$this->process->out->close();
$this->process->terminate();
}
}
25 changes: 25 additions & 0 deletions src/it/php/web/unittest/TestingApplication.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<?php namespace web\unittest;

use lang\IllegalAccessException;
use web\Application;

class TestingApplication extends Application {

/** @return var */
public function routes() {
return [
'/status/420' => function($req, $res) {
$res->answer(420, 'Enhanced your calm');
$res->send('Answered with status 420', 'text/plain');
},
'/status' => function($req, $res) {
$status= basename($req->uri()->path());
$res->answer($status);
$res->send('Answered with status '.$status, 'text/plain');
},
'/raise/exception' => function($req, $res) {
throw new IllegalAccessException('No access!');
}
];
}
}
37 changes: 37 additions & 0 deletions src/it/php/web/unittest/TestingServer.class.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
<?php namespace web\unittest;

use lang\Throwable;
use peer\ServerSocket;
use peer\server\Server;
use util\cmd\Console;
use web\{Environment, Logging};
use xp\web\srv\HttpProtocol;

/**
* Socket server used by integration tests.
*
* Process interaction is performed by messages this server prints to
* standard out:
*
* - Server listens on a free port @ 127.0.0.1
* - On startup success, "+ Service (IP):(PORT)" is written
* - On errors, "- " and the exception message are written
*/
class TestingServer {

/** Starts the server */
public static function main(array $args) {
$application= new TestingApplication(new Environment('test', '.', '.', '.', [], null));

$s= new Server();
try {
$s->listen(new ServerSocket('127.0.0.1', 0), new HttpProtocol($application, new Logging(null)));
$s->init();
Console::writeLinef('+ Service %s:%d', $s->socket->host, $s->socket->port);
$s->service();
$s->shutdown();
} catch (Throwable $e) {
Console::writeLine('- ', $e->getMessage());
}
}
}

0 comments on commit bf4af43

Please sign in to comment.