diff --git a/.env.travis b/.env.travis new file mode 100644 index 0000000..71d1656 --- /dev/null +++ b/.env.travis @@ -0,0 +1,46 @@ +APP_NAME='RestApi' +APP_ENV=testing +APP_KEY=base64:CS3VLz1BGmBBZBTSCQLShZ5NaM+A1MZukOro1fT4hRU= +APP_DEBUG=true +APP_URL=http://localhost:8080 + +LOG_CHANNEL=stack + +DB_CONNECTION=testing +DB_TEST_DATABASE=testing +DB_TEST_USERNAME=root +DB_TEST_PASSWORD= + +QUEUE_DRIVER=sync +BROADCAST_DRIVER=log +CACHE_DRIVER=array +QUEUE_CONNECTION=sync +SESSION_DRIVER=array +SESSION_LIFETIME=120 + +REDIS_HOST=127.0.0.1 +REDIS_PASSWORD=null +REDIS_PORT=6379 + +MAIL_DRIVER=smtp +MAIL_HOST=smtp.mailtrap.io +MAIL_PORT=2525 +MAIL_USERNAME=null +MAIL_PASSWORD=null +MAIL_ENCRYPTION=null + +AWS_ACCESS_KEY_ID= +AWS_SECRET_ACCESS_KEY= +AWS_DEFAULT_REGION=us-east-1 +AWS_BUCKET= + +PUSHER_APP_ID= +PUSHER_APP_KEY= +PUSHER_APP_SECRET= +PUSHER_APP_CLUSTER=mt1 + +MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}" +MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}" + +TELESCOPE_ENABLED=false +TELESCOPE_USERS=1,2 diff --git a/.travis.yml b/.travis.yml index 4324dc0..ab012fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,5 +1,15 @@ language: php + +services: + - mysql before_script: - - composer install - - cp .env.example .env + - cp .env.travis .env + - mysql -e 'create database testing;' + - composer self-update + - composer install --no-interaction - php artisan key:generate + - php artisan migrate --seed + - php artisan passport:install +script: + - vendor/bin/phpunit + diff --git a/app/Http/Controllers/AuthController.php b/app/Http/Controllers/AuthController.php index f2bbdca..e6de840 100644 --- a/app/Http/Controllers/AuthController.php +++ b/app/Http/Controllers/AuthController.php @@ -21,7 +21,7 @@ public function login() $token = $user->createToken('RestApi')->accessToken; return $this->sendResponse($token); } else { - return $this->sendError('Unauthorized.', [], 401); + return $this->sendError('Unauthorized.', [], 400); } } @@ -34,7 +34,7 @@ public function register(Request $request) 'c_password' => 'required|same:password', ]); if ($validator->fails()) { - return $this->sendError('Validation error.', $validator->errors(), 401); + return $this->sendError('Validation error.', $validator->errors(), 400); } $input = $request->all(); $input['password'] = bcrypt($input['password']); diff --git a/app/Http/Middleware/Authenticate.php b/app/Http/Middleware/Authenticate.php index 48251cb..f546f24 100644 --- a/app/Http/Middleware/Authenticate.php +++ b/app/Http/Middleware/Authenticate.php @@ -11,7 +11,7 @@ class Authenticate extends Middleware public function handle($request, Closure $next, ...$guards) { if ($this->authenticate($request, $guards) === 'authentication_failed') { - return response()->json(['success' => false, 'message' => 'Unauthorized'], 400); + return response()->json(['success' => false, 'message' => 'Unauthorized'], 401); } return $next($request); } diff --git a/composer.json b/composer.json index 7e2e2ba..6f69c12 100644 --- a/composer.json +++ b/composer.json @@ -50,6 +50,9 @@ "minimum-stability": "dev", "prefer-stable": true, "scripts": { + "tests" : [ + "vendor/bin/phpunit" + ], "post-autoload-dump": [ "Illuminate\\Foundation\\ComposerScripts::postAutoloadDump", "@php artisan package:discover --ansi" diff --git a/config/database.php b/config/database.php index 921769c..c6e55f6 100644 --- a/config/database.php +++ b/config/database.php @@ -38,7 +38,7 @@ 'sqlite' => [ 'driver' => 'sqlite', 'url' => env('DATABASE_URL'), - 'database' => env('DB_DATABASE', database_path('database.sqlite')), + 'database' => ':memory:', 'prefix' => '', 'foreign_key_constraints' => env('DB_FOREIGN_KEYS', true), ], @@ -63,6 +63,18 @@ ]) : [], ], + 'testing' => [ + 'driver' => 'mysql', + 'host' => env('DB_TEST_HOST', 'localhost'), + 'database' => env('DB_TEST_DATABASE', 'testing_db'), + 'username' => env('DB_TEST_USERNAME', 'root'), + 'password' => env('DB_TEST_PASSWORD', 'secret'), + 'charset' => 'utf8', + 'collation' => 'utf8_unicode_ci', + 'prefix' => '', + 'strict' => false, + ], + 'pgsql' => [ 'driver' => 'pgsql', 'url' => env('DATABASE_URL'), @@ -78,19 +90,6 @@ 'sslmode' => 'prefer', ], - 'sqlsrv' => [ - 'driver' => 'sqlsrv', - 'url' => env('DATABASE_URL'), - 'host' => env('DB_HOST', 'localhost'), - 'port' => env('DB_PORT', '1433'), - 'database' => env('DB_DATABASE', 'forge'), - 'username' => env('DB_USERNAME', 'forge'), - 'password' => env('DB_PASSWORD', ''), - 'charset' => 'utf8', - 'prefix' => '', - 'prefix_indexes' => true, - ], - ], /* diff --git a/database/factories/UserFactory.php b/database/factories/UserFactory.php index 5e516ce..68dfe4d 100644 --- a/database/factories/UserFactory.php +++ b/database/factories/UserFactory.php @@ -1,20 +1,11 @@ define(User::class, function (Faker $faker) { return [ diff --git a/database/seeds/DatabaseSeeder.php b/database/seeds/DatabaseSeeder.php index 67c20d1..2be35e9 100644 --- a/database/seeds/DatabaseSeeder.php +++ b/database/seeds/DatabaseSeeder.php @@ -11,6 +11,7 @@ class DatabaseSeeder extends Seeder */ public function run() { + $this->call(UserSeeder::class); $this->call(ClientSeeder::class); } } diff --git a/database/seeds/UserSeeder.php b/database/seeds/UserSeeder.php new file mode 100644 index 0000000..89a5db2 --- /dev/null +++ b/database/seeds/UserSeeder.php @@ -0,0 +1,17 @@ +create(); + } +} diff --git a/phpunit.xml b/phpunit.xml index da4add3..1c34afa 100644 --- a/phpunit.xml +++ b/phpunit.xml @@ -8,18 +8,15 @@ convertWarningsToExceptions="true" processIsolation="false" stopOnFailure="false"> - - - ./tests/Unit - - - ./tests/Feature + + + ./tests/ - - ./app + + app/ @@ -27,7 +24,9 @@ + + diff --git a/tests/CreatesApplication.php b/tests/CreatesApplication.php index 547152f..18f9825 100644 --- a/tests/CreatesApplication.php +++ b/tests/CreatesApplication.php @@ -3,13 +3,14 @@ namespace Tests; use Illuminate\Contracts\Console\Kernel; +use Illuminate\Foundation\Application; trait CreatesApplication { /** * Creates the application. * - * @return \Illuminate\Foundation\Application + * @return Application */ public function createApplication() { diff --git a/tests/Feature/ClientTest.php b/tests/Feature/ClientTest.php new file mode 100644 index 0000000..42f8d6b --- /dev/null +++ b/tests/Feature/ClientTest.php @@ -0,0 +1,142 @@ +create(); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + $payload = [ + 'name' => 'Szczepańska', + 'vat_number' => '6529411372', + 'street' => 'Sądowa 15A/28', + 'city' => 'Bogatynia', + 'post_code' => '22-090', + 'email' => 'jbaranowska@kwiatkowska.info' + ]; + + $this->json('POST', '/api/clients', $payload, $headers) + ->assertStatus(200) + ->assertJson([ + 'success' => true, + 'data' => [ + 'id' => 11, + 'name' => 'Szczepańska', + 'vat_number' => '6529411372', + 'street' => 'Sądowa 15A/28', + 'city' => 'Bogatynia', + 'post_code' => '22-090', + 'email' => 'jbaranowska@kwiatkowska.info' + ], + 'message' => 'Client created successfully.' + ]); + } + + public function testsClientsAreUpdatedCorrectly() + { + $user = factory(User::class)->create(); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + $article = factory(Client::class)->create([ + 'name' => 'Szczepańska', + 'vat_number' => '6529411372', + 'street' => 'Sądowa 15A/28', + 'city' => 'Bogatynia', + 'post_code' => '22-090', + 'email' => 'jszczepan@kwiatkowska.info' + ]); + + $payload = [ + 'name' => 'Barańska', + 'vat_number' => '1234567890', + 'street' => 'Rolna 12', + 'city' => 'Warszawa', + 'post_code' => '01-000', + 'email' => 'pbaranska@mail.com' + ]; + + $response = $this->json('PUT', '/api/clients/'.$article->id, $payload, $headers) + ->assertStatus(200) + ->assertJson([ + 'success' => true, + 'data' => [ + 'id' => 11, + 'name' => 'Barańska', + 'vat_number' => '1234567890', + 'street' => 'Rolna 12', + 'city' => 'Warszawa', + 'post_code' => '01-000', + 'email' => 'pbaranska@mail.com' + ], + 'message' => 'Client updated successfully.' + ]); + } + + public function testsClientsAreDeletedCorrectly() + { + $user = factory(User::class)->create(); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + $article = factory(Client::class)->create([ + 'name' => 'Szczepańska', + 'vat_number' => '6529411372', + 'street' => 'Sądowa 15A/28', + 'city' => 'Bogatynia', + 'post_code' => '22-090', + 'email' => 'jszczepan@kwiatkowska.info' + ]); + + $this->json('DELETE', '/api/clients/'.$article->id, [], $headers) + ->assertStatus(200); + } + + public function testClientsAreListedCorrectly() + { + factory(Client::class)->create([ + 'name' => 'Szczepańska', + 'vat_number' => '6529411372', + 'street' => 'Sądowa 15A/28', + 'city' => 'Bogatynia', + 'post_code' => '22-090', + 'email' => 'jszczepan@kwiatkowska.info' + ]); + + factory(Client::class)->create([ + 'name' => 'Barańska', + 'vat_number' => '1234567890', + 'street' => 'Rolna 12', + 'city' => 'Warszawa', + 'post_code' => '01-000', + 'email' => 'pbaranska@mail.com' + ]); + + $user = factory(User::class)->create(); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + + $response = $this->json('GET', '/api/clients', [], $headers) + ->assertStatus(200) + ->assertJsonStructure([ + 'success', + 'data' => [ + '*' => [ + 'id', + 'name', + 'vat_number', + 'street', + 'city', + 'post_code', + 'email' + ] + ], + 'message' + ]); + } +} diff --git a/tests/Feature/LoginTest.php b/tests/Feature/LoginTest.php new file mode 100644 index 0000000..a225315 --- /dev/null +++ b/tests/Feature/LoginTest.php @@ -0,0 +1,38 @@ +json('POST', 'api/login') + ->assertStatus(400) + ->assertJson([ + 'success' => false, + 'message' => 'Unauthorized.', + ]); + } + + + public function testUserLoginsSuccessfully() + { + $user = factory(User::class)->create([ + 'email' => 'admin@mail.com', + 'password' => bcrypt('password'), + ]); + + $payload = ['email' => 'admin@mail.com', 'password' => 'password']; + + $this->json('POST', 'api/login', $payload) + ->assertStatus(200) + ->assertJsonStructure([ + 'success', + 'data', + ]); + + } +} diff --git a/tests/Feature/LogoutTest.php b/tests/Feature/LogoutTest.php new file mode 100644 index 0000000..2e990bf --- /dev/null +++ b/tests/Feature/LogoutTest.php @@ -0,0 +1,32 @@ +create(['email' => 'admin@mail.com']); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + + $this->json('get', '/api/clients', [], $headers)->assertStatus(200); + $this->json('get', '/api/logout', [], $headers)->assertStatus(200); + } + + public function testUserWithNullToken() + { + // Simulating login + $user = factory(User::class)->create(['email' => 'admin@mail.com']); + $token = $user->createToken('RestApi')->accessToken; + $headers = ['Authorization' => "Bearer $token"]; + + // Simulating logout + $user->authAcessToken()->delete(); + + $this->json('get', '/api/clients', [], $headers)->assertStatus(401); + } +} diff --git a/tests/Feature/RegisterTest.php b/tests/Feature/RegisterTest.php new file mode 100644 index 0000000..0aaf048 --- /dev/null +++ b/tests/Feature/RegisterTest.php @@ -0,0 +1,60 @@ + 'Admin', + 'email' => 'admin@mail.com', + 'password' => 'password', + 'c_password' => 'password', + ]; + + $this->json('post', '/api/register', $payload) + ->assertStatus(200) + ->assertJsonStructure([ + 'success', + 'data', + ]);; + } + + public function testsRequiresPasswordEmailAndName() + { + $this->json('post', '/api/register') + ->assertStatus(400) + ->assertJson([ + 'success' => false, + 'message' => 'Validation error.', + 'data' => [ + 'name' => ['The name field is required.'], + 'email' => ['The email field is required.'], + 'password' => ['The password field is required.'], + 'c_password' => ['The c password field is required.'] + ] + ]); + } + + public function testsRequirePasswordConfirmation() + { + $payload = [ + 'name' => 'Admin', + 'email' => 'admin@mail.com', + 'password' => 'password', + ]; + + $this->json('post', '/api/register', $payload) + ->assertStatus(400) + ->assertJson([ + 'success' => false, + 'message' => 'Validation error.', + 'data' => [ + 'c_password' => ['The c password field is required.'] + ] + ]); + } +} diff --git a/tests/TestCase.php b/tests/TestCase.php index 2932d4a..bb45dc0 100644 --- a/tests/TestCase.php +++ b/tests/TestCase.php @@ -3,8 +3,23 @@ namespace Tests; use Illuminate\Foundation\Testing\TestCase as BaseTestCase; +use Illuminate\Foundation\Testing\DatabaseMigrations; +use Illuminate\Support\Facades\Artisan; +use Illuminate\Support\Facades\DB; +use Laravel\Passport\ClientRepository; abstract class TestCase extends BaseTestCase { - use CreatesApplication; + use CreatesApplication, DatabaseMigrations; + + public function setUp(): void + { + parent::setUp(); + Artisan::call('db:seed'); + + $clientRepository = new ClientRepository(); + $client = $clientRepository->createPersonalAccessClient(null, 'RestApi Personal Access Client', env('APP_URL')); + + DB::table('oauth_personal_access_clients')->insert(['client_id' => $client->id,]); + } }