'POST' ]; }); describe('[check] config file', function(){ it('pass when config file exists', function(){ expect( file_exists(Request::config_path()) )->toBeTruthy(); }); it('pass when we can read the config file', function(){ expect( @file_get_contents(Request::config_path()) )->not->toBe(false); }); it('pass when the config file is valid json', function(){ $read = @file_get_contents(Request::config_path()); expect($read)->not->toBe(false); expect( json_decode($read, true) )->not->toBeNull(); expect( json_decode($read, true) )->toBeA('array'); }); }); describe('__construct(@path, @params)', function(){ it('fail if no HTTP_METHOD', function(){ unset($_SERVER); $req = new Request('someString'); expect($req->error->get())->toBe(Err::UnknownHttpMethod); }); context('with argument combinations', function(){ it('fail if @path is missing', function(){ $req = new Request(); expect($req->error->get())->toBe(Err::MissingPath); }); it('fail if @path is not a string', function(){ expect('is_string')->toBeCalled(); $req = new Request(1); expect($req->error->get())->toBe(Err::WrongPathModule); }); it('pass if @params is an array', function(){ unset($_SERVER); expect('is_array')->toBeCalled(); $req = new Request('someString', ['a', 'b']); expect($req->error->get())->toBe(Err::UnknownHttpMethod); }); it('pass if @params is not an array (default: [])', function(){ unset($_SERVER); expect('is_array')->toBeCalled(); $types = [true, false, null, 1, 2.3, -1.2, 0, 'blabla']; foreach($types as $type){ $req = new Request('someString', true); expect($req->error->get())->toBe(Err::UnknownHttpMethod); } }); }); context('with config errors', function(){ it('fail if the config file doesn\'t exist', function(){ // Request::config_path -> 'aa' allow(Request::class)->toReceive('::config_path')->andReturn('invalid_fname'); expect(Request::config_path())->toBe('invalid_fname'); // file_exists -> false allow('file_exists')->toBeCalled()->andReturn(false); expect('file_exists')->toBeCalled(); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::UnreachableResource); }); it('fail if the config file cannot be read', function(){ // file_get_contents -> false allow('file_get_contents')->toBeCalled()->andReturn(false); expect('file_get_contents')->toBeCalled(); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::UnreachableResource); }); it('fail if the json format is not valid', function(){ // file_exists -> true allow('file_exists')->toBeCalled()->andReturn(true); expect('file_exists')->toBeCalled(); // file_get_contents -> false allow('file_get_contents')->toBeCalled()->andReturn('{incorrect_json'); expect('file_get_contents')->toBeCalled(); // json_decode -> called expect('json_decode')->toBeCalled(); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::ParsingFailed); }); }); context('with checks errors', function(){ it('fail with checkPath() fails', function(){ allow(Request::class)->toReceive('checkPath')->andRun(function(){ $this->error->set(Err::UnknownError); return false; }); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::UnknownError); }); it('fail with checkPermission() fails', function(){ // bypass checkPath(); allow(Request::class)->toReceive('checkPath')->andReturn(true); allow(Request::class)->toReceive('checkPermission')->andRun(function(){ $this->error->set(Err::UnknownError); return false; }); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::UnknownError); }); it('fail with checkParams() fails', function(){ // bypass checkPath(); + checkPermission(); allow(Request::class)->toReceive('checkPath')->andReturn(true); allow(Request::class)->toReceive('checkPermission')->andReturn(true); allow(Request::class)->toReceive('checkParams')->andRun(function(){ $this->error->set(Err::UnknownError); return false; }); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::UnknownError); }); it('pass with all checks ok', function(){ // bypass all checks allow(Request::class)->toReceive('checkPath')->andReturn(true); allow(Request::class)->toReceive('checkPermission')->andReturn(true); allow(Request::class)->toReceive('checkParams')->andReturn(true); // bypass buildOptions(); allow(Request::class)->toReceive('buildOptions')->andReturn(true); $req = new Request('a/b'); expect($req->error->get())->toBe(Err::Success); }); }); }); describe('checkPath(@path)', function(){ it('fail when wrong path format: \'module/method\'', function(){ $values = ['a-b', 'a /b', 'a/b ']; foreach($values as $value){ $req = new Request($value); expect($req->error->get())->toBe(Err::WrongPathModule); } }); it('pass when valid path format: letter/number/-/_', function(){ $values = ['a_b-c/d-e_f', 'abc/def', '_a_/_b_']; foreach($values as $value){ $req = new Request($value); expect($req->error->get())->not->toBe(Err::WrongPathModule); } }); it('fail when unknown module', function(){ allow('json_decode')->toBeCalled()->andReturn([ 'mo-du_leA' => [], 'moduleB' => [] ]); $values = ['modulea/method', 'MODULEA/method', 'moduleC/method']; foreach($values as $value){ $req = new Request($value); expect($req->error->get())->toBe(Err::UnknownModule); } }); it('fail when unknown method', function(){ allow('json_decode')->toBeCalled()->andReturn([ 'mo-du_leA' => [ 'me-th_odA' => [] ], 'moduleB' => [] ]); $values = ['mo-du_leA/me-thodA', 'mo-du_leA/meth_odA', 'mo-du_leA/me-th_oda']; foreach($values as $value){ $req = new Request($value); expect($req->error->get())->toBe(Err::UnknownMethod); } }); it('pass all right', function(){ // bypass all checks allow(Request::class)->toReceive('checkPermission')->andReturn(true); allow(Request::class)->toReceive('checkParams')->andReturn(true); // bypass buildOptions(); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'mo-du_leA' => [ 'me-th_odA' => [] ], 'moduleB' => [] ]); $req = new Request('mo-du_leA/me-th_odA'); expect($req->error->get())->toBe(Err::Success); }); }); describe('checkPermission()', function(){ it('pass when no permission', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [] ] ]); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); }); it('pass when permission is not an array', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [ 'permissions' => 23.2 ] ] ]); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); }); it('pass when permission is an empty array', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [ 'permissions' => [] ] ] ]); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); }); it('fail if neither AuthSystem nor AuthSystemDefault', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('file_exists')->toBeCalled()->andReturn(true, false); expect('file_exists')->toBeCalled()->times(2); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [ 'permissions' => ['a'] ] ] ]); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::UnreachableResource); }); it('pass if no AuthSystem but AuthSystemDefault (Success)', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [ 'permissions' => ['a'] ] ] ]); allow(AuthSystemDefault::class)->toReceive('::permission')->andReturn(new Error(Err::Success)); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); }); it('pass if no AuthSystem but AuthSystemDefault (PermissionError)', function(){ // bypass checkers allow(Request::class)->toReceive('checkParams')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); allow('json_decode')->toBeCalled()->andReturn([ 'moduleA' => [ 'methodA' => [ 'permissions' => ['a'] ] ] ]); allow(AuthSystemDefault::class)->toReceive('::permission')->andReturn(new Error(Err::PermissionError)); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::PermissionError); }); }); describe('setAuthSystem(@instance)', function(){ it('pass when instance of AuthSystem', function(){ $instance = new AuthSystemDefault(); expect($instance)->toBeAnInstanceOf('api\core\AuthSystem'); expect(Request::setAuthSystem($instance))->toBeTruthy(); }); it('fail when not instance of AuthSystem', function(){ $instance = new Error(Err::Success); expect($instance)->not->toBeAnInstanceOf('api\core\AuthSystem'); expect(Request::setAuthSystem($instance))->toBeFalsy(); }); }); describe('checkParams(@params)', function(){ beforeEach(function(){ // bypass checkers allow(Request::class)->toReceive('checkPermission')->andReturn(true); allow(Request::class)->toReceive('buildOptions')->andReturn(true); $this->json = [ 'moduleA' => [ 'methodA' => [] ] ]; // dispatch() -> return $this->params allow(Request::class)->toReceive('dispatch')->andRun(function(){ return $this->params; }); }); context('config error', function(){ it('fail if @params is not an array', function(){ allow('is_array')->toBeCalled()->andReturn(false); allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::MissingParam); }); it('fail if array \'parameters\' is missing in config', function(){ allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::ConfigError); }); it('fail if \'parameters\' has no name', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 1 => [] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::ConfigError); }); it('fail if \'parameters\' has no specification', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => 1 ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::ConfigError); }); it('fail if one \'parameters\' have no \'type\' clause', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => false ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::ConfigError); }); it('fail if one \'parameters\' have incorrect \'type\' clause', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => false, 'type' => 12 ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::ConfigError); }); }); context('optional/required parameters', function(){ it('pass if missing optional parameter -> set to null by default', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => true, 'type' => 'text' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); // check param created with 'null' value $params = $req->dispatch(); expect($params)->toContainKey('paramName'); expect($params['paramName'])->toBeNull(); }); it('fail if optional param wrong type ', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => true, 'type' => 'id' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); expect(Checker::class)->toReceive('::run')->with('id', 'bla'); $req = new Request('moduleA/methodA', ['paramName' => 'bla']); expect($req->error->get())->toBe(Err::WrongParam); }); it('pass if optional param matching type ', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => true, 'type' => 'id' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); expect(Checker::class)->toReceive('::run')->with('id', '12'); $req = new Request('moduleA/methodA', ['paramName' => '12']); expect($req->error->get())->toBe(Err::Success); }); it('fail if required param missing ', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => false, 'type' => 'id' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $req = new Request('moduleA/methodA', []); expect($req->error->get())->toBe(Err::MissingParam); }); it('fail if required param wrong type ', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => false, 'type' => 'id' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); expect(Checker::class)->toReceive('::run')->with('id', 'bla'); $req = new Request('moduleA/methodA', ['paramName' => 'bla']); expect($req->error->get())->toBe(Err::WrongParam); }); it('pass if required param matching type ', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => false, 'type' => 'id' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); expect(Checker::class)->toReceive('::run')->with('id', '12'); $req = new Request('moduleA/methodA', ['paramName' => '12']); expect($req->error->get())->toBe(Err::Success); }); }); context('file parameters', function(){ it('fail if FILE param required + file does not exist', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'type' => 'FILE' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $_FILES = []; $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::MissingParam); }); it('pass if FILE param required + file exists -> create ref', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'type' => 'FILE' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $_FILES = [ 'paramName' => 'some_file_value' ]; $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); // check param created with 'null' value $params = $req->dispatch(); expect($params)->toContainKey('paramName'); expect($params['paramName'])->toBe('some_file_value'); }); it('pass if FILE optional param missing -> null', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => true, 'type' => 'FILE' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $_FILES = [ ]; $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); // check param created with 'null' value $params = $req->dispatch(); expect($params)->toContainKey('paramName'); expect($params['paramName'])->toBeNull(); }); it('pass if FILE optional param + file exists -> create ref', function(){ $this->json['moduleA']['methodA']['parameters'] = [ 'paramName' => [ 'optional' => true, 'type' => 'FILE' ] ]; allow('json_decode')->toBeCalled()->andReturn($this->json); $_FILES = [ 'paramName' => 'some_file_value' ]; $req = new Request('moduleA/methodA'); expect($req->error->get())->toBe(Err::Success); // check param created with 'null' value $params = $req->dispatch(); expect($params)->toContainKey('paramName'); expect($params['paramName'])->toBe('some_file_value'); }); }); }); describe('dispatch()', function(){ it('pass when option[\'download\'] == true -> launch download()', function(){ // bypass constructor allow(Request::class)->toReceive('buildRequestObject')->andRun(function(){ $this->options['download'] = true; $this->error = new Error(Err::Success); }); // bypass download() allow(Request::class)->toReceive('download')->andReturn(true); $req = new Request(); expect(Request::class)->toReceive('download')->once(); $req->dispatch(); }); it('fail when request have an error != Err::Success', function(){ // bypass constructor allow(Request::class)->toReceive('buildRequestObject')->andRun(function(){ $this->error = new Error(Err::UnknownError); }); $req = new Request(); $res = $req->dispatch(); expect($res)->toBeAnInstanceOf('\\api\\core\\Response'); expect($res->error->get())->toBe(Err::UnknownError); }); it('fail when ModuleFactory have an error', function(){ // bypass constructor allow(Request::class)->toReceive('buildRequestObject')->andRun(function(){ $this->error = new Error(Err::UnknownError); }); // bypass ModuleFactory allow(ModuleFactory::class)->toReceive('::getModule')->andRun(function(){ return new Error(Err::UnknownError); }); $req = new Request(); $res = $req->dispatch(); expect($res)->toBeAnInstanceOf('\\api\\core\\Response'); expect($res->error->get())->toBe(Err::UnknownError); }); }); }); }); });