Skip to content

Commit a7a5f24

Browse files
committed
fixing tests after rebase
1 parent 9dc98b2 commit a7a5f24

File tree

4 files changed

+115
-123
lines changed

4 files changed

+115
-123
lines changed

packages/cypress/cypress/pages/modelsAsAService.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -452,6 +452,10 @@ class RevokeAPIKeyModal extends Modal {
452452
super('Revoke API key?');
453453
}
454454

455+
findRevokeAllButton(): Cypress.Chainable<JQuery<HTMLElement>> {
456+
return cy.findByRole('button', { name: 'Revoke keys' });
457+
}
458+
455459
findRevokeButton(): Cypress.Chainable<JQuery<HTMLElement>> {
456460
return cy.findByRole('button', { name: 'Revoke' });
457461
}

packages/cypress/cypress/support/commands/odh.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1134,6 +1134,14 @@ declare global {
11341134
((
11351135
type: 'POST /maas/api/v1/api-keys',
11361136
response: { data: OdhResponse<CreateAPIKeyResponse> },
1137+
) => Cypress.Chainable<null>) &
1138+
((
1139+
type: 'GET /maas/api/v1/user',
1140+
response: OdhResponse<{ data: { userId: string; clusterAdmin: boolean } }>,
1141+
) => Cypress.Chainable<null>) &
1142+
((
1143+
type: 'GET /maas/api/v1/namespaces',
1144+
response: OdhResponse<{ data: { metadata: { name: string } }[] }>,
11371145
) => Cypress.Chainable<null>);
11381146
}
11391147
}

packages/cypress/cypress/tests/mocked/modelsAsAService/maasApiKeys.cy.ts

Lines changed: 99 additions & 122 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ describe('API Keys Page', () => {
2222
}),
2323
);
2424

25+
cy.interceptOdh('GET /maas/api/v1/user', {
26+
data: { userId: 'test-user', clusterAdmin: false },
27+
});
28+
cy.interceptOdh('GET /maas/api/v1/namespaces', { data: [] });
29+
2530
cy.interceptOdh(
2631
'GET /api/dsc/status',
2732
mockDscStatus({
@@ -39,143 +44,115 @@ describe('API Keys Page', () => {
3944
has_more: false,
4045
},
4146
});
47+
apiKeysPage.visit();
48+
});
4249

43-
it('should display the API keys table page', () => {
44-
apiKeysPage.findTitle().should('contain.text', 'API Keys');
45-
apiKeysPage
46-
.findDescription()
47-
.should(
48-
'contain.text',
49-
'Manage personal API keys that can be used to access AI asset endpoints.',
50-
);
51-
52-
apiKeysPage.findTable().should('exist');
53-
apiKeysPage.findRows().should('have.length', 4);
54-
55-
const ciPipelineRow = apiKeysPage.getRow('ci-pipeline');
56-
ciPipelineRow.findName().should('contain.text', 'ci-pipeline');
57-
ciPipelineRow
58-
.findDescription()
59-
.should('contain.text', 'API key for CI/CD pipeline automation');
60-
ciPipelineRow.findStatus().should('contain.text', 'Active');
61-
ciPipelineRow.findCreationDate().should('contain.text', 'Jan 11, 2026');
62-
ciPipelineRow.findExpirationDate().should('contain.text', 'Jan 18, 2026');
63-
apiKeysPage.findTable().should('exist');
64-
apiKeysPage.findRows().should('have.length', 4);
65-
});
50+
it('should display the API keys table page', () => {
51+
apiKeysPage.findTitle().should('contain.text', 'API Keys');
52+
apiKeysPage
53+
.findDescription()
54+
.should(
55+
'contain.text',
56+
'Manage personal API keys that can be used to access AI asset endpoints.',
57+
);
58+
59+
apiKeysPage.findTable().should('exist');
60+
apiKeysPage.findRows().should('have.length', 4);
61+
62+
const ciPipelineRow = apiKeysPage.getRow('ci-pipeline');
63+
ciPipelineRow.findName().should('contain.text', 'ci-pipeline');
64+
ciPipelineRow.findDescription().should('contain.text', 'API key for CI/CD pipeline automation');
65+
ciPipelineRow.findStatus().should('contain.text', 'Active');
66+
ciPipelineRow.findCreationDate().should('contain.text', 'Jan 11, 2026');
67+
ciPipelineRow.findExpirationDate().should('contain.text', 'Jan 18, 2026');
68+
apiKeysPage.findTable().should('exist');
69+
apiKeysPage.findRows().should('have.length', 4);
70+
});
6671

