add: add individuals api tests and extend openapi spec

pull/80/head
Luciano Righetti 2022-01-11 17:17:04 +01:00
parent f774f68ede
commit 5906f6d2c7
16 changed files with 537 additions and 20 deletions

View File

@ -10,17 +10,11 @@ class IndividualsFixture extends TestFixture
{
public $connection = 'test';
// Admin individual
public const INDIVIDUAL_ADMIN_ID = 1;
// Sync individual
public const INDIVIDUAL_SYNC_ID = 2;
// Org Admin individual
public const INDIVIDUAL_ORG_ADMIN_ID = 3;
// Regular User individual
public const INDIVIDUAL_REGULAR_USER_ID = 4;
public const INDIVIDUAL_A_ID = 5;
public function init(): void
{
@ -66,6 +60,16 @@ class IndividualsFixture extends TestFixture
'position' => 'user',
'created' => $faker->dateTime()->getTimestamp(),
'modified' => $faker->dateTime()->getTimestamp()
],
[
'id' => self::INDIVIDUAL_A_ID,
'uuid' => $faker->uuid(),
'email' => $faker->email(),
'first_name' => $faker->firstName,
'last_name' => $faker->lastName,
'position' => 'user',
'created' => $faker->dateTime()->getTimestamp(),
'modified' => $faker->dateTime()->getTimestamp()
]
];
parent::init();

View File

@ -11,10 +11,7 @@ class OrganisationsFixture extends TestFixture
{
public $connection = 'test';
// Organisation A
public const ORGANISATION_A_ID = 1;
// Organisation B
public const ORGANISATION_B_ID = 2;
public function init(): void

View File

@ -0,0 +1,71 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Helper\ApiTestTrait;
class AddIndividualApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/individuals/add';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function setUp(): void
{
parent::setUp();
$this->initializeValidator(APP . '../webroot/docs/openapi.yaml');
}
public function testAddIndividual(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->post(
self::ENDPOINT,
[
'email' => 'john@example.com',
'first_name' => 'John',
'last_name' => 'Doe',
'position' => 'Security Analyst'
]
);
$this->assertResponseOk();
$this->assertResponseContains('"email": "john@example.com"');
$this->assertDbRecordExists('Individuals', ['email' => 'john@example.com']);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
public function testAddUserNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$this->post(
self::ENDPOINT,
[
'email' => 'john@example.com',
'first_name' => 'John',
'last_name' => 'Doe',
'position' => 'Security Analyst'
]
);
$this->assertResponseCode(405);
$this->assertDbRecordNotExists('Individuals', ['email' => 'john@example.com']);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
}

View File

@ -0,0 +1,59 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\IndividualsFixture;
use App\Test\Helper\ApiTestTrait;
class DeleteIndividualApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/individuals/delete';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function setUp(): void
{
parent::setUp();
$this->initializeValidator(APP . '../webroot/docs/openapi.yaml');
}
public function testDeleteIndividual(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, IndividualsFixture::INDIVIDUAL_A_ID);
$this->delete($url);
$this->assertResponseOk();
$this->assertDbRecordNotExists('Individuals', ['id' => IndividualsFixture::INDIVIDUAL_A_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteIndividualNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, IndividualsFixture::INDIVIDUAL_ADMIN_ID);
$this->delete($url);
$this->assertResponseCode(405);
$this->assertDbRecordExists('Individuals', ['id' => IndividualsFixture::INDIVIDUAL_ADMIN_ID]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'delete');
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
}

View File

@ -0,0 +1,73 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\IndividualsFixture;
use App\Test\Helper\ApiTestTrait;
class EditIndividualApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/individuals/edit';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function setUp(): void
{
parent::setUp();
$this->initializeValidator(APP . '../webroot/docs/openapi.yaml');
}
public function testEditIndividualAsAdmin(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, IndividualsFixture::INDIVIDUAL_REGULAR_USER_ID);
$this->put(
$url,
[
'email' => 'foo@bar.com',
]
);
$this->assertResponseOk();
$this->assertDbRecordExists('Individuals', [
'id' => IndividualsFixture::INDIVIDUAL_REGULAR_USER_ID,
'email' => 'foo@bar.com'
]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'put');
}
public function testEditAnyIndividualNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, IndividualsFixture::INDIVIDUAL_ADMIN_ID);
$this->put(
$url,
[
'email' => 'foo@bar.com',
]
);
$this->assertResponseCode(405);
$this->assertDbRecordNotExists('Individuals', [
'id' => IndividualsFixture::INDIVIDUAL_ADMIN_ID,
'email' => 'foo@bar.com'
]);
//TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url, 'put');
}
}

View File

@ -0,0 +1,44 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\IndividualsFixture;
use App\Test\Helper\ApiTestTrait;
class IndexIndividualsApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/individuals/index';
protected $fixtures = [
'app.Organisations',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function setUp(): void
{
parent::setUp();
$this->initializeValidator(APP . '../webroot/docs/openapi.yaml');
}
public function testIndexIndividuals(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$this->get(self::ENDPOINT);
$this->assertResponseOk();
$this->assertResponseContains(sprintf('"id": %d', IndividualsFixture::INDIVIDUAL_ADMIN_ID));
// TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT);
}
}

