mirror of
https://codeberg.org/demostf/api.git
synced 2026-06-03 09:54:17 +02:00
add basic api tests
This commit is contained in:
parent
64b0aff075
commit
e00e6ece5f
30 changed files with 350 additions and 17 deletions
|
|
@ -1,3 +1,3 @@
|
||||||
.env
|
.env
|
||||||
tests
|
test
|
||||||
vendor
|
vendor
|
||||||
|
|
|
||||||
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
.idea
|
.idea
|
||||||
.env
|
.env
|
||||||
vendor
|
vendor
|
||||||
|
node_modules
|
||||||
|
|
|
||||||
|
|
@ -19,19 +19,22 @@ env:
|
||||||
- DB_PASSWORD=
|
- DB_PASSWORD=
|
||||||
- DB_DATABASE=travis_ci_test
|
- DB_DATABASE=travis_ci_test
|
||||||
- BASE_HOST=example.com
|
- BASE_HOST=example.com
|
||||||
|
- DEMO_ROOT=/tmp/demos
|
||||||
|
|
||||||
install:
|
install:
|
||||||
- composer install --no-interaction
|
- composer install --no-interaction
|
||||||
|
- npm install
|
||||||
|
|
||||||
before_script:
|
before_script:
|
||||||
|
- phpenv config-add travis.php.ini
|
||||||
- psql -c 'create database travis_ci_test;' -U postgres
|
- psql -c 'create database travis_ci_test;' -U postgres
|
||||||
- wget https://raw.githubusercontent.com/demostf/db/master/schema.sql
|
- wget https://raw.githubusercontent.com/demostf/db/master/schema.sql
|
||||||
- psql -U postgres -d travis_ci_test -f schema.sql
|
- psql -U postgres -d travis_ci_test -f schema.sql
|
||||||
- echo "error_reporting = E_ALL & ~E_DEPRECATED" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini #random-lib complains about mcrypt
|
- echo "error_reporting = E_ALL & ~E_DEPRECATED" >> ~/.phpenv/versions/$(phpenv version-name)/etc/php.ini #random-lib complains about mcrypt
|
||||||
|
|
||||||
script:
|
script:
|
||||||
- cd tests
|
- phpunit --coverage-clover coverage.xml --configuration test/phpunit.xml
|
||||||
- phpunit --coverage-clover coverage.xml --configuration phpunit.xml
|
- node node_modules/.bin/mocha --recursive
|
||||||
|
|
||||||
after_success:
|
after_success:
|
||||||
- bash <(curl -s https://codecov.io/bash)
|
- bash <(curl -s https://codecov.io/bash)
|
||||||
|
|
|
||||||
15
Makefile
15
Makefile
|
|
@ -6,6 +6,17 @@ docker:
|
||||||
testdb:
|
testdb:
|
||||||
docker run -d --name api-test -p 5433:5432 -e POSTGRES_PASSWORD=test demostf/db
|
docker run -d --name api-test -p 5433:5432 -e POSTGRES_PASSWORD=test demostf/db
|
||||||
|
|
||||||
|
node_modules: package.json
|
||||||
|
npm install
|
||||||
|
|
||||||
|
.PHONY: mocha
|
||||||
|
mocha: node_modules
|
||||||
|
DEMO_ROOT=/tmp/demos DB_PORT=5433 DB_TYPE=pgsql DB_HOST=localhost DB_USERNAME=postgres DB_USERNAME=postgres DB_PASSWORD=test DB_DATABASE=postgres\
|
||||||
|
node node_modules/.bin/mocha --recursive
|
||||||
|
|
||||||
|
.PHONY: phpunit
|
||||||
|
phpunit:
|
||||||
|
cd test; DB_PORT=5433 DB_TYPE=pgsql DB_HOST=localhost DB_USERNAME=postgres DB_USERNAME=postgres DB_PASSWORD=test DB_DATABASE=postgres phpunit
|
||||||
|
|
||||||
.PHONY: test
|
.PHONY: test
|
||||||
test:
|
tests: phpunit mocha
|
||||||
cd tests; DB_PORT=5433 DB_TYPE=pgsql DB_HOST=localhost DB_USERNAME=postgres DB_USERNAME=postgres DB_PASSWORD=test DB_DATABASE=postgres phpunit
|
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
],
|
],
|
||||||
"psr-4": {
|
"psr-4": {
|
||||||
"Demostf\\API\\": "src/",
|
"Demostf\\API\\": "src/",
|
||||||
"Demostf\\API\\Test\\": "tests/"
|
"Demostf\\API\\Test\\": "test/"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"require-dev": {
|
"require-dev": {
|
||||||
|
|
|
||||||
8
package.json
Normal file
8
package.json
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
{
|
||||||
|
"devDependencies": {
|
||||||
|
"chakram": "^1.5.0",
|
||||||
|
"express": "^4.15.3",
|
||||||
|
"mocha": "^3.4.2",
|
||||||
|
"tf2-demo": "^1.1.3"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -123,4 +123,8 @@ class Container {
|
||||||
public function getEditKey(): string {
|
public function getEditKey(): string {
|
||||||
return $this->editKey;
|
return $this->editKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getConnection(): Connection {
|
||||||
|
return $this->connection;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -82,19 +82,19 @@ class DemoController extends BaseController {
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setDemoUrl($id) {
|
public function setDemoUrl($id) {
|
||||||
$hash = $this->query('hash', '');
|
$hash = $this->post('hash', '');
|
||||||
$backend = $this->query('backend', '');
|
$backend = $this->post('backend', '');
|
||||||
$path = $this->query('path', '');
|
$path = $this->post('path', '');
|
||||||
$url = $this->query('url', '');
|
$url = $this->post('url', '');
|
||||||
$editKey = $this->query('key', '');
|
$editKey = $this->post('key', '');
|
||||||
if ($editKey !== $this->editKey) {
|
if ($editKey !== $this->editKey || $editKey === '') {
|
||||||
throw new \InvalidArgumentException('Invalid key');
|
throw new \InvalidArgumentException('Invalid key');
|
||||||
}
|
}
|
||||||
|
|
||||||
$demo = $this->demoProvider->get($id);
|
$demo = $this->demoProvider->get((int)$id);
|
||||||
$existingHash = $demo->getHash();
|
$existingHash = $demo->getHash();
|
||||||
if ($existingHash === '' || $existingHash === $hash) {
|
if ($existingHash === '' || $existingHash === $hash) {
|
||||||
$this->demoProvider->setDemoUrl($id, $backend, $url, $path);
|
$this->demoProvider->setDemoUrl((int)$id, $backend, $url, $path);
|
||||||
} else {
|
} else {
|
||||||
throw new \InvalidArgumentException('Invalid demo hash');
|
throw new \InvalidArgumentException('Invalid demo hash');
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,10 +16,19 @@ class UploadController extends BaseController {
|
||||||
$blu = $this->post('blu', 'BLU');
|
$blu = $this->post('blu', 'BLU');
|
||||||
$name = $this->post('name', 'Unnamed');
|
$name = $this->post('name', 'Unnamed');
|
||||||
$demo = $this->file('demo');
|
$demo = $this->file('demo');
|
||||||
|
if (is_null($demo)) {
|
||||||
|
echo 'No demo uploaded';
|
||||||
|
return;
|
||||||
|
}
|
||||||
$demoFile = $demo['tmp_name'];
|
$demoFile = $demo['tmp_name'];
|
||||||
|
|
||||||
try {
|
try {
|
||||||
echo $this->uploadProvider->upload($key, $red, $blu, $name, $demoFile);
|
$result = $this->uploadProvider->upload($key, $red, $blu, $name, $demoFile);
|
||||||
|
if ($result === 'Invalid key') {
|
||||||
|
\Flight::response()->status(401)->write($result)->send();
|
||||||
|
} else {
|
||||||
|
echo $result;
|
||||||
|
}
|
||||||
} catch (\Exception $e) {
|
} catch (\Exception $e) {
|
||||||
\Flight::response()
|
\Flight::response()
|
||||||
->status(500)
|
->status(500)
|
||||||
|
|
|
||||||
|
|
@ -42,9 +42,9 @@ Flight::route('/stats', [$infoController, 'stats']);
|
||||||
Flight::route('/demos', [$demoController, 'listDemos']);
|
Flight::route('/demos', [$demoController, 'listDemos']);
|
||||||
Flight::route('/demos/@id', [$demoController, 'get']);
|
Flight::route('/demos/@id', [$demoController, 'get']);
|
||||||
Flight::route('/demos/@id/chat', [$demoController, 'chat']);
|
Flight::route('/demos/@id/chat', [$demoController, 'chat']);
|
||||||
|
Flight::route('/demos/@id/url', [$demoController, 'setDemoUrl']);
|
||||||
Flight::route('/profiles/@steamid', [$demoController, 'listProfile']);
|
Flight::route('/profiles/@steamid', [$demoController, 'listProfile']);
|
||||||
Flight::route('/uploads/@steamid', [$demoController, 'listUploads']);
|
Flight::route('/uploads/@steamid', [$demoController, 'listUploads']);
|
||||||
Flight::route('/demos/@id/url', [$demoController, 'setDemoUrl']);
|
|
||||||
|
|
||||||
Flight::route('/users/search', [$userController, 'search']);
|
Flight::route('/users/search', [$userController, 'search']);
|
||||||
Flight::route('/users/@steamid', [$userController, 'get']);
|
Flight::route('/users/@steamid', [$userController, 'get']);
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
require '../app.php';
|
require __DIR__ . '/../app.php';
|
||||||
|
|
|
||||||
252
test/Integration/Tests.js
Normal file
252
test/Integration/Tests.js
Normal file
|
|
@ -0,0 +1,252 @@
|
||||||
|
/**
|
||||||
|
* parser server
|
||||||
|
*/
|
||||||
|
var DemoParser = require('tf2-demo');
|
||||||
|
var express = require('express');
|
||||||
|
var app = express();
|
||||||
|
var url = require('url');
|
||||||
|
var https = require('https');
|
||||||
|
var http = require('http');
|
||||||
|
|
||||||
|
app.set('port', (process.env.PORT || 80));
|
||||||
|
app.use(express.static(__dirname + '/public'));
|
||||||
|
|
||||||
|
app.get('/', function (request, response) {
|
||||||
|
response.send('Hello World!');
|
||||||
|
});
|
||||||
|
|
||||||
|
function handleDataStream(stream, cb) {
|
||||||
|
var buffers = [];
|
||||||
|
stream.on('data', function (buffer) {
|
||||||
|
buffers.push(buffer);
|
||||||
|
});
|
||||||
|
stream.on('end', function () {
|
||||||
|
try {
|
||||||
|
var buffer = Buffer.concat(buffers);
|
||||||
|
var demo = DemoParser.Demo.fromNodeBuffer(buffer);
|
||||||
|
var parser = demo.getParser(true);
|
||||||
|
var header = parser.readHeader();
|
||||||
|
var match = parser.parseBody();
|
||||||
|
var body = match.getState();
|
||||||
|
body.header = header;
|
||||||
|
cb(body);
|
||||||
|
} catch (e) {
|
||||||
|
cb(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
app.post('/parse', function (req, res) {
|
||||||
|
handleDataStream(req, function (body) {
|
||||||
|
res.set('Content-Type', 'application/json');
|
||||||
|
res.write(JSON.stringify(body));
|
||||||
|
res.end();
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
app.listen(9123);
|
||||||
|
|
||||||
|
|
||||||
|
const chakram = require('chakram');
|
||||||
|
const expect = chakram.expect;
|
||||||
|
const root = 'http://localhost:8000/';
|
||||||
|
const fs = require('fs');
|
||||||
|
|
||||||
|
process.env.PARSER_URL = `http://localhost:9123/parse`;
|
||||||
|
process.env.EDIT_SECRET = 'edit_key';
|
||||||
|
|
||||||
|
chakram.setRequestDefaults({baseUrl: root});
|
||||||
|
|
||||||
|
before((done) => {
|
||||||
|
console.log('spawn server');
|
||||||
|
const server = require('child_process').spawn('php', ['-S', '0.0.0.0:8000', 'router.php'], {
|
||||||
|
cwd: __dirname + '/../',
|
||||||
|
env: process.env
|
||||||
|
});
|
||||||
|
server.stdout.on('data', (data) => {
|
||||||
|
console.log(`stdout: ${data}`);
|
||||||
|
});
|
||||||
|
server.stderr.on('data', (data) => {
|
||||||
|
console.log(`stderr: ${data}`);
|
||||||
|
});
|
||||||
|
after(() => {
|
||||||
|
console.log('clean server');
|
||||||
|
server && server.kill();
|
||||||
|
});
|
||||||
|
setTimeout(done, 1000);
|
||||||
|
});
|
||||||
|
|
||||||
|
before("reset db", function () {
|
||||||
|
chakram.post("reset");
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
beforeEach("create test user", function () {
|
||||||
|
chakram.post("testuser");
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
afterEach("reset db", function () {
|
||||||
|
chakram.post("reset");
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
function uploadDemo(file) {
|
||||||
|
return chakram.post("upload", undefined, {
|
||||||
|
formData: {
|
||||||
|
name: 'foo',
|
||||||
|
blue: 'BLU',
|
||||||
|
red: 'RED',
|
||||||
|
demo: fs.createReadStream(file),
|
||||||
|
key: 'key1'
|
||||||
|
}
|
||||||
|
}).then((response) => {
|
||||||
|
return parseInt(response.body.match(/\/(\d+)/)[1], 10);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
chakram.addMethod("text", function (respObj, text) {
|
||||||
|
const body = respObj.response.body;
|
||||||
|
this.assert(body === text,
|
||||||
|
'expected response text ' + body + ' to equal ' + text,
|
||||||
|
'expected response text ' + body + ' to not be equal to ' + text);
|
||||||
|
});
|
||||||
|
chakram.addMethod("containsText", function (respObj, text) {
|
||||||
|
const body = respObj.response.body;
|
||||||
|
this.assert(body.indexOf(text) !== -1,
|
||||||
|
'expected response text ' + body + ' to contain ' + text,
|
||||||
|
'expected response text ' + body + ' to not contain ' + text);
|
||||||
|
});
|
||||||
|
describe("Upload", function () {
|
||||||
|
this.timeout(1000 * 30);
|
||||||
|
it("fails without valid key", function () {
|
||||||
|
const response = chakram.post("upload", undefined, {
|
||||||
|
formData: {
|
||||||
|
name: 'foo',
|
||||||
|
blue: 'BLU',
|
||||||
|
red: 'RED',
|
||||||
|
demo: fs.createReadStream(__dirname + '/../data/product.dem'),
|
||||||
|
key: 'dummy'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(response).to.have.status(401);
|
||||||
|
expect(response).to.be.text('Invalid key');
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("returns the demo path on success", function () {
|
||||||
|
const response = chakram.post("upload", undefined, {
|
||||||
|
formData: {
|
||||||
|
name: 'foo',
|
||||||
|
blue: 'BLU',
|
||||||
|
red: 'RED',
|
||||||
|
demo: fs.createReadStream(__dirname + '/../data/product.dem'),
|
||||||
|
key: 'key1'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
// expect(response).to.have.status(401);
|
||||||
|
expect(response).to.be.containsText('STV available at: ');
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Demo listing", function () {
|
||||||
|
this.timeout(1000 * 30);
|
||||||
|
|
||||||
|
it("starts empty", function () {
|
||||||
|
const response = chakram.get("demos");
|
||||||
|
expect(response).to.have.status(200);
|
||||||
|
expect(response).to.have.header("content-type", "application/json; charset=utf-8");
|
||||||
|
expect(response).to.comprise.of.json([]);
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("contains uploaded demo", function () {
|
||||||
|
return uploadDemo(__dirname + '/../data/product.dem').then(id => {
|
||||||
|
return chakram.get("demos").then(response => {
|
||||||
|
const body = response.body;
|
||||||
|
expect(body[0].id).to.be.equal(id);
|
||||||
|
expect(body[0].name).to.be.equal('foo');
|
||||||
|
expect(body[0].server).to.be.equal('UGC Highlander Match');
|
||||||
|
expect(body[0].duration).to.be.equal(778);
|
||||||
|
expect(body[0].nick).to.be.equal('SourceTV Demo');
|
||||||
|
expect(body[0].map).to.be.equal('koth_product_rc8');
|
||||||
|
expect(body[0].red).to.be.equal('RED');
|
||||||
|
expect(body[0].blue).to.be.equal('BLU');
|
||||||
|
expect(body[0].redScore).to.be.equal(3);
|
||||||
|
expect(body[0].blueScore).to.be.equal(0);
|
||||||
|
expect(body[0].playerCount).to.be.equal(18);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("Set url", function () {
|
||||||
|
this.timeout(1000 * 30);
|
||||||
|
|
||||||
|
it("fails with invalid key", function () {
|
||||||
|
return uploadDemo(__dirname + '/../data/product.dem').then(id => {
|
||||||
|
return chakram.get("demos").then(response => {
|
||||||
|
const setUrl = chakram.post(`/demos/${id}/url`, undefined, {
|
||||||
|
formData: {
|
||||||
|
hash: 'asd',
|
||||||
|
backend: 'foo',
|
||||||
|
url: 'http://bar',
|
||||||
|
path: 'bar',
|
||||||
|
key: 'foo'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(setUrl).to.be.containsText('Invalid key');
|
||||||
|
expect(setUrl).to.have.status(500);
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("fails with invalid hash", function () {
|
||||||
|
return uploadDemo(__dirname + '/../data/product.dem').then(id => {
|
||||||
|
return chakram.get("demos").then(response => {
|
||||||
|
const setUrl = chakram.post(`/demos/${id}/url`, undefined, {
|
||||||
|
formData: {
|
||||||
|
hash: 'asd',
|
||||||
|
backend: 'foo',
|
||||||
|
url: 'http://bar',
|
||||||
|
path: 'bar',
|
||||||
|
key: 'edit_key'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(setUrl).to.be.containsText('Invalid demo hash');
|
||||||
|
expect(setUrl).to.have.status(500);
|
||||||
|
return chakram.wait();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it("changes url, backend and path on success", function () {
|
||||||
|
return uploadDemo(__dirname + '/../data/product.dem').then(id => {
|
||||||
|
return chakram.get(`demos/${id}`).then(response => {
|
||||||
|
const hash = response.body.hash;
|
||||||
|
|
||||||
|
const setUrl = chakram.post(`/demos/${id}/url`, undefined, {
|
||||||
|
formData: {
|
||||||
|
hash: hash,
|
||||||
|
backend: 'foo',
|
||||||
|
url: 'http://bar',
|
||||||
|
path: 'bar',
|
||||||
|
key: 'edit_key'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(setUrl).to.have.status(200);
|
||||||
|
return setUrl.then(response => {
|
||||||
|
return chakram.get(`demos/${id}`)
|
||||||
|
}).then(response => {
|
||||||
|
const body = response.body;
|
||||||
|
expect(body.id).to.be.equal(id);
|
||||||
|
expect(body.backend).to.be.equal('foo');
|
||||||
|
expect(body.url).to.be.equal('http://bar');
|
||||||
|
expect(body.path).to.be.equal('bar');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
43
test/router.php
Normal file
43
test/router.php
Normal file
|
|
@ -0,0 +1,43 @@
|
||||||
|
<?php declare(strict_types=1);
|
||||||
|
|
||||||
|
$_SERVER['SCRIPT_NAME'] = '';
|
||||||
|
|
||||||
|
if ($_SERVER["REQUEST_URI"] === '/upload') {
|
||||||
|
require __DIR__ . '/../src/public/upload.php';
|
||||||
|
} else if ($_SERVER["REQUEST_URI"] === '/reset') {
|
||||||
|
// allow the api tests to reset the database
|
||||||
|
/** @var \Demostf\API\Container $container */
|
||||||
|
$container = require __DIR__ . '/../src/init.php';
|
||||||
|
$connection = $container->getConnection();
|
||||||
|
|
||||||
|
clearDatabase($connection);
|
||||||
|
} else if ($_SERVER["REQUEST_URI"] === '/testuser') {
|
||||||
|
// allow the api tests to create a test user
|
||||||
|
/** @var \Demostf\API\Container $container */
|
||||||
|
$container = require __DIR__ . '/../src/init.php';
|
||||||
|
$connection = $container->getConnection();
|
||||||
|
|
||||||
|
$query = $connection->createQueryBuilder();
|
||||||
|
$query->insert('users')
|
||||||
|
->values([
|
||||||
|
'steamid' => $query->createNamedParameter('steamid1'),
|
||||||
|
'name' => $query->createNamedParameter('nickname1'),
|
||||||
|
'avatar' => $query->createNamedParameter('avatar1'),
|
||||||
|
'token' => $query->createNamedParameter('key1')
|
||||||
|
])->add('orderBy', 'ON CONFLICT DO NOTHING')// hack to append arbitrary string to sql
|
||||||
|
->execute();
|
||||||
|
} else {
|
||||||
|
require __DIR__ . '/../src/public/index.php';
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearDatabase(\Doctrine\DBAL\Connection $connection) {
|
||||||
|
$tables = $connection->getSchemaManager()->listTables();
|
||||||
|
foreach ($tables as $table) {
|
||||||
|
truncateTable($connection, $table->getName());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function truncateTable(\Doctrine\DBAL\Connection $connection, string $tableName) {
|
||||||
|
$sql = sprintf('TRUNCATE TABLE %s;', $tableName);
|
||||||
|
$connection->query($sql);
|
||||||
|
}
|
||||||
2
travis.php.ini
Normal file
2
travis.php.ini
Normal file
|
|
@ -0,0 +1,2 @@
|
||||||
|
post_max_size = 100M
|
||||||
|
upload_max_filesize = 100M
|
||||||
Loading…
Add table
Add a link
Reference in a new issue