67-
it('should bulk revoke all API keys', () => {
68-
apiKeysPage.findTitle().should('contain.text', 'API Keys');
69-
apiKeysPage.findActionsToggle().click();
70-
apiKeysPage.findRevokeAllAPIKeysAction().click();
72+
it('should bulk revoke all API keys', () => {
73+
apiKeysPage.findTitle().should('contain.text', 'API Keys');
74+
apiKeysPage.findActionsToggle().click();
75+
apiKeysPage.findRevokeAllAPIKeysAction().click();
7176

72-
bulkRevokeAPIKeyModal.shouldBeOpen();
73-
bulkRevokeAPIKeyModal.findRevokeButton().should('be.disabled');
74-
bulkRevokeAPIKeyModal.findRevokeConfirmationInput().type('incorrect');
75-
bulkRevokeAPIKeyModal.findRevokeButton().should('be.disabled');
76-
bulkRevokeAPIKeyModal.findRevokeConfirmationInput().clear().type('revoke');
77-
bulkRevokeAPIKeyModal.findRevokeButton().should('be.enabled');
77+
bulkRevokeAPIKeyModal.shouldBeOpen();
78+
bulkRevokeAPIKeyModal.findRevokeButton().should('be.disabled');
79+
bulkRevokeAPIKeyModal.findRevokeConfirmationInput().type('incorrect');
80+
bulkRevokeAPIKeyModal.findRevokeButton().should('be.disabled');
81+
bulkRevokeAPIKeyModal.findRevokeConfirmationInput().clear().type('revoke');
82+
bulkRevokeAPIKeyModal.findRevokeButton().should('be.enabled');
7883

79-
cy.interceptOdh('POST /maas/api/v1/api-keys/bulk-revoke', {
80-
data: {
81-
revokedCount: 4,
82-
message: 'All API keys revoked',
83-
},
84-
}).as('deleteAllApiKeys');
84+
cy.interceptOdh('POST /maas/api/v1/api-keys/bulk-revoke', {
85+
data: {
86+
revokedCount: 4,
87+
message: 'All API keys revoked',
88+
},
89+
}).as('deleteAllApiKeys');
8590

86-
revokeAPIKeyModal.findRevokeButton().click();
87-
apiKeysPage.findEmptyState().should('exist');
91+
revokeAPIKeyModal.findRevokeAllButton().click();
8892

89-
cy.wait('@deleteAllApiKeys').then((interception) => {
90-
expect(interception.response?.statusCode).to.eq(200);
91-
});
93+
cy.wait('@deleteAllApiKeys').then((interception) => {
94+
expect(interception.response?.statusCode).to.eq(200);
9295
});
96+
});
9397

94-
it.skip('should revoke api keys', () => {
95-
apiKeysPage.findTitle().should('contain.text', 'API Keys');
96-
apiKeysPage.findActionsToggle().click();
97-
apiKeysPage.findRevokeAllAPIKeysAction().click();
98-
99-
revokeAPIKeyModal.shouldBeOpen();
100-
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
101-
revokeAPIKeyModal.findRevokeConfirmationInput().type('incorrect');
102-
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
103-
revokeAPIKeyModal.findRevokeConfirmationInput().clear().type('revoke');
104-
revokeAPIKeyModal.findRevokeButton().should('be.enabled');
105-
106-
// TODO: fix api calls after uncommenting
107-
// cy.interceptOdh('POST /maas/api/v1/api-keys/bulk-revoke', { data: null }).as('deleteAllApiKeys');
108-
// cy.interceptOdh('GET /maas/api/v1/api-keys', {
109-
// data: [],
110-
// }).as('getApiKeysAfterDelete');
111-
112-
revokeAPIKeyModal.findRevokeButton().click();
113-
apiKeysPage.findEmptyState().should('exist');
114-
115-
cy.wait('@deleteAllApiKeys').then((interception) => {
116-
expect(interception.response?.statusCode).to.eq(200);
117-
});
118-
});
98+
it('should revoke a specific API key', () => {
99+
apiKeysPage.findTitle().should('contain.text', 'API Keys');
100+
apiKeysPage.getRow('ci-pipeline').findKebabAction('Revoke API key').click();
101+
102+
revokeAPIKeyModal.shouldBeOpen();
103+
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
104+
revokeAPIKeyModal.findRevokeConfirmationInput().type('incorrect');
105+
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
106+
revokeAPIKeyModal.findRevokeConfirmationInput().clear().type('ci-pipeline');
107+
revokeAPIKeyModal.findRevokeButton().should('be.enabled');
119108

120-
it('should revoke a specific API key', () => {
121-
apiKeysPage.findTitle().should('contain.text', 'API Keys');
122-
apiKeysPage.getRow('ci-pipeline').findKebabAction('Revoke API key').click();
123-
124-
revokeAPIKeyModal.shouldBeOpen();
125-
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
126-
revokeAPIKeyModal.findRevokeConfirmationInput().type('incorrect');
127-
revokeAPIKeyModal.findRevokeButton().should('be.disabled');
128-
revokeAPIKeyModal.findRevokeConfirmationInput().clear().type('ci-pipeline');
129-
revokeAPIKeyModal.findRevokeButton().should('be.enabled');
130-
131-
cy.interceptOdh(
132-
'DELETE /maas/api/v1/api-keys/:id',
133-
{ path: { id: 'key-ci-pipeline-003' } },
134-
{
135-
data: {
136-
id: 'key-ci-pipeline-003',
137-
name: 'ci-pipeline',
138-
description: 'API key for CI/CD pipeline automation',
139-
status: 'revoked',
140-
creationDate: '2026-01-11T11:54:34.521671447-05:00',
141-
},
109+
cy.interceptOdh(
110+
'DELETE /maas/api/v1/api-keys/:id',
111+
{ path: { id: 'key-ci-pipeline-003' } },
112+
{
113+
data: {
114+
id: 'key-ci-pipeline-003',
115+
name: 'ci-pipeline',
116+
description: 'API key for CI/CD pipeline automation',
117+
status: 'revoked',
118+
creationDate: '2026-01-11T11:54:34.521671447-05:00',
142119
},
143-
).as('deleteApiKey');
120+
},
121+
).as('deleteApiKey');
144122

145-
revokeAPIKeyModal.findRevokeButton().click();
123+
revokeAPIKeyModal.findRevokeButton().click();
146124

147-
cy.wait('@deleteApiKey').then((interception) => {
148-
expect(interception.response?.statusCode).to.eq(200);
149-
});
125+
cy.wait('@deleteApiKey').then((interception) => {
126+
expect(interception.response?.statusCode).to.eq(200);
150127
});
128+
});
151129

152-
it('should create a new API key', () => {
153-
const now = new Date(2026, 0, 14).getTime(); // January 14, 2026
154-
// Set the clock to the same day every time so the expiration date is always the same
155-
cy.clock(now);
156-
157-
cy.interceptOdh('POST /maas/api/v1/api-keys', {
158-
data: mockCreateAPIKeyResponse(),
159-
}).as('createApiKey');
160-
161-
apiKeysPage.findCreateApiKeyButton().click();
162-
createApiKeyModal.shouldBeOpen();
163-
createApiKeyModal.findNameInput().type('production-backend');
164-
createApiKeyModal.findDescriptionInput().type('Production API key for backend service');
165-
createApiKeyModal.findExpirationDateInput().type('2026-01-20');
166-
createApiKeyModal.findCreateButton().click();
167-
cy.wait('@createApiKey').then((interception) => {
168-
expect(interception.response?.body?.data).to.include({
169-
name: 'production-backend',
170-
expiresAt: '2026-01-20T11:54:34.521671447-05:00',
171-
});
130+
it('should create a new API key', () => {
131+
const now = new Date(2026, 0, 14).getTime(); // January 14, 2026
132+
// Set the clock to the same day every time so the expiration date is always the same
133+
cy.clock(now);
134+
135+
cy.interceptOdh('POST /maas/api/v1/api-keys', {
136+
data: mockCreateAPIKeyResponse(),
137+
}).as('createApiKey');
138+
139+
apiKeysPage.findCreateApiKeyButton().click();
140+
createApiKeyModal.shouldBeOpen();
141+
createApiKeyModal.findNameInput().type('production-backend');
142+
createApiKeyModal.findDescriptionInput().type('Production API key for backend service');
143+
createApiKeyModal.findExpirationDateInput().type('2026-01-20');
144+
createApiKeyModal.findCreateButton().click();
145+
cy.wait('@createApiKey').then((interception) => {
146+
expect(interception.response?.body?.data).to.include({
147+
name: 'production-backend',
148+
expiresAt: '2026-01-20T11:54:34.521671447-05:00',
172149
});
173-
174-
copyApiKeyModal.shouldBeOpen();
175-
// Verify the token is displayed correctly in the ClipboardCopy input
176-
copyApiKeyModal.findApiKeyTokenInput().should('have.value', mockCreateAPIKeyResponse().key);
177-
copyApiKeyModal.findApiKeyName().should('contain.text', 'production-backend');
178-
copyApiKeyModal.findApiKeyExpirationDate().should('contain.text', '2026-01-20');
179150
});
151+
152+
copyApiKeyModal.shouldBeOpen();
153+
// Verify the token is displayed correctly in the ClipboardCopy input
154+
copyApiKeyModal.findApiKeyTokenInput().should('have.value', mockCreateAPIKeyResponse().key);
155+
copyApiKeyModal.findApiKeyName().should('contain.text', 'production-backend');
156+
copyApiKeyModal.findApiKeyExpirationDate().should('contain.text', '2026-01-20');
180157
});
181158
});

packages/gen-ai/frontend/src/app/AIAssets/components/EndpointDetailModal.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
} from '@patternfly/react-core';
1919
import { InfoCircleIcon } from '@patternfly/react-icons';
2020
import { Link } from 'react-router-dom';
21+
import { genRandomChars } from 'mod-arch-shared';
2122
import { AIModel } from '~/app/types';
2223
import useGenerateMaaSToken from '~/app/hooks/useGenerateMaaSToken';
2324
import { copyToClipboardWithTracking } from '~/app/utilities/utils';
@@ -33,7 +34,9 @@ const EndpointDetailModal: React.FC<EndpointDetailModalProps> = ({ model, onClos
3334
const hasInternal = !!model.internalEndpoint;
3435
const isMaaS = model.model_source_type === 'maas';
3536

36-
const { isGenerating, tokenData, error, generateToken, resetToken } = useGenerateMaaSToken();
37+
const { isGenerating, tokenData, error, generateToken, resetToken } = useGenerateMaaSToken(
38+
`maas-token-${genRandomChars()}`,
39+
);
3740

3841
const handleEndpointCopy = (endpoint: string, endpointType: 'external' | 'internal') =>
3942
copyToClipboardWithTracking(endpoint, 'Available Endpoints Endpoint Copied', {

0 commit comments

Comments
 (0)