View File

@ -57,7 +57,7 @@ class AddOrganisationApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
public function testAddOrganisationNotAllowedToRegularUser(): void
public function testAddOrganisationNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);

View File

@ -44,7 +44,7 @@ class DeleteOrganisationApiTest extends TestCase
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteOrganisationNotAllowedToRegularUser(): void
public function testDeleteOrganisationNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, OrganisationsFixture::ORGANISATION_B_ID);

View File

@ -55,7 +55,7 @@ class EditOrganisationApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec($url, 'put');
}
public function testEditOrganisationNotAllowedToRegularUser(): void
public function testEditOrganisationNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);

View File

@ -59,7 +59,7 @@ class TagOrganisationApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec($url, 'post');
}
public function testTagOrganisationNotAllowedToRegularUser(): void
public function testTagOrganisationNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);

View File

@ -59,7 +59,7 @@ class UntagOrganisationApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec($url, 'post');
}
public function testUntagOrganisationNotAllowedToRegularUser(): void
public function testUntagOrganisationNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);

View File

@ -0,0 +1,48 @@
<?php
declare(strict_types=1);
namespace App\Test\TestCase\Api\Users;
use Cake\TestSuite\IntegrationTestTrait;
use Cake\TestSuite\TestCase;
use App\Test\Fixture\AuthKeysFixture;
use App\Test\Fixture\OrganisationsFixture;
use App\Test\Fixture\UsersFixture;
use App\Test\Helper\ApiTestTrait;
class ViewOrganisationApiTest extends TestCase
{
use IntegrationTestTrait;
use ApiTestTrait;
protected const ENDPOINT = '/api/v1/organisations/view';
protected $fixtures = [
'app.TagsTags',
'app.Organisations',
'app.TagsTaggeds',
'app.Individuals',
'app.Roles',
'app.Users',
'app.AuthKeys'
];
public function setUp(): void
{
parent::setUp();
$this->initializeValidator(APP . '../webroot/docs/openapi.yaml');
}
public function testViewOrganisationById(): void
{
$this->setAuthToken(AuthKeysFixture::ADMIN_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, OrganisationsFixture::ORGANISATION_A_ID);
$this->get($url);
$this->assertResponseOk();
$this->assertResponseContains('"name": "Organisation A"');
// TODO: $this->assertRequestMatchesOpenApiSpec();
$this->assertResponseMatchesOpenApiSpec($url);
}
}

View File

@ -55,7 +55,7 @@ class AddUserApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec(self::ENDPOINT, 'post');
}
public function testAddUserNotAllowedToRegularUser(): void
public function testAddUserNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$this->post(

View File

@ -46,7 +46,7 @@ class DeleteUserApiTest extends TestCase
$this->addWarning('TODO: CRUDComponent::delete() sets some view variables, does not take into account `isRest()`, fix it.');
}
public function testDeleteUserNotAllowedToRegularUser(): void
public function testDeleteUserNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$url = sprintf('%s/%d', self::ENDPOINT, UsersFixture::USER_ORG_ADMIN_ID);

View File

@ -53,7 +53,7 @@ class EditUserApiTest extends TestCase
$this->assertResponseMatchesOpenApiSpec($url, 'put');
}
public function testEditRoleNotAllowedToRegularUser(): void
public function testEditRoleNotAllowedAsRegularUser(): void
{
$this->setAuthToken(AuthKeysFixture::REGULAR_USER_API_KEY);
$this->put(

View File

@ -9,6 +9,8 @@ servers:
- url: https://cerebrate.local
tags:
- name: Individuals
description: "Individuals are natural persons. They are meant to describe the basic information about an individual that may or may not be a user of this community. Users in genral require an individual object to identify the person behind them - however, no user account is required to store information about an individual. Individuals can have affiliations to organisations and broods as well as cryptographic keys, using which their messages can be verified and which can be used to securely contact them."
- name: Users
description: "Users enrolled in this Cerebrate instance."
- name: Organisations
@ -17,12 +19,88 @@ tags:
description: "Tags can be attached to entity to quickly classify them, allowing further filtering and searches."
paths:
/api/v1/individuals/index:
get:
summary: "Get individuals list"
operationId: getIndividuals
tags:
- Individuals
parameters:
- $ref: "#/components/parameters/quickFilter"
responses:
"200":
$ref: "#/components/responses/IndividualListResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/individuals/add:
post:
summary: "Add individual"
operationId: addIndividual
tags:
- Users
requestBody:
$ref: "#/components/requestBodies/AddIndividualRequest"
responses:
"200":
$ref: "#/components/responses/IndividualResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/individuals/edit/{individualId}:
put:
summary: "Edit individual"
operationId: editIndividual
tags:
- Individuals
parameters:
- $ref: "#/components/parameters/individualId"
requestBody:
$ref: "#/components/requestBodies/EditIndividualRequest"
responses:
"200":
$ref: "#/components/responses/IndividualResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/individuals/delete/{individualId}:
delete:
summary: "Delete individual by ID"
operationId: deleteIndividualById
tags:
- Individuals
parameters:
- $ref: "#/components/parameters/individualId"
responses:
"200":
$ref: "#/components/responses/IndividualResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/users/index:
get:
summary: "Get users list"
operationId: getUsers
tags:
- Users
parameters:
- $ref: "#/components/parameters/quickFilter"
responses:
"200":
$ref: "#/components/responses/UserListResponse"
@ -185,6 +263,8 @@ paths:
operationId: getOrganisations
tags:
- Organisations
parameters:
- $ref: "#/components/parameters/quickFilter"
responses:
"200":
$ref: "#/components/responses/OrganisationListResponse"
@ -195,6 +275,24 @@ paths:
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/organisations/view/{organisationId}:
get:
summary: "View organisation by ID"
operationId: getOrganisationById
tags:
- Organisations
parameters:
- $ref: "#/components/parameters/organisationId"
responses:
"200":
$ref: "#/components/responses/OrganisationResponse"
"403":
$ref: "#/components/responses/UnauthorizedApiErrorResponse"
"405":
$ref: "#/components/responses/MethodNotAllowedApiErrorResponse"
default:
$ref: "#/components/responses/ApiErrorResponse"
/api/v1/organisations/delete/{organisationId}:
delete:
summary: "Delete organisation by ID"
@ -259,6 +357,8 @@ paths:
operationId: getTags
tags:
- Tags
parameters:
- $ref: "#/components/parameters/quickFilter"
responses:
"200":
$ref: "#/components/responses/TagListResponse"
@ -288,11 +388,64 @@ components:
format: datetime
example: "2022-01-05T11:19:26+00:00"
Email:
type: string
format: email
example: "user@example.com"
# Individuals
IndividualFirstName:
type: string
example: "John"
IndividualLastName:
type: string
example: "Doe"
IndividualFullName:
type: string
example: "John Doe"
IndividualPosition:
type: string
example: "Security Analyst"
Individual:
type: object
properties:
id:
$ref: "#/components/schemas/ID"
uuid:
$ref: "#/components/schemas/UUID"
email:
$ref: "#/components/schemas/Email"
first_name:
$ref: "#/components/schemas/IndividualFirstName"
last_name:
$ref: "#/components/schemas/IndividualLastName"
full_name:
$ref: "#/components/schemas/IndividualFullName"
position:
$ref: "#/components/schemas/IndividualPosition"
tags:
$ref: "#/components/schemas/TagList"
aligments:
$ref: "#/components/schemas/AligmentList"
created:
$ref: "#/components/schemas/DateTime"
modified:
$ref: "#/components/schemas/DateTime"
# Users
Username:
type: string
example: "admin"
IndividualList:
type: array
items:
$ref: "#/components/schemas/Individual"
User:
type: object
properties:
@ -320,8 +473,6 @@ components:
items:
$ref: "#/components/schemas/User"
# Individuals
# Organisations
OrganisationName:
type: string
@ -533,6 +684,14 @@ components:
example: 404
parameters:
individualId:
name: userId
in: path
description: "Numeric ID of the User"
required: true
schema:
$ref: "#/components/schemas/ID"
userId:
name: userId
in: path
@ -549,6 +708,14 @@ components:
schema:
$ref: "#/components/schemas/ID"
quickFilter:
name: quickFilter
in: query
description: "Quick filter used to match multiple attributes such as name, description, emails, etc."
schema:
type: string
example: "user@example.com"
securitySchemes:
ApiKeyAuth:
type: apiKey
@ -560,6 +727,43 @@ components:
Authorization: YOUR_API_KEY
requestBodies:
# Individuals
AddIndividualRequest:
required: true
content:
application/json:
schema:
type: object
properties:
uuid:
$ref: "#/components/schemas/UUID"
email:
$ref: "#/components/schemas/IndividualLastName"
first_name:
$ref: "#/components/schemas/IndividualFirstName"
last_name:
type: boolean
position:
$ref: "#/components/schemas/IndividualPosition"
EditIndividualRequest:
required: true
content:
application/json:
schema:
type: object
properties:
uuid:
$ref: "#/components/schemas/UUID"
email:
$ref: "#/components/schemas/IndividualLastName"
first_name:
$ref: "#/components/schemas/IndividualFirstName"
last_name:
type: boolean
position:
$ref: "#/components/schemas/IndividualPosition"
# Users
AddUserRequest:
required: true
@ -588,6 +792,8 @@ components:
schema:
type: object
properties:
id:
$ref: "#/components/schemas/ID"
individual_id:
$ref: "#/components/schemas/ID"
organisation_id:
@ -671,6 +877,21 @@ components:
example: '["red"]'
responses:
# Individuals
IndividualResponse:
description: "Individual response"
content:
application/json:
schema:
$ref: "#/components/schemas/Individual"
IndividualListResponse:
description: "Individuals list response"
content:
application/json:
schema:
$ref: "#/components/schemas/IndividualList"
# Users
UserResponse:
description: "User response"