Skip to content

test(ui): add unit tests for useModelVersionById hook#2595

Merged
google-oss-prow[bot] merged 2 commits intokubeflow:mainfrom
YuliaKrimerman:useModelArtifactsByVersionId-tests
Apr 17, 2026
Merged

test(ui): add unit tests for useModelVersionById hook#2595
google-oss-prow[bot] merged 2 commits intokubeflow:mainfrom
YuliaKrimerman:useModelArtifactsByVersionId-tests

Conversation

@YuliaKrimerman
Copy link
Copy Markdown
Contributor

Description

Adds unit tests for the useModelVersionById hook (clients/ui/frontend/src/app/hooks/useModelVersionById.ts), which previously had no test coverage.

The test file covers four scenarios:

  • API unavailable — verifies the hook returns an error when apiAvailable is false
  • No modelVersionId — verifies the hook silently fails (returns null, no error) when called without an ID
  • Successful fetch — verifies the hook returns the fetched ModelVersion and loaded: true
  • Correct API arguments — captures the callback passed to useFetchState and asserts api.getModelVersion is called with the correct modelVersionId

Follows the same testing patterns established by useModelArtifactsByVersionId.spec.ts and useModelTransferJobs.spec.ts.

How Has This Been Tested?

  • All 4 unit tests pass locally via npx jest src/app/hooks/__tests__/useModelVersionById.spec.ts
  • ESLint passes with no errors on the new file

Merge criteria:

  • All the commits have been signed-off (To pass the DCO check)

  • The commits have meaningful messages

  • Automated tests are provided as part of the PR for major new functionalities; testing instructions have been added in the PR body (for PRs involving changes that are not immediately obvious).

  • The developer has manually tested the changes and verified that the changes work.

  • Code changes follow the kubeflow contribution guidelines.

  • For first time contributors: Please reach out to the Reviewers to ensure all tests are being run, ensuring the label ok-to-test has been added to the PR.

Signed-off-by: Yulia Krimerman <juliapiterova@hotmail.com>
Copy link
Copy Markdown
Contributor

@manaswinidas manaswinidas left a comment

Choose a reason for hiding this comment

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

3 of the 4 tests are tautological — they mock useFetchState's return value then assert exactly what was mocked, without ever invoking the hook's callback. Only test 4 (callback capture) genuinely exercises the hook logic, and it only covers the happy path.

Please rewrite tests 1–2 to capture the callback (same pattern as test 4) and verify it rejects with the expected errors. Test 3 can be dropped since test 4 already covers that path. This will give real coverage of all 3 branches.

Comment on lines +60 to +75
refreshAllAPI: jest.fn(),
});

const mockError = new Error('API not yet available');
mockUseFetchState.mockReturnValue([null, false, mockError, jest.fn()]);

const { result } = testHook(useModelVersionById)('version-1');

await waitFor(() => {
const [, , error] = result.current;
expect(error).toBeInstanceOf(Error);
expect(error?.message).toBe('API not yet available');
});
});

it('should silently fail when modelVersionId is not provided', async () => {
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Tautological: you mock useFetchState to return [null, false, mockError, …] then assert the error is exactly what you mocked. The hook's !apiAvailable branch is never invoked.

Capture the callback instead (same approach as test 4) and verify it rejects:

const cb = mockUseFetchState.mock.calls[0][0];
await expect(cb({})).rejects.toThrow('API not yet available');

Comment on lines +78 to +91
apiAvailable: true,
refreshAllAPI: jest.fn(),
});

mockUseFetchState.mockReturnValue([null, false, undefined, jest.fn()]);

const { result } = testHook(useModelVersionById)();

await waitFor(() => {
const [data, , error] = result.current;
expect(data).toBeNull();
expect(error).toBeUndefined();
});
});
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Same issue — useFetchState is mocked to return [null, false, undefined, …], then you assert data is null and error is undefined. This just validates the mock, not the hook.

To genuinely test the !modelVersionId branch, capture the callback and check:

const cb = mockUseFetchState.mock.calls[0][0];
await expect(cb({})).rejects.toThrow('No model version id');

Comment on lines +93 to +112
it('should fetch the model version when API is available and id is provided', async () => {
const mockedVersion = mockModelVersion({ id: 'version-1', name: 'my-version' });

mockUseModelRegistryAPI.mockReturnValue({
api: {
...mockModelRegistryAPIs,
getModelVersion: jest.fn().mockResolvedValue(mockedVersion),
},
apiAvailable: true,
refreshAllAPI: jest.fn(),
});

mockUseFetchState.mockReturnValue([mockedVersion, true, undefined, jest.fn()]);

const { result } = testHook(useModelVersionById)('version-1');

await waitFor(() => {
const [data, loaded] = result.current;
expect(loaded).toBe(true);
expect(data).toEqual(mockedVersion);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Also tautological — asserts data and loaded equal exactly what mockUseFetchState was told to return. The getModelVersion mock is set up but never called.

This test can be removed entirely; test 4 already covers the happy path by actually invoking the captured callback and verifying getModelVersion receives the right args.

Comment on lines +114 to +137
});

it('should pass the correct modelVersionId to api.getModelVersion', async () => {
const getModelVersionMock = jest.fn().mockResolvedValue(mockModelVersion({ id: 'v-42' }));

mockUseModelRegistryAPI.mockReturnValue({
api: { ...mockModelRegistryAPIs, getModelVersion: getModelVersionMock },
apiAvailable: true,
refreshAllAPI: jest.fn(),
});

// Capture the callback that the hook passes to useFetchState
mockUseFetchState.mockReturnValue([null, false, undefined, jest.fn()]);

testHook(useModelVersionById)('v-42');

// useFetchState receives the callback as its first argument
const capturedCallback = mockUseFetchState.mock.calls[0][0] as (
opts: unknown,
) => Promise<unknown>;

await capturedCallback({});

expect(getModelVersionMock).toHaveBeenCalledWith({}, 'v-42');
Copy link
Copy Markdown
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 test that genuinely exercises hook logic — nice. Consider reusing the same callback-capture approach for the !apiAvailable and !modelVersionId branches so all three paths are properly covered.

Signed-off-by: Yulia Krimerman <juliapiterova@hotmail.com>
Copy link
Copy Markdown
Contributor

@manaswinidas manaswinidas left a comment

Choose a reason for hiding this comment

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

/lgtm
/approve

All review comments addressed

@google-oss-prow google-oss-prow Bot added the lgtm label Apr 17, 2026
@google-oss-prow
Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: manaswinidas

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@google-oss-prow google-oss-prow Bot merged commit d9aa591 into kubeflow:main Apr 17, 2026
30 of 31 checks passed
Taj010 pushed a commit to Taj010/model-registry that referenced this pull request Apr 21, 2026
* add unit tests

Signed-off-by: Yulia Krimerman <juliapiterova@hotmail.com>

* addressed comments

Signed-off-by: Yulia Krimerman <juliapiterova@hotmail.com>

---------

Signed-off-by: Yulia Krimerman <juliapiterova@hotmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants