Skip to content

Commit 4cc265c

Browse files
Add E2E Cypress negative cases tests for projects.cy.ts
Signed-off-by: Lukasz Gryglicki <[email protected]> Assisted by [OpenAI](https://platform.openai.com/) Assisted by [GitHub Copilot](https://github.com/features/copilot)
1 parent 5528fcf commit 4cc265c

File tree

1 file changed

+263
-0
lines changed

1 file changed

+263
-0
lines changed

tests/functional/cypress/e2e/v4/projects.cy.ts

Lines changed: 263 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import {
22
validateApiResponse,
33
validate_200_Status,
4+
validate_401_Status,
5+
validate_expected_status,
46
getTokenKey,
57
getAPIBaseURL,
68
getXACLHeader,
@@ -30,6 +32,8 @@ describe('To Validate & get projects Activity Callback via API call', function (
3032
let projectName = appConfig.projectName;
3133
let projectName2 = appConfig.projectName2;
3234
let allowFail: boolean = !(Cypress.env('ALLOW_FAIL') === 1);
35+
const timeout = 180000;
36+
const local = Cypress.env('LOCAL');
3337

3438
before(() => {
3539
if (bearerToken == null) {
@@ -175,4 +179,263 @@ describe('To Validate & get projects Activity Callback via API call', function (
175179
cy.log(jsonResponse);
176180
});
177181
});
182+
183+
// ========================= Expected failures (projects) =========================
184+
describe('Expected failures', () => {
185+
it('Returns 401 for all Project APIs when called without token', () => {
186+
const exampleProjectSFID = 'a09P000000DsNH2IAN';
187+
const exampleFoundationSFID = 'a09P000000DsNGsIAN';
188+
const exampleExternalID = 'sample-external-id';
189+
const exampleProjectName = 'sample-project';
190+
const exampleProjectID = 'sample-project-id';
191+
192+
const requests = [
193+
// GET /project
194+
{
195+
method: 'GET',
196+
url: `${claEndpoint}`,
197+
},
198+
// PUT /project
199+
{
200+
method: 'PUT',
201+
url: `${claEndpoint}`,
202+
body: {
203+
project_name: 'test-project',
204+
project_description: 'test description',
205+
},
206+
},
207+
// GET /project/{projectSfdcId}
208+
{
209+
method: 'GET',
210+
url: `${claEndpoint}/${exampleProjectSFID}`,
211+
},
212+
// DELETE /project/{projectSfdcId}
213+
{
214+
method: 'DELETE',
215+
url: `${claEndpoint}/${exampleProjectSFID}`,
216+
},
217+
// GET /project/enabled/{foundationSFID}
218+
{
219+
method: 'GET',
220+
url: `${claEndpoint}/enabled/${exampleFoundationSFID}`,
221+
},
222+
// GET /project/external/{externalID}
223+
{
224+
method: 'GET',
225+
url: `${claEndpoint}/external/${exampleExternalID}`,
226+
},
227+
// GET /project/name/{projectName}
228+
{
229+
method: 'GET',
230+
url: `${claEndpoint}/name/${exampleProjectName}`,
231+
},
232+
// GET /project-info/{projectSFID}
233+
{
234+
method: 'GET',
235+
url: `${getAPIBaseURL('v4')}project-info/${exampleProjectSFID}`,
236+
},
237+
];
238+
239+
cy.wrap(requests).each((req: any) => {
240+
return cy
241+
.request({
242+
method: req.method as any,
243+
url: req.url,
244+
body: req.body,
245+
failOnStatusCode: false, // expect 401 without token
246+
timeout,
247+
})
248+
.then((response) => {
249+
return cy.logJson('401 response (projects)', response).then(() => {
250+
expect(response.status).to.eq(401);
251+
252+
// Projects API returns JSON with code/message in both local and remote environments
253+
if (response.body && typeof response.body === 'object' && response.body.code === 401) {
254+
expect(response.body).to.have.property('code', 401);
255+
expect(response.body).to.have.property('message');
256+
expect(response.body.message).to.contain('unauthenticated');
257+
} else if (local) {
258+
// Some endpoints in local might return plain text
259+
if (typeof response.body === 'string') {
260+
expect(response.body).to.contain('unauthenticated');
261+
} else {
262+
validate_401_Status(response, local);
263+
}
264+
} else {
265+
// Remote environment - use standard validation
266+
validate_401_Status(response, local);
267+
}
268+
});
269+
});
270+
});
271+
});
272+
273+
it('Returns errors due to missing or malformed parameters for Project APIs', function () {
274+
const claBaseEndpoint = getAPIBaseURL('v4');
275+
const exampleProjectSFID = 'a09P000000DsNH2IAN';
276+
const exampleFoundationSFID = 'a09P000000DsNGsIAN';
277+
const badProjectSFID = 'bad';
278+
const badProjectSFID2 = '123';
279+
const badFoundationSFID = 'bad';
280+
const nonExistentProjectName = 'non-existent-project-name-12345';
281+
282+
const defaultHeaders = getXACLHeader();
283+
const defaultAuth = { bearer: bearerToken };
284+
285+
const cases: Array<{
286+
title: string;
287+
method: 'GET' | 'POST' | 'PUT' | 'DELETE';
288+
url: string;
289+
body?: any;
290+
mode?: 'auth' | 'noauth' | 'either';
291+
// when running locally
292+
expectedStatusLocal?: number;
293+
expectedCodeLocal?: number;
294+
expectedMessageLocal?: string;
295+
expectedMessageContainsLocal?: boolean;
296+
// when running against dev via ACS & API-gw
297+
expectedStatusRemote?: number;
298+
expectedCodeRemote?: number;
299+
expectedMessageRemote?: string;
300+
expectedMessageContainsRemote?: boolean;
301+
// if the same
302+
expectedStatus?: number;
303+
expectedCode?: number;
304+
expectedMessage?: string;
305+
expectedMessageContains?: boolean;
306+
}> = [
307+
// --- PUT /project (missing required fields) ---
308+
{
309+
title: 'PUT /project with missing project_name',
310+
method: 'PUT',
311+
url: `${claBaseEndpoint}project`,
312+
body: {
313+
project_description: 'test description',
314+
},
315+
expectedStatus: 404,
316+
// Don't check code for local 404 since it might not have one
317+
expectedMessage: 'ValidationException',
318+
expectedMessageContains: true,
319+
},
320+
321+
// --- Path parameter validation ---
322+
{
323+
title: 'GET /project/{projectSfdcId} with malformed projectSFID (too short)',
324+
method: 'GET',
325+
url: `${claBaseEndpoint}project/${badProjectSFID}`,
326+
expectedStatus: 400,
327+
expectedMessage: 'cla group bad not found',
328+
expectedMessageContains: true,
329+
},
330+
{
331+
title: 'GET /project/{projectSfdcId} with malformed projectSFID (bad format)',
332+
method: 'GET',
333+
url: `${claBaseEndpoint}project/${badProjectSFID2}`,
334+
expectedStatus: 400,
335+
expectedMessage: 'cla group 123 not found',
336+
expectedMessageContains: true,
337+
},
338+
{
339+
title: 'DELETE /project/{projectSfdcId} with malformed projectSFID (too short)',
340+
method: 'DELETE',
341+
url: `${claBaseEndpoint}project/${badProjectSFID}`,
342+
expectedStatus: 400,
343+
expectedMessage: 'cla group bad not found',
344+
expectedMessageContains: true,
345+
},
346+
{
347+
title: 'GET /project/enabled/{foundationSFID} with malformed foundationSFID',
348+
method: 'GET',
349+
url: `${claBaseEndpoint}project/enabled/${badFoundationSFID}`,
350+
expectedStatus: 422,
351+
expectedCode: 604,
352+
expectedMessage: 'foundationSFID in path should be at least 15 chars long',
353+
expectedMessageContains: false,
354+
},
355+
{
356+
title: 'GET /project/name/{projectName} with non-existent project name',
357+
method: 'GET',
358+
url: `${claBaseEndpoint}project/name/${nonExistentProjectName}`,
359+
expectedStatus: 404,
360+
// No message check because body is empty
361+
},
362+
{
363+
title: 'GET /project-info/{projectSFID} with malformed projectSFID',
364+
method: 'GET',
365+
url: `${claBaseEndpoint}project-info/${badProjectSFID}`,
366+
expectedStatusLocal: 422,
367+
expectedStatusRemote: 401,
368+
expectedCodeLocal: 604,
369+
expectedCodeRemote: 401,
370+
expectedMessageLocal: 'projectSFID in path should be at least 15 chars long',
371+
expectedMessageRemote: 'unauthenticated for invalid credentials',
372+
expectedMessageContainsLocal: false,
373+
expectedMessageContainsRemote: false,
374+
},
375+
376+
// (Sanity) valid-looking parameters should succeed (or at least get past validation)
377+
{
378+
title: 'GET /project with valid parameters',
379+
method: 'GET',
380+
url: `${claBaseEndpoint}project`,
381+
expectedStatus: 200,
382+
},
383+
{
384+
title: 'GET /project/{projectSfdcId} with valid projectSFID',
385+
method: 'GET',
386+
url: `${claBaseEndpoint}project/${exampleProjectSFID}`,
387+
expectedStatus: 400,
388+
expectedMessage: 'cla group a09P000000DsNH2IAN not found',
389+
expectedMessageContains: true,
390+
},
391+
{
392+
title: 'GET /project/enabled/{foundationSFID} with valid foundationSFID',
393+
method: 'GET',
394+
url: `${claBaseEndpoint}project/enabled/${exampleFoundationSFID}`,
395+
expectedStatus: 200,
396+
},
397+
{
398+
title: 'GET /project-info/{projectSFID} with valid projectSFID',
399+
method: 'GET',
400+
url: `${claBaseEndpoint}project-info/${exampleProjectSFID}`,
401+
expectedStatusLocal: 200,
402+
expectedStatusRemote: 401,
403+
expectedCodeRemote: 401,
404+
expectedMessageRemote: 'unauthenticated for invalid credentials',
405+
expectedMessageContainsRemote: false,
406+
},
407+
];
408+
409+
cy.wrap(cases).each((c: any) => {
410+
cy.task('log', `--> ${c.title} | ${c.method} ${c.url}`);
411+
const opts: any = {
412+
method: c.method,
413+
url: c.url,
414+
headers: defaultHeaders,
415+
auth: defaultAuth,
416+
failOnStatusCode: false,
417+
timeout,
418+
};
419+
if (c.body) opts.body = c.body;
420+
421+
cy.request(opts).then((response) => {
422+
return cy.logJson('response', response).then(() => {
423+
const es = local
424+
? (c.expectedStatusLocal ?? c.expectedStatus)
425+
: (c.expectedStatusRemote ?? c.expectedStatus);
426+
const ec = local ? (c.expectedCodeLocal ?? c.expectedCode) : (c.expectedCodeRemote ?? c.expectedCode);
427+
const em = local
428+
? (c.expectedMessageLocal ?? c.expectedMessage)
429+
: (c.expectedMessageRemote ?? c.expectedMessage);
430+
const emc = local
431+
? (c.expectedMessageContainsLocal ?? c.expectedMessageContains)
432+
: (c.expectedMessageContainsRemote ?? c.expectedMessageContains);
433+
434+
cy.task('log', ` --> expected ${es}, ${ec}, '${em}' (contains? ${emc})`);
435+
validate_expected_status(response, es, ec, em, emc);
436+
});
437+
});
438+
});
439+
});
440+
});
178441
});

0 commit comments

Comments
 (0)