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

Add tests for UsersManager #1149

Merged
merged 22 commits into from
Sep 5, 2024
Merged

Conversation

fflorent
Copy link
Collaborator

@fflorent fflorent commented Aug 5, 2024

Context

HomeDBManager lacks of direct tests, which makes hard to make rework or refactorations.

Proposed solution

Specifically here, I introduce tests which call exposed UsersManager methods directly and check their result.

Also:

  • I removed updateUserName which seems to me useless (updateUser does the same work)
  • Taking a look at the getUserByLogin methods, it appears that Typescirpt infers it returns a Promise<User|null> while in no case it may resolve a nullish value, therefore I have forced to return a Promise<User> and have changed the call sites to reflect the change.

Related issues

I make this change for then working on #870

Has this been tested?

  • 👍 yes, I added tests to the test suite
  • 💭 no, because this PR is a draft and still needs work
  • 🙅 no, because this is not relevant here
  • 🙋 no, because I need help

@fflorent fflorent force-pushed the users-management-tests branch 4 times, most recently from b65d331 to c5a693e Compare August 5, 2024 19:40
@fflorent fflorent force-pushed the users-management-tests branch 3 times, most recently from 2cf3fdb to abdfccc Compare August 6, 2024 09:57
Copy link
Collaborator Author

@fflorent fflorent left a comment

Choose a reason for hiding this comment

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

Comments after having taken a look at the PR with @hexaltation

test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
app/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
@fflorent fflorent force-pushed the users-management-tests branch 3 times, most recently from ca8492a to 7d8b326 Compare August 7, 2024 10:17
@fflorent fflorent requested a review from hexaltation August 12, 2024 09:19
Copy link
Collaborator

@hexaltation hexaltation left a comment

Choose a reason for hiding this comment

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

LGTM

@fflorent fflorent self-assigned this Aug 14, 2024
@berhalak berhalak self-requested a review August 15, 2024 14:35
app/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
app/server/companion.ts Outdated Show resolved Hide resolved
import { updateDb } from 'app/server/lib/dbUtils';

const username = process.env.USER || "nobody";
const tmpDir = path.join(tmpdir(), `grist_test_${username}_userendpoint`);
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we randomize it more? Runing this test twice will reuse the same folder?
https://dev.to/omardulaimi/how-to-create-unique-temporary-directories-in-nodejs-1n69

Copy link
Collaborator Author

@fflorent fflorent Aug 16, 2024

Choose a reason for hiding this comment

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

I would say it is not necessary.
The point here is:

  • to have a temporary path, so it does not pollute the user filesystem;
  • to have a predictable path, so you can take a look after the tests run at the assets (here the databases) if something surprising happened;
  • to have this folder cleared before each run thanks to the call of prepareFilesystemDirectoryForTests (called here), so the test run is not affected by previous ones.

What do you think? Does it make sense to you?

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm just worry about running this tests twice (simultaneously) on the same machine, most of our tests (if not all of them) can be run simultaneously without a problem.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I see!

FWIW, I took inspiration of what those other tests suit do:

That being said, I can think of something that would get the best of the two worlds.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@berhalak I propose something to address your concerns, and still have the ability to disable the tests cleanup for debugging: 4031257

What do you think?

test/gen-server/lib/homedb/UsersManager.ts Outdated Show resolved Hide resolved
this.timeout(30000);

describe('static method', function () {
function* makeIdxIterator() {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you move helper methods to the end of the file? (And even outside of the describe funcition)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I prefer having the helper functions in the most limited scope, so it is clear where it is meant to be used, and for what purpose.

That being said, if you say your convention is to rather put all the helper at the end, I would agree to follow it.

Copy link
Contributor

Choose a reason for hiding this comment

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

Ok, I don't have a strong opinion here. I just prefer to have them hidden, or not to have them at all :).

}
}

function makeUsers(nbUsers: number, idxIt = makeIdxIterator()) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Can you use here the range function from lodash? I think it will look much more simpler, this one is hard to follow.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I need to keep track of the increment for users' id across makeUsers call. When I call makeUsers let's say this way:

        const idxIt = makeIdxIterator();
        const groupsUsersMap = {
          'editors': makeUsers(2, idxIt),
          'owners': makeUsers(1, idxIt),
        };

groupsUsersMap equals to the following object, whose users' id are unique:

        {
          'editors': [{id: 0, name: 'user 0'}, {id: 1, name: 'user 1'}],
          'owners': [{id: 2, name: 'user 2'}]
        }

I don't know how to make a similar thing with lodash.range().

An alternative I can do, but I find it less elegant, would consist in passing the start index each time:

let startIdx = 0;
const groupsUsersMap = {};
groupsUsersMap['editors'] = makeUsers(2, startIdx);
startIdx += 2;
groupsUsersMap['owners'] = makeUsers(1, startIdx);
startIdx += 1;

});
}

