- Issue created by @tedbow
- πΊπΈUnited States tedbow Ithaca, NY, USA
I think I started
testAssetLibrary()
and probably copied the existingtestJavaScriptComponent()
as starting point . So it makes sense that sincetestJavaScriptComponent()
didn't test thistestAssetLibrary()
would also miss it - π§πͺBelgium wim leers Ghent π§πͺπͺπΊ
For
Pattern
config entities, this is actually tested:// Use the individual URL in the list response body. $individual_body = $this->assertExpectedResponse('GET', Url::fromUri('base:/xb/api/config/pattern/testpatternpleaseignore'), [], 200, ['languages:language_interface', 'user.permissions', 'theme'], ['config:experience_builder.component.sdc.xb_test_sdc.props-no-slots', 'config:experience_builder.pattern.testpatternpleaseignore', 'http_response'], 'UNCACHEABLE (request policy)', 'MISS'); $expected_individual_body_normalization = $expected_pattern_normalization; $expected_individual_body_normalization['js_footer'] = str_replace('xb\/api\/config\/pattern', 'xb\/api\/config\/pattern\/testpatternpleaseignore', $expected_pattern_normalization['js_footer']); $this->assertSame($expected_individual_body_normalization, $individual_body);
So it's only β¨ HTTP API for code component config entities Active and β¨ Storage for global CSS Active that missed this. #2 explains why both missed it.
- πΊπΈUnited States tedbow Ithaca, NY, USA
Would be a good idea to refactor this test? looking at
testPattern()
don't all the entity types need to test the same things- Authorization, already refactored into
assertAuthenticationAndAuthorization
- Failed POST for Openapi validation
- Failed POST for requests that pass Openapi validation
- empty individual GET and empty list GET before entity creation
- Successful POST
- non-empty individual GET and non-empty list GET after entity creation
- 409 error for trying to POST another entity using the entity id key
- Failed PATCH for Openapi validation
- Failed PATCH for requests that pass Openapi validation
- Successful PATCH
- DELETE
- empty individual GET and empty list GET after entity deletion
Maybe this not the exact list but whatever the list is shouldn't be same for all entities?
Could we maybe make 1 test method like
public function test(string $entity_type_id, array $openapi_invalid_data_cases, array $entity_invalid_data_cases, array $valid_initial_entity, array $expected_entity, array $valid_update_entity, $expected_updated_entity);
For
- Failed POST for Openapi validation
- Failed POST for requests that pass Openapi validation
- Failed PATCH for Openapi validation
- Failed PATCH for requests that pass Openapi validation
we could use
array $openapi_invalid_data_cases, array $entity_invalid_data_cases
for both casesI think this would force use to have the same coverage for the all entity types.
If that is too many arguments to a test method we could also do,
public function testOpenApiValidation(string $entity_type_id, array $invalid_entities, $valid_entity);
public function testEntityiValidation(string $entity_type_id, array $invalid_entities, $valid_entity);
- Authorization, already refactored into
- πΊπΈUnited States tedbow Ithaca, NY, USA
there are also big problems with our test coverage for non-empty list responses in `XbConfigEntityHttpApiTest`
\Drupal\Tests\experience_builder\Functional\XbConfigEntityHttpApiTest::testAssetLibrary
doesn't have test coverage for a non-empty response from\Drupal\experience_builder\Controller\ApiConfigControllers::list
Trying to add test coverage for this in β¨ Auto-save code components Active I found it is not possible because the response has an openapi validation error.
We have
description: All available asset libraries content: application/json: schema: type: array
But the "array" schema I don't think works because Looking at
\League\OpenAPIValidation\Schema\Keywords\Type::validate
it hascase CebeType::ARRAY: if (! is_array($data) || ArrayHelper::isAssoc($data)) { break; } $matchedType = $type; break;
So it doesn't match "array" for associative arrays.
\Drupal\Tests\experience_builder\Functional\XbConfigEntityHttpApiTest::testJavaScriptComponent
does seem to test a non-empty list response but the openapi spec for/xb/api/config/js_component
doesn't have GET specified so there is no openapi validation for the response\Drupal\Tests\experience_builder\Functional\XbConfigEntityHttpApiTest::testPattern
does seem to have test coverage for non-empty list responses but Looking atpaths./xb/api/config/pattern.get
in our openapi there is
patternProperties: '^\\[a-zA-Z0-9_-]$': $ref: '#/components/schemas/PatternPreview'
So because of π Openapi.yml uses patternProperties which is not supported by our dependencies Needs work this doesn't actually trigger validation
I have confirmed this by switching the above to$ref: '#/components/schemas/LayoutSlot'
andtestPattern()
still passes π
So I think they are all broken in a different way π±
- π§πͺBelgium wim leers Ghent π§πͺπͺπΊ
- π± Excellent sleuthing! π
- Yep, I've noticed there's plenty of missing OpenAPI validation. But looking at it more closely again: why is that not triggering
\League\OpenAPIValidation\PSR7\Exception\NoPath
?! π± - π
This makes it all the more important to have proactive OpenAPI maintainers on this project, because it's supposed to save us time, not cost us time. And if the infrastructure doesn't let us do
It's feeling more and more that OpenAPI is less and less worth it?! π Feels like we need to spend some dedicated time to make it actually robust.
- First commit to issue fork.
- πΊπΈUnited States traviscarden
So the questions we're asking now are obviously much broader than the original scope of the issue--almost existential in some ways. There's a lot of--though not total--overlap with π Decide on an approach for writing tests for OpenAPI integration Active . I'll go ahead and leave my analysis here until we decide to reorganize the work. For findability, so it's not just buried in the body of this comment, I'll refer to OpenAPI.Tools, which looks very useful.
So in short, what I'm hearing is that our API development is still risky and painful due to poor specification, testing, and quality controls, leading to regressions and surprise obstacles. Specifically...
- Our OpenAPI implementation is incomplete, untrustworthy, fragile, sometimes invalid, and inconsistently enforced.
- Our test coverage is likewise incomplete, inconsistent, and ad hoc.
In order to stabilize API development, we need to figure out how to...
-
Ensure that
openapi.yml
is valid, correct, and complete. That...- All API paths have an entry.
- Each entry specifies all of its supported methods, e.g.,
GET
andPOST
. - Each method specifies all known possible responses, including error responses, e.g.,
200
and500
. - The strictest available data validation rules are applied consistent with design requirements.
- The file is as correct, readable, and idiomatic as possible.
- It would also be nice to enforce some consistent formatting and reduce merge conflicts.
- Get it to actually be consistently enforced on API messages (requests/responses).
- Ensure our tests are coextensive with it and both are complete.
- And ensure that it all stays that way.
Here are my thoughts on solutions, taking for granted that they will still revolve around
openapi.yml
and PHPUnit:- We should consider static analysis that goes deeper than our PHPUnit tests of specification itself currently do. It looks like there are a lot of options out there. A good linter that runs on CI seems like an obvious opportunity.
- We should consider whether
thephpleague/openapi-psr7-validator
is (still) the best solution for enforcing the specification. - We might consider publishing an OpenAPI documentation site generated from our
openapi.yml
. That would mostly likely prevent certain errors from creeping in, and it would probably have value in its own right, since anybody in the community could access it. - We can either write some PHPUnit "meta tests" that inspect
openapi.yml
and look for corresponding tests, or we can try to enforce test requirements with interfaces and test class design, or we can look into PHP attribute-based solutions to see if they can provide any help. - We can extract some universal but commonly overlooked test cases and centralize them. For example, instead of each developer having to remember to test for error-handling for invalid
POST
bodies, we could have one test that just iterates over the paths inopenapi.yml
or uses the autoloader to get all of our endpoint controllers and just posts garbage, asserting that it gets an error response for each one. - It may be possible, by extending some of the open source tools available (many of them have extension systems) or by writing something ourselves, to automatically generate some amount of
openapi.yml
code automatically. - There may be some opportunities to improve the design of the API (controller) classes to enforce some consistency--to reduce boilerplate and require a baseline of common functionality through interfaces and base classes.
That's my first round of analysis. If I were asked, I would probably suggest starting with a linter/static analysis tool, since we'd be likely to get so much out of a good one "for free". Then I would look at our validation library and either fix our implementation or replace it. After that, I would do whatever seemed the most fun at the time. π
Hopefully that's what you were looking for, @wim leers. Now's the right time to ask, right? When I've already done it. π¬ π