Skip to content

Commit

Permalink
Test API using phpunit and Guzzle
Browse files Browse the repository at this point in the history
  • Loading branch information
korelstar committed Mar 29, 2020
1 parent 0033c26 commit d93b429
Show file tree
Hide file tree
Showing 9 changed files with 368 additions and 12 deletions.
10 changes: 6 additions & 4 deletions .github/workflows/lint.yml
Expand Up @@ -3,14 +3,15 @@ on:
push:
branches:
- master
- stable*
pull_request:

jobs:
lint-js:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@v2
- name: Set up Node
uses: actions/setup-node@v1
- name: Set up Stylelint Problem Matcher
Expand All @@ -30,7 +31,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@v2
- name: Setup xmllint
run: |
sudo apt update
Expand All @@ -52,9 +53,9 @@ jobs:
php-version: 7.4
steps:
- name: Checkout
uses: actions/checkout@master
uses: actions/checkout@v2
- name: Set up php${{ matrix.php-versions }}
uses: shivammathur/setup-php@v1
uses: shivammathur/setup-php@v2
with:
php-version: ${{ matrix.php-version }}
- name: Install Dependencies
Expand All @@ -66,3 +67,4 @@ jobs:
uses: korelstar/phplint-problem-matcher@master
- name: lint PHP
run: make -k lint-php

62 changes: 62 additions & 0 deletions .github/workflows/test.yml
@@ -0,0 +1,62 @@
name: Test
on:
push:
branches:
- master
- stable*
pull_request:

jobs:
test-api:
runs-on: ubuntu-latest
steps:
- name: where is PHPUnit?
run: |
which phpunit
find / -name PHPUnit -o -name phpunit
- name: Checkout
uses: actions/checkout@v2
- name: Set up php
uses: shivammathur/setup-php@v2
- name: Install Dependencies
run: composer install --prefer-dist
- name: Prepare MySQL database
run: |
sudo systemctl start mysql
mysql -u root -proot -e "CREATE DATABASE nextcloud;"
mysql -u root -proot -e "CREATE USER 'nextcloud'@'localhost' IDENTIFIED BY '';"
mysql -u root -proot -e "GRANT ALL ON nextcloud.* TO 'nextcloud'@'localhost';"
- name: Prepare Nextcloud server
working-directory: ../
run: |
git clone https://github.com/nextcloud/server.git --recursive --depth 1 -b master server
cp -r notes server/apps/
- name: Setup Nextcloud server
working-directory: ../server/
run: |
php occ maintenance:install --database-name nextcloud --database-user nextcloud --admin-user admin --admin-pass admin --database mysql --database-pass=''
OC_PASS=test php occ user:add --password-from-env --display-name="Test" test
OC_PASS=test php occ user:add --password-from-env --display-name="QuotaTest" quotatest
php occ user:setting quotatest files quota "0"
- name: Setup Notes app
working-directory: ../server/
run: |
php occ app:enable notes
php occ app:check-code notes
- name: Create some reference notes
working-directory: ../
run: |
mkdir -p server/data/test/files/
mkdir -p server/data/quotatest/files/
cp -r notes/tests/reference-notes server/data/test/files/Notes
cp -r notes/tests/reference-notes server/data/quotatest/files/Notes
php server/occ files:scan --all
- name: Start Nextcloud server
working-directory: ../server/
run: "php -S localhost:8080 &"
- name: Test API
run: make test-api
- name: Show nextcloud.log
if: always()
run: "cat ../server/data/nextcloud.log"

10 changes: 3 additions & 7 deletions Makefile
Expand Up @@ -78,14 +78,10 @@ watch-js:
npm run watch

# Testing
test:
npm run test
test: test-api

test-watch:
npm run test:watch

test-coverage:
npm run test:coverage
test-api:
phpunit --bootstrap vendor/autoload.php --testdox tests/api/