function makeUsersList(usersListLength: number, nbUsersByGroups: number) {
Copy link
Contributor

Choose a reason for hiding this comment

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

What does nb stands for?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

number of. I am commenting the function, with the hope to make things clearer

@fflorent fflorent requested a review from berhalak August 16, 2024 13:19
@fflorent
Copy link
Collaborator Author

@berhalak I think of something: my test assume that they run using Sqlite.

Several questions come to my mind:

  • Can running these tests with Postgresql be worth?
  • If so: I don't see tests running with Postgresql on the CI, I can take a look to see how hard it is to support this database.
  • If not: I propose to skip these tests when TYPEORM_TYPE is set to postgresql

To the first question, my answer would be no, the UsersManager uses a lot of TypeORM methods, and does not seem to make SQL query directly (but I may be a bit biased by my lazyness).

@fflorent fflorent force-pushed the users-management-tests branch 3 times, most recently from 42eee85 to 2a2ccf1 Compare August 19, 2024 13:32
@fflorent fflorent force-pushed the users-management-tests branch from 2a2ccf1 to eaaff1b Compare August 19, 2024 13:49
}
}

export function dereferenceConnection(name: string) {
// There seem to be no official way to delete connections.
// Also we should probably get rid of the use of connectionManager, which is deprecated
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I am tempted to do that right now, but I fear that the PR becomes huge, especially to review it.

The new way consist in using DataSource (which is the new name for the Connection class, renamed because it is confusing), and instantiate using the constructor in place of createConnection.

We won't have any name collision, as only the ConnectionManager use it for other purposes than identifying the DataSource in error messages.

Copy link
Contributor

@berhalak berhalak left a comment

Choose a reason for hiding this comment

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

Thanks @fflorent!

export async function removeConnection() {
if (getConnectionManager().connections.length > 0) {
if (getConnectionManager().connections.length > 1) {
export async function removeConnection(name?: string) {
Copy link
Contributor

Choose a reason for hiding this comment

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

The name parameter doesn't seem to be used here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

That's true, thanks!

@berhalak
Copy link
Contributor

  • Can running these tests with Postgresql be worth?

We run some tests with Postgresql in our internal CI, but since it is not part of the grist-core yet, and those tests are rather unit tests, I think it is fine to rely on typeorm abstraction.

@fflorent fflorent requested a review from berhalak August 23, 2024 11:10
@paulfitz
Copy link
Member

I'm seeing failures in tests after UsersManager in a derived version of Grist (the one for our SaaS). Haven't looked into why yet, and may be specific to us.

Screenshot from 2024-08-23 12-19-34

if (!connectionMap.has(name)) {
throw new Error('connection with this name not found: ' + name);
}
connectionMap.delete(name);
Copy link
Member

Choose a reason for hiding this comment

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

Is it possible this results in the change of behavior we see in our tests @fflorent? This is a pretty murky corner of TypeORM.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I feel more like the issue is due to this helper:
https://github.com/gristlabs/grist-core/pull/1149/files#diff-3d1ec6a021479fb3895f89f5c855b14f664e45c31e979082da277538b906cc5cR264-R282

Which has been introduced to test functions related to special ids, but I feel like these two tests bring more pain than are actually helpful:

I also tried to see whether removing the Connection related methods in favor of using DataSource would help, but with no luck (see #1166, which I may continue to work on but rather for the satisfaction of stop using deprecated entities and functions).

So I won't mind removing these tests, especially if it solves your failures, I agree that it deals with strange behaviors of TypeORM.

Copy link
Member

Choose a reason for hiding this comment

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

It is awkward getting this sorted out, so I'm going to land and that'll force us to sort it out internally. Thanks for the suggestions.

@paulfitz paulfitz merged commit 16ebc32 into gristlabs:main Sep 5, 2024
11 checks passed
@fflorent fflorent deleted the users-management-tests branch September 5, 2024 20:37
@fflorent
Copy link
Collaborator Author

fflorent commented Sep 5, 2024

Thank you @paulfitz!

hexaltation pushed a commit to hexaltation/grist-core that referenced this pull request Sep 24, 2024
Context

HomeDBManager lacks of direct tests, which makes hard to make rework or refactorations.
Proposed solution

Specifically here, I introduce tests which call exposed UsersManager methods directly and check their result.

Also:

    I removed updateUserName which seems to me useless (updateUser does the same work)
    Taking a look at the getUserByLogin methods, it appears that Typescirpt infers it returns a Promise<User|null> while in no case it may resolve a nullish value, therefore I have forced to return a Promise<User> and have changed the call sites to reflect the change.

Related issues

I make this change for then working on gristlabs#870
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
Archived in project
Development

Successfully merging this pull request may close these issues.

4 participants