-
Notifications
You must be signed in to change notification settings - Fork 1.1k
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
refactor(repository-tests): move relation tests to repository-tests package #3538
Conversation
c6feb46
to
7710fba
Compare
d42454a
to
975fc50
Compare
|
791f572
to
f551ade
Compare
7786f05
to
a94bb14
Compare
5800d99
to
9fa74e5
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for taking this non-trivial task!
Based on what I see in the code, I think there are few tricky areas that may require a bit of research:
- How to define model classes to take into account
features.idType
- How to work with repository classes and relation repository factories/accessors.
I feel that moving all tests in one pull request is not very practical, as there is too much code involved. I am proposing to split this effort into smaller chunks that will make it easier to iterate on the test design.
Personally, I would start with a very small piece and pick a single relation type (BelongsTo or HasMany) and a single test type (e.g. integration tests for the repository factory).
acceptance/repository-mongodb/src/__tests__/mongodb-default-repository.acceptance.ts
Outdated
Show resolved
Hide resolved
"debug": "^4.1.1" | ||
"debug": "^4.1.1", | ||
"@loopback/context": "^1.21.1", | ||
"@loopback/core": "^1.9.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I find it suspicious that we need to add these new dependencies. I quickly skimmed through the code and don't see any place using them. Can you double check please?
Ideally, there should be no changes in packages/repository-tests/package.json
and packages/repository-tests/package-lock.json
needed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We used '@loopback/context'
in our repository fixtures: import {Getter, inject} from '@loopback/context';
, but I don't think we use @loopback/core
anywhere so I'll remove it
packages/repository-tests/src/__tests__/acceptance/default-repository.memory.acceptance.ts
Outdated
Show resolved
Hide resolved
acceptance/repository-mysql/src/__tests__/mysql-default-repository.acceptance.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/relations/acceptance/belongs-to.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/relations/acceptance/belongs-to.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
async () => customerRepo, | ||
async () => shipmentRepo, | ||
); | ||
customerRepo = new repositoryClass(Customer, db) as CustomerRepository; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This isn't going to work.
Your CustomerRepository is always extending DefaultCrudRepository
, ignoring repositoryClass
constructor provided from outside.
When you call new repositoryClass
, an instance of repositoryClass
(typically DefaultCrudRepository
) is created. The constructor of CustomerRepository
is not called. As a result, the repository instance won't have any orders
property as defined in CustomerRepository
.
I see two options how to move forward:
(1)
Move the code creating repositories into a factory function, accepting repositoryClass
as an argument.
For example:
export function createCustomerRepository(repositoryClass: /*insert type*/) {
return class CustomerRepository extends repositoryClass<Customer, /*how do we specify id type here? */> {
// ...
}
}
(2)
Don't create model-specific repository classes, do everything from outside in the test.
async function givenBoundCrudRepositories(db: juggler.DataSource) {
orderRepo = new repositoryClass(Order, db);
// option 1: create variable instead of a property
orderCustomer = createBelongsToAccessor(
/* lookup relation definition */,
async () => customerRepo,
orderRepo,
);
// option 2: define a property - this requires `orderRepo` to be cast to something like
// `repositoryClass & {shipment: BelongsToAccessor<Shipment, typeof Order.prototype.id>;}`
orderRepo.shipment = createBelongsToAccessor(
/* lookup relation definition */,
async () => shipmentRepo,
orderRepo
);
return {orderRepo, orderCustomer};
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We were trying to pass db
to the CustomerRepository
in here. I am not sure if it doesn't run on mongo case we solved some mongo failures from this file before.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We created a helper file to get the repositories. What do you think?
packages/repository-tests/src/relations/fixtures/models/order.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/relations/acceptance/has-many-without-di.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
def69c1
to
ec41e56
Compare
708a1c7
to
b267a7b
Compare
b267a7b
to
bda386c
Compare
dc8782a
to
299fd3f
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great effort! LGTM in general, left one comment. I assume the test files are copied from the old ones, so didn't review them. The CI tests are failing and seem related, please fix them before merge :)
@property({ | ||
type: 'string', | ||
}) | ||
street: string; | ||
@property({ | ||
type: 'string', | ||
id: true, | ||
default: '12345', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
hmm, why do we need these default values?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we don't set the default value, MySQL somehow would have undefined
instead of null
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The CI tests are failing and seem related
They were just timeout failures. They're gone now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
MySQL somehow would have undefined instead of null
This behavior sounds reasonable to me, 🤔 , why is it a problem? Some tests expect null
instead of undefined
? Or any API calls will be broken?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If nothing is provided then loopback uses undefined
but mysql uses null
. And if we use null
in lb4, it's not the same thing as it's in MySQL. That's why we use these default values.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a well-know problem (feature?) that NoSQL databases like memory
and MongoDB represent missing values as undefined
, while SQL databases use NULL
. We have features.emptyValue
to use in tests to deal with that difference.
Please remove default
fields from the property definitions and modify the checks to use feature.emptyValue
instead of the default or undefined
.
See e.g. this test for inspiration:
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nice! I see you are making good progress here, discovering subtle differences in database-specific behavior. I find certain parts of the proposed changes problematic, let's do few more iterations to improve them.
...es/repository-tests/src/crud/relations/acceptance/has-many-without-di.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
// use strictObjectIDCoercion here to make sure mongo's happy | ||
@model({ | ||
settings: { | ||
strictObjectIDCoercion: true, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Configuring connector-specific settings in the shared test suite creates unwanted coupling between the generic test suite (that should be connector-independent) and individual connectors. It makes it difficult for individual connectors to tweak these settings, because the change must be made in the shared test suite - this is even more difficult for 3rd party connectors. Let's find a better solution please.
I am proposing to introduce a new features
flag allowing each connector to provide additional model settings that should be used by all models. Example usage here:
@model({
settings: features.modelSettings,
})
class Order extends Entity {
// ...
}
However! I believe the MongoDB connector allows users to enable strictObjectIDCoercion
at dataSource-level, for all models connected to that datasource. I prefer to enable strictObjectIDCoercion
that way (see MONGODB_CONFIG
) instead of introducing a new CrudFeature
flag.
I'd also like to better understand why is it necessary to enable strictObjectIDCoercion
? What is happening under the hood?
If strictObjectIDCoercion
is necessary to make relations work, then we need to make this very clear in our documentation! Ideally, we should modify lb4 datasource
generator to enable that flag in every datasource that's using the MongoDB connector. Docs & CLI updates are out of scope of this pull request, we should open a new issue to keep track of those changes. Ideally, that issue should explain why is that flag necessary (what is happening under the hood).
Very loosely related: In the future, I'd like us to rework MongoDB connector so that ObjectID type is never leaked outside of the connector, so that the Models and any user-facing code is using string
instead. See #3456
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We use strictObjectIDCoercion
here cause I checked these 2 issues: Mongo fails to find user by id, Document strictObjectIDCorecion flag. And also since we were using string
, I thought it might help to deal with ObjectId
.
But I don't think it works as expected. It will be removed.
packages/repository-tests/src/crud/relations/acceptance/repository.acceptance.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/acceptance/repository.acceptance.ts
Outdated
Show resolved
Hide resolved
@property({ | ||
type: 'string', | ||
}) | ||
street: string; | ||
@property({ | ||
type: 'string', | ||
id: true, | ||
default: '12345', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's a well-know problem (feature?) that NoSQL databases like memory
and MongoDB represent missing values as undefined
, while SQL databases use NULL
. We have features.emptyValue
to use in tests to deal with that difference.
Please remove default
fields from the property definitions and modify the checks to use feature.emptyValue
instead of the default or undefined
.
See e.g. this test for inspiration:
packages/repository-tests/src/crud/relations/fixtures/models/customer.model.ts
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/order.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/product.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/repositories/product.repository.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/integration/belongs-to.factory.integration.ts
Outdated
Show resolved
Hide resolved
b81937c
to
7a5faa7
Compare
85c290a
to
bb71b45
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great progress!
The new design using create{ModelName}Repo
looks much more correct & robust to me. Well done 👏
I found few more places where we need to take into account that certain types will be different depending on the connector, please take a look.
packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
...es/repository-tests/src/crud/relations/acceptance/has-many-without-di.relation.acceptance.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/order.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/order.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/shipment.model.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/repositories/address.repository.ts
Outdated
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/repositories/address.repository.ts
Show resolved
Hide resolved
d8c1a48
to
b3c20a4
Compare
f5631c8
to
95d6941
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The patch looks mostly good now, I have few last & hopefully minor comments to address.
packages/repository-tests/src/crud/relations/acceptance/belongs-to.relation.acceptance.ts
Show resolved
Hide resolved
packages/repository-tests/src/crud/relations/fixtures/models/customer.model.ts
Show resolved
Hide resolved
All major problems have been addressed by now.
ba9cb02
to
ea20fba
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Lovely 👏
🚀
…ackage Move these tests to repository-tests package so that they would be tested against real databases such as MongoDB and MySQL. Introduce a new type MixedIdType to CrudFeatures to solve the different usage of id types in different databases. Co-authored-by: Nora <[email protected]>
ea20fba
to
f4f97f8
Compare
resolves #3442
repository-tests
package so that they would be tested against real databases such as MongoDB and MySQL.mixedType
for different id types usageCrudRepositoryCtor
.relation.factory.integration.ts
is split intohas-many.factory.integration.ts
andbelongs-to.factory.integration.ts
Checklist
👉 Read and sign the CLA (Contributor License Agreement) 👈
npm test
passes on your machinepackages/cli
were updatedexamples/*
were updated👉 Check out how to submit a PR 👈