Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom requester + date match + use trait #56

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

pionl
Copy link

@pionl pionl commented Jun 15, 2020

Hi again, thank you for your package! I've modified the source to support few things we are currently using:

  1. I'm unable to extend your class (Laravel TestCase) - I've added underlaying trait that will work the same
  2. Added support for date match (ISO format)
  3. I want to test our api schema without real HTTP connections - I've added ability to test Laravel kernel request -> https://github.com/pionl/laravel-swagger-test. A did not want to add any breaking changes and I've tried to minimize the implementation as possible.

I've not updated your documentation as my English is not so great, but do you want me to edit it?

Thank you for your time

// This code is only reached if the send is successful and
// all matches are satisfied. Otherwise an error is throwed before
// reach this
$this->assertTrue(true);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a wrong usage of a trait and typical mistake: this method is not defined in this trait and therefore it can not be used here. You have created a dependency which is not protected and leads to mistakes.

This will not work:

class Example
{
   use \ByJG\ApiTools\AssertRequestAgainstSchema;

   public function foo()
   {
       $schema = \ByJG\ApiTools\Base\Schema::getInstance(
           file_get_contents('/path/to/json/definition')
       );
       $this->setSchema($schema);
       
       $request = new \ByJG\ApiTools\ApiRequester();
       $request
           ->withMethod('GET')
           ->withPath("/path/for/get/1");

       $this->assertRequest($request);
   }
}

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that bad. All that is missing is a line assert($this instanceof TestCase); at the beginning, plus a comment explaining that this is intended as plugin method for use in PHPUnit.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Or, another method could be to add an abstract declaration of assertTrue, though that might be problematic to do for the different PHP and PHPUnit versions. I'm not sure if this even works technically though, that a trait defines an abstract function that is then implemented by the baseclass of the class using the trait.

Copy link

@froschdesign froschdesign Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's not that bad.

This has nothing to do with good or bad, it is wrong usage of a trait. Unfortunately you can see a lot of it in different libraries, frameworks and so on.

I'm not sure if this even works technically though, that a trait defines an abstract function that is then implemented by the baseclass of the class using the trait.

https://www.php.net/manual/en/language.oop5.traits.php#language.oop5.traits.abstract

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi, not sure what you want me to do.

  1. Comment that the method is expected
  2. add abstract public static function assertTrue($condition, string $message = ''): void -- could cause problem with php version + phpunit as @UlrichEckhardt states.
  3. Add check assert($this instanceof TestCase);

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, Trait will only work if Assert class is used.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Last change of assertTrue was 3 years ago with initial implementation :)

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course, Trait will only work if Assert class is used.

Exactly that is the misuse of a trait and assert($this instanceof TestCase) is only a workaround which doesn't make things any better.

2. add abstract public static function assertTrue($condition, string $message = ''): void -- could cause problem with php version + phpunit as @UlrichEckhardt states.

Only with dead versions of PHP and PHPUnit. Bump the PHP version is an option here but this pull request is already mixing up too many topics. (imo)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @froschdesign sorry for late response, lately I've noticed, that we do not need a the instance for assertTrue, we can use Assert::assertTrue! This will solve the not ideal "assertTrue" requirement. What do you think?

Copy link
Owner

@byjg byjg Jul 5, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sorry, I am late to the party, but I have to agree that refer a suppose parent class inside a trait it is not the best approach. However if Assert::assertTrue() doesn't need any instance, I believe you solve the issue.

@@ -17,11 +20,31 @@ class ApiRequester extends AbstractRequester

public function __construct()
{
parent::__construct();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, that's a fix I'd take.

public function getStatusCode();

public function getBody();
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interface without any documentation or guarantees describing what you can expect from it. I know you said you'd help documenting it, but it's hard to tell the intention with nothing.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where would you expect the documentation to be? thank you

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've update the documentation. have a great day. Martin

return $this->interface->getBody();
}

}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the only class implementing ResponseInterface, so I wonder why there is a separate interface at all. Also, it is just a wrapper around the existing interface, so it doesn't add any feature but just a layer of code.

Concerning this part of your proposed changes, I'd rather not merge them. BTW: I'm using this framework with Phalcon, which also doesn't have a PSR request and response type. In order to achieve that, we create the Phalcon type from the PSR type (simple conversion) and then convert the response in a similar way. We also use this in a way that no actual HTTP transfer is made, the request is directly injected into a kernel in-process. Is there a reason this doesn't work for you?

Copy link
Author

@pionl pionl Jun 16, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand, I was not able to do a simple "swap" of the requester, that's why I wanted separate layer to add ability to use "anything".

Here I'm converting Laravel response to interface that AbstractRequester needs:

https://github.com/pionl/laravel-swagger-test/blob/master/src/Response/LaravelResponse.php

Now I'm able to change handleRequest implementation and swap it with Laravel request:

https://github.com/pionl/laravel-swagger-test/blob/master/src/LaravelRequester.php

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Of course I can build psr interface but BadResponseException will not be thrown (I would have to throw it -> more dependency on guzzle.

* @param $name
* @param $schema
* @param $body
* @param $type
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Types?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I did not want to change the "code" style (I've copied the) same structure as matchString and etc. I can of course add correct types.

return null;
}

if (!(bool)strtotime($body)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use strtotime($body) === false. The integer number zero is a non-error return value as per https://www.php.net/manual/en/function.strtotime.

That said, PHP does funny things with dates, like even parsing yesterday 12:00. I'm not sure about strtotime(), but I don't think this happens to match the OpenAPI standard by chance. What is the delta between this and the standard?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I've checked the specification and I've got it wrong - the date should not be implemented.

I should use

An optional format modifier serves as a hint at the contents and format of the string. OpenAPI defines the following built-in string formats: date – full-date notation as defined by RFC 3339, section 5.6, for example, 2017-07-21

At this moment matchString does not validate anything -- we could probably add a support for it?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So I've prepare format validation, but this could do a breaking change (tests fails because date-time has date string).

  1. We can remove the matchDate and format validation and leave the PR only for custom request + trait
  2. We can add the date format + future validations

@pionl
Copy link
Author

pionl commented Jun 16, 2020

I've update the code based on feedback - thank you for your time.

{
// Missing Request
$datesTimes = [
'01-01T01:00:00+1200',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add more invalid variants?

{
// Missing Request
$datesTimes = [
'2000-01-01T01:00:00+1200',
Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we add more invalid variants?

@byjg
Copy link
Owner

byjg commented Jul 5, 2020

I think these features are great, however some of them already implemented in the new version.

The CustomRequester feature was implemented by using the PSR7 (Web Request and URI) implementation. These implementation allows you have any implementation you want.

As example of Custom implementation we have the ApiRequester that actually executes a request to the api and the MockRequester that emulates the request by calling the class directly.

The other features (trait and date validator) I believe it is a good idea.

@pionl
Copy link
Author

pionl commented Nov 11, 2020

Ok guys, I'll split the implmentation, change the the laravel requester and etc and send a new PR. Thank you

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

7 participants