# Linting
Expand Down
1 change: 1 addition & 0 deletions composer.json
Expand Up @@ -3,6 +3,7 @@
"christophwurst/nextcloud": "^16.0",
"squizlabs/php_codesniffer": "3.*",
"phan/phan": "^2.0",
"guzzlehttp/guzzle": "^6.5",
"staabm/annotate-pull-request-from-checkstyle": "^1.1.0"
}
}
168 changes: 168 additions & 0 deletions tests/api/APIv02Test.php
@@ -0,0 +1,168 @@
<?php declare(strict_types=1);

require_once 'AbstractAPITest.php';

class APIv02Test extends AbstractAPITest {

public function __construct() {
parent::__construct('v0.2');
}

public function testCheckForReferenceNotes() : array {
$response = $this->http->request('GET', 'notes');
$this->checkResponse($response, 'Get existing notes', 200);
$notes = json_decode($response->getBody()->getContents());
$this->assertNotEmpty($notes, 'List of notes');
return $notes;
}

/** @depends testCheckForReferenceNotes */
public function testGetNotesWithExclude(array $refNotes) : void {
$this->checkGetReferenceNotes($refNotes, 'exclude content', '?exclude=content', false, ['content']);
$this->checkGetReferenceNotes($refNotes, 'exclude content and category', '?exclude=content,category', false, ['content','category']);
}

/** @depends testCheckForReferenceNotes */
public function testGetNotesWithEtag(array $refNotes) : void {
$response1 = $this->http->request('GET', 'notes');
$this->checkResponse($response1, 'Initial response', 200);
$this->assertTrue($response1->hasHeader('ETag'), 'Initial response has ETag header');
$etag = $response1->getHeaderLine('ETag');
$this->assertRegExp('/^"[[:alnum:]]{32}"$/', $etag, 'ETag format');

// Test If-None-Match with ETag
$response2 = $this->http->request('GET', 'notes', [ 'headers' => [ 'If-None-Match' => $etag ] ]);
$this->checkResponse($response2, 'ETag response', 304);
$this->assertEquals('', $response2->getBody(), 'ETag response body');
}

/** @depends testCheckForReferenceNotes */
public function testGetNotesWithPruneBefore(array $refNotes) : void {
sleep(1); // wait for 'Last-Modified' to be >= Last-change + 1
$response1 = $this->http->request('GET', 'notes');
$this->checkResponse($response1, 'Initial response', 200);
$this->assertTrue($response1->hasHeader('Last-Modified'), 'Initial response has Last-Modified header');
$lastModified = $response1->getHeaderLine('Last-Modified');
$dt = \DateTime::createFromFormat(\DateTime::RFC2822, $lastModified);
$this->assertInstanceOf(\DateTime::class, $dt);

$this->checkGetReferenceNotes($refNotes, 'pruneBefore with Last-Modified', '?pruneBefore='.$dt->getTimestamp(), true);
$this->checkGetReferenceNotes($refNotes, 'pruneBefore with 1', '?pruneBefore=1', false);
$this->checkGetReferenceNotes($refNotes, 'pruneBefore with PHP_INT_MAX (32bit)', '?pruneBefore=2147483647', true); // 2038-01-19 03:14:07
$this->checkGetReferenceNotes($refNotes, 'pruneBefore with PHP_INT_MAX (64bit)', '?pruneBefore=9223372036854775807', true);
}

/** @depends testCheckForReferenceNotes */
public function testCreateNotes(array $refNotes) : array {
$this->checkGetReferenceNotes($refNotes, 'Pre-condition');
$testNotes = [];
$testNotes[] = $this->createNote((object)[
'title' => 'This is not used',
'content' => '# *First* test/ note'.PHP_EOL.'This is some body content with some data.',
'favorite' => true,
'category' => 'Test/../New Category',
'modified' => mktime(8, 14, 30, 10, 2, 2020),
], (object)[
'title' => 'First test note',
'category' => 'Test/New Category',
]);
$testNotes[] = $this->createNote((object)[
'content' => 'Note with Defaults'.PHP_EOL.'This is some body content with some data.',
], (object)[
'title' => 'Note with Defaults',
'favorite' => false,
'category' => '',
'modified' => time(),
]);
$this->checkGetReferenceNotes(array_merge($refNotes, $testNotes), 'After creating notes');
return $testNotes;
}

/**
* @depends testCheckForReferenceNotes
* @depends testCreateNotes
*/
public function testGetSingleNote(array $refNotes, array $testNotes) : void {
foreach($testNotes as $testNote) {
$response = $this->http->request('GET', 'notes/'.$testNote->id);
$this->checkResponse($response, 'Get note '.$testNote->title, 200);
$note = json_decode($response->getBody()->getContents());
$this->checkReferenceNote($testNote, $note, 'Get single note');
}
// test non-existing note
$response = $this->http->request('GET', 'notes/1');
$this->assertEquals(404, $response->getStatusCode());
}


/**
* @depends testCheckForReferenceNotes
* @depends testCreateNotes
*/
public function testUpdateNotes(array $refNotes, array $testNotes) : array {
$this->checkGetReferenceNotes(array_merge($refNotes, $testNotes), 'Pre-condition');
$note = $testNotes[0];
// test update note with all attributes
$this->updateNote($note, (object)[
'title' => 'This is not used',
'content' => '# *First* edited/ note'.PHP_EOL.'This is some body content with some data.',
'favorite' => false,
'category' => 'Test/Another Category',
'modified' => mktime(11, 46, 23, 4, 3, 2020),
], (object)[
'title' => 'First edited note',
]);
// test update note with single attributes
/* TODO this doesn't work (content is null)
$this->updateNote($note, (object)[
'category' => 'Test/Third Category',
], (object)[]);
*/
$this->updateNote($note, (object)[
'favorite' => true,
], (object)[]);
$this->updateNote($note, (object)[
'content' => '# First multi edited note'.PHP_EOL.'This is some body content with some data.',
], (object)[
'title' => 'First multi edited note',
'modified' => time(),
]);
$this->checkGetReferenceNotes(array_merge($refNotes, $testNotes), 'After updating notes');
return $testNotes;
}

/**
* @depends testCheckForReferenceNotes
* @depends testUpdateNotes
*/
public function testDeleteNotes(array $refNotes, array $testNotes) : void {
$this->checkGetReferenceNotes(array_merge($refNotes, $testNotes), 'Pre-condition');
foreach($testNotes as $note) {
$response = $this->http->request('DELETE', 'notes/'.$note->id);
$this->checkResponse($response, 'Delete note '.$note->title, 200);
}
// test non-existing note
$response = $this->http->request('DELETE', 'notes/1');
$this->checkResponse($response, 'Delete non-existing note', 404);
$this->checkGetReferenceNotes($refNotes, 'After deletion');
}

public function testInsuficientStorage() {
$auth = ['quotatest', 'test'];
// get notes must still work
$response = $this->http->request('GET', 'notes', [ 'auth' => $auth ]);
$this->checkResponse($response, 'Get existing notes', 200);
$notes = json_decode($response->getBody()->getContents());
$this->assertNotEmpty($notes, 'List of notes');
$note = $notes[0]; // @phan-suppress-current-line PhanTypeArraySuspiciousNullable
$request = (object)[ 'content' => 'New test content' ];
// update will fail
$response1 = $this->http->request('PUT', 'notes/'.$note->id, [ 'auth' => $auth, 'json' => $request]);
$this->assertEquals(507, $response1->getStatusCode());
// craete will fail
$response2 = $this->http->request('POST', 'notes', [ 'auth' => $auth, 'json' => $request]);
$this->assertEquals(507, $response2->getStatusCode());
}

// TODO Test settings (switch to another notes folder)
}

0 comments on commit d93b429

Please sign in to comment.