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

RJS-2397: Collections in mixed - synced tests #6617

Merged
merged 33 commits into from
May 8, 2024

Conversation

papafe
Copy link
Contributor

@papafe papafe commented Apr 15, 2024

This PR adds tests for synced collections in mixed

☑️ ToDos

  • 🚦 Tests

}

// TODO: This might be a useful utility
// TODO: This might be a useful utility //Should we keep this around if not used?
Copy link
Member

Choose a reason for hiding this comment

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

If not used, it should be safe to remove it

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree, I'll remove it

integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
@kneth kneth self-requested a review April 15, 2024 13:28
Copy link
Member

@kneth kneth left a comment

Choose a reason for hiding this comment

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

Thank you.

Do you plan to add more tests?


/**
* The default tester of values.
* @param actual The value downloaded from the server.
* @param inserted The value inserted locally before upload.
*/
function defaultTester(actual: Realm.Mixed, inserted: Realm.Mixed) {
expect(actual).equals(inserted);
if (actual instanceof Realm.List) {
Copy link
Member

Choose a reason for hiding this comment

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

If actual is the wrong type (the database is broken), then this might be asserting the wrong thing and we cannot rely on the type of inserted since that's has the unmanaged value 🤔 I believe this was why the test functions were separate. Also it allows for more detailed testing and multiple tests per type.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Makes sense. I'll reinstantiate the local test functions that were used in the tests and keep this for the mixed testing

Copy link
Member

@kraenhansen kraenhansen Apr 25, 2024

Choose a reason for hiding this comment

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

Did you address this? Perhaps I misunderstood the action you would take, based on my first comment.
The current use of this still feel wrong to me, as the assertion performed depends on the type of the actual, if the implementation is broken to the point where actual has the wrong value, we're not ensured that we'll be choosing the right assertion below.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I misunderstood your comment in the beginning
So... I agree with your concern and I agree this is super suboptimal. The reason why it was done like that is that in the tests that were already there the realm gets closed and opened again so if one of the tested values is an object then we can't access it anymore. To be honest I'm not sure how to solve this in a clean way. I'm open for suggestions though 😁
I think we can discuss this on slack

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I've fixed this. I've made the testing a little bit more complicated, but now the default tester actually uses the inserted value as the truth value

return realmAndConfig.realm;
};

this.closeAllRealms = async () => {
Copy link
Member

@kneth kneth Apr 19, 2024

Choose a reason for hiding this comment

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

Realm.clearTestState() and Realm.shutdown() are closing all open Realms: https://github.com/realm/realm-js/blob/main/packages/realm/src/Realm.ts#L159-L191

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From what I can understand from the body of the function, then just calling Realm.clearTestState would be enough here, right?

If so, maybe it's also not necessary in the hook that's already there?

export function openRealmHook(config: OpenRealmConfiguration = {}) {
return async function openRealmHandler(this: Partial<RealmContext> & UserContext & Mocha.Context): Promise<void> {
this.longTimeout();
if (this.realm) {
throw new Error("Unexpected realm on context, use only one openRealmBefore per test");
} else {
const { realm, config: actualConfig } = await openRealm(config, this.user as unknown as User);
this.realm = realm;
this.closeRealm = async ({
clearTestState = true,
deleteFile = true,
reopen = false,
}: Partial<CloseRealmOptions>) => {
if (this.realm && !this.realm.isClosed) {
this.realm.close();
}
// Get rid of the Realm in any case
delete this.realm;
if (deleteFile) {
Realm.deleteFile(actualConfig);
}
if (clearTestState) {
Realm.clearTestState();
}
if (reopen) {
const { realm } = await openRealm(actualConfig, this.user as unknown as User);
this.realm = realm;
}
};
}
};
}
/**
* Close the Realm instance on the current `this` context
*
* @param this Mocha `this` context
*/
export function closeThisRealm(this: RealmContext & Mocha.Context): void {
if (this.closeRealm) {
this.closeRealm({ clearTestState: true, deleteFile: true });
}
// Clearing the test state to ensure the sync session gets completely reset and nothing is cached between tests
Realm.clearTestState();

@papafe papafe requested a review from elle-j April 23, 2024 09:08
@papafe
Copy link
Contributor Author

papafe commented Apr 23, 2024

For me the work on the test is "practically" done. The roundtrip tests are all passing, no issues with those.
The issue is for the "mixed collections" tests, that even though pass when running each on their own, exhibit some strange behaviour when run together, so I need to investigate why we get this flakyness.
But general comments about the tests/structure is appreciated 😁

@papafe papafe marked this pull request as ready for review April 23, 2024 09:10
// realm.close();
// }
// Realm.deleteFile(config);
// });
Copy link
Contributor Author

Choose a reason for hiding this comment

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

There is another thread in this PR that is not visible here because it's on an outdated version of this file, regarding if here using clearTestState is enough.

@@ -153,6 +157,11 @@ type RealmContext = {
*/
closeRealm(options?: Partial<CloseRealmOptions>): Promise<void>;
} & Mocha.Context;
type MultiRealmContext = {
openedInfo: { realm: Realm; config: Realm.Configuration }[];
getRealm: (config: any) => Promise<Realm>; //any should be OpenRealmConfiguration
Copy link
Contributor Author

Choose a reason for hiding this comment

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

I think I can't use OpenRealmConfiguration here because it would create a circular reference and TS didn't seem to appreciate that

@papafe
Copy link
Contributor Author

papafe commented Apr 24, 2024

@elle-j @kraenhansen @kneth I've removed all the flaky tests and the changes I've done with those. I will add them in a separate PR, but for now I wanted to have at least the basic tests merged

@papafe papafe changed the title RJ-2397: Collections in mixed - synced tests RJS-2397: Collections in mixed - synced tests Apr 24, 2024
@papafe papafe force-pushed the fp/collections-mixed-sync-tests branch from 17a6329 to c8d02a1 Compare April 25, 2024 08:43
Copy link
Member

@elle-j elle-j left a comment

Choose a reason for hiding this comment

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

Nice to get some roundtrip tests in 🥳 Left some ideas 👍

integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
@papafe papafe force-pushed the fp/collections-mixed-sync-tests branch from c8a4f24 to b9c7470 Compare April 29, 2024 08:39
Copy link
Member

@elle-j elle-j left a comment

Choose a reason for hiding this comment

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

Nice 🥳

integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
integration-tests/tests/src/tests/sync/mixed.ts Outdated Show resolved Hide resolved
papafe and others added 2 commits May 6, 2024 09:33
Co-authored-by: LJ <81748770+elle-j@users.noreply.github.com>

/**
* The default tester of values.
* @param actual The value downloaded from the server.
* @param inserted The value inserted locally before upload.
*/
function defaultTester(actual: Realm.Mixed, inserted: Realm.Mixed) {
expect(actual).equals(inserted);
function defaultTester(actual: unknown, inserted: unknown) {
Copy link
Member

Choose a reason for hiding this comment

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

Since the code infers the expected type from the shape of the inserted POJO, it establishes implicit expectations from the calling code in the behavior of the function. This is especially problematic because inserted is unknown and therefore accepts any values in combination with the final else block, which just compares the string representation. In my opinion it's too easy for the calling code to receive a changed that will render this default tester function useless and as such I fear this will be a source of subtle bugs in the testing code that are hard to discover. Additionally, since this puts restrictions on the shape of the inserted values, it's potentially harder for us to test combinations of these and types we add in the future.

Do you see any way of passing the tester function explicitly from the tests?
Although potentially more verbose.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I understand your concerns.
I suppose it depends what you mean by passing the tester function explicitly from the tests. The methods that are used to define the tests accept a tester function, and this is the one that is used when no function is passed.
If you mean that we should pass a more specific tester function for each test then... We can do it, but it's just much more complicated, because this method not only considers all the possible mixed types (I think at least), but also collections, so it does a recursive comparison if needed.

Copy link
Member

Choose a reason for hiding this comment

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

pass a more specific tester function for each test

Exactly.

does a recursive comparison if needed

Just an idea on the top of my head: We could implement a higher-order function to create dictionary tester functions with a specific tester applied on items.

const createDictionaryTester = (itemTester: ValueTester) => (actual: unknown, inserted: unknown) {
  expectRealmDictionary(actual);
  if (typeof inserted !== "object" || inserted === null) {
    throw new Error("Expected an object of values");
  }
  for (const [insertedKey, insertedValue] of Object.entries(inserted)) {
    itemTester(actual[insertedKey], insertedValue);
  }
}

To test a nested dictionary { foo: { bar: ["baz"] } } this be chained like:

tester: createDictionaryTester(createDictionaryTester(stringTester))

Copy link
Contributor Author

@papafe papafe May 6, 2024

Choose a reason for hiding this comment

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

I think there are a couple of things to consider:

  • The values that are being used the tests look something like [1, true, "string", ...., object, innerList, innerDicti]. S
    Thus the testing methods wouldn't look particularly pretty.
  • It gets even more messy in the other PR with additional tests. In that PR I don't have a single mixed value that I'm trying to roundtrip like here, but I do a lot of modifications to the list/dictionary in order to verify it all works correctly, so I can't have one specific testing function.

Overall I tried to make a testing function that would be generic enough to work with different kind of tests, even though it is not maybe the most specific or strict. I think that if we go the more strict route we either need to simplify the test cases (mostly considering the additional PR) or we need to greatly complicate our tester.

Copy link
Member

Choose a reason for hiding this comment

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

🤔 Yeah. I agree, there are tradeoffs here, which complicates the decision.
I wanted to mention my reservations, but I won't object to this merging as is.

@papafe papafe merged commit 97a48e9 into lj/collections-in-mixed May 8, 2024
29 checks passed
@papafe papafe deleted the fp/collections-mixed-sync-tests branch May 8, 2024 08:12
@papafe papafe restored the fp/collections-mixed-sync-tests branch May 23, 2024 13:35
@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 22, 2024
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants