diff --git a/src/api/routes/linkry.ts b/src/api/routes/linkry.ts index a8aa3773..3b5d2437 100644 --- a/src/api/routes/linkry.ts +++ b/src/api/routes/linkry.ts @@ -495,7 +495,9 @@ const linkryRoutes: FastifyPluginAsync = async (fastify, _options) => { setUserGroups, ); if (mutualGroups.size == 0) { - throw new NotFoundError({ endpointName: request.url }); + throw new UnauthorizedError({ + message: "You have not been delegated access.", + }); } } return reply.status(200).send(item); diff --git a/tests/unit/auth.test.ts b/tests/unit/auth.test.ts index 1fa46cdc..ed4bfb9a 100644 --- a/tests/unit/auth.test.ts +++ b/tests/unit/auth.test.ts @@ -19,7 +19,7 @@ const ddbMock = mockClient(SecretsManagerClient); const app = await init(); const jwt_secret = secretObject["jwt_key"]; -export function createJwt(date?: Date, group?: string, email?: string) { +export function createJwt(date?: Date, groups?: string[], email?: string) { let modifiedPayload = { ...jwtPayload, email: email || jwtPayload.email, @@ -36,8 +36,8 @@ export function createJwt(date?: Date, group?: string, email?: string) { }; } - if (group) { - modifiedPayload.groups = [group]; + if (groups) { + modifiedPayload.groups = groups; } return jwt.sign(modifiedPayload, jwt_secret, { algorithm: "HS256" }); } diff --git a/tests/unit/eventPost.test.ts b/tests/unit/eventPost.test.ts index 9973b857..5658d2f8 100644 --- a/tests/unit/eventPost.test.ts +++ b/tests/unit/eventPost.test.ts @@ -48,7 +48,7 @@ test("Sad path: Not authenticated", async () => { test("Sad path: Authenticated but not authorized", async () => { await app.ready(); - const testJwt = createJwt(undefined, "1"); + const testJwt = createJwt(undefined, ["1"]); const response = await supertest(app.server) .post("/api/v1/events") .set("Authorization", `Bearer ${testJwt}`) @@ -66,7 +66,7 @@ test("Sad path: Authenticated but not authorized", async () => { }); test("Sad path: Prevent empty body request", async () => { await app.ready(); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); const response = await supertest(app.server) .post("/api/v1/events") .set("Authorization", `Bearer ${testJwt}`) @@ -227,7 +227,7 @@ describe("ETag Lifecycle Tests", () => { Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. Check initial etag for all events is 0 const initialAllResponse = await app.inject({ @@ -313,7 +313,7 @@ describe("ETag Lifecycle Tests", () => { Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. Create an event const eventResponse = await supertest(app.server) @@ -413,7 +413,7 @@ describe("ETag Lifecycle Tests", () => { Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. Check initial etag for all events is 0 const initialAllResponse = await app.inject({ diff --git a/tests/unit/events.test.ts b/tests/unit/events.test.ts index 75f5256f..b6478a8b 100644 --- a/tests/unit/events.test.ts +++ b/tests/unit/events.test.ts @@ -50,7 +50,7 @@ test("ETag should increment after event creation", async () => { Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. Check initial etag for all events is 0 const initialAllResponse = await app.inject({ @@ -138,7 +138,7 @@ test("Should return 304 Not Modified when If-None-Match header matches ETag", as Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. First GET request to establish ETag const initialResponse = await app.inject({ @@ -188,7 +188,7 @@ test("Should return 304 Not Modified when If-None-Match header matches quoted ET Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. First GET request to establish ETag const initialResponse = await app.inject({ @@ -238,7 +238,7 @@ test("Should NOT return 304 when ETag has changed", async () => { Items: [], }); - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); // 1. Initial GET to establish ETag const initialResponse = await app.inject({ @@ -313,7 +313,7 @@ test("Should handle 304 responses for individual event endpoints", async () => { ddbMock.on(PutItemCommand).resolves({}); // Create an event - const testJwt = createJwt(undefined, "0"); + const testJwt = createJwt(undefined, ["0"]); const eventResponse = await supertest(app.server) .post("/api/v1/events") .set("Authorization", `Bearer ${testJwt}`) diff --git a/tests/unit/linkry.test.ts b/tests/unit/linkry.test.ts index f1aa9333..899f4971 100644 --- a/tests/unit/linkry.test.ts +++ b/tests/unit/linkry.test.ts @@ -1,9 +1,10 @@ -import { expect, test, vi } from "vitest"; +import { beforeEach, expect, test, vi } from "vitest"; import { DynamoDBClient, ScanCommand, QueryCommand, TransactWriteItemsCommand, + TransactionCanceledException, } from "@aws-sdk/client-dynamodb"; import { mockClient } from "aws-sdk-client-mock"; import init from "../../src/api/index.js"; @@ -15,6 +16,8 @@ import { import { secretJson, secretObject } from "./secret.testdata.js"; import supertest from "supertest"; +import { dynamoTableData } from "./mockLinkryData.testdata.js"; +import { genericConfig } from "../../src/common/config.js"; const ddbMock = mockClient(DynamoDBClient); const smMock = mockClient(SecretsManagerClient); @@ -48,17 +51,28 @@ smMock.on(GetSecretValueCommand).resolves({ SecretString: secretJson, }); -const testJwt = createJwt(undefined, "0", "test@gmail.com"); +const adminJwt = createJwt(undefined, ["LINKS_ADMIN"], "test@gmail.com"); -test("Happy path: Fetch all linkry redirects with proper roles", async () => { - ddbMock.on(QueryCommand).resolves({ - Items: [], - }); +beforeEach(() => { + ddbMock.reset(); +}); +// Get Link +beforeEach(() => { + ddbMock.reset(); +}); +// Get Link +test("Happy path: Fetch all linkry redirects with admin roles", async () => { ddbMock - .on(ScanCommand) - .resolvesOnce({ + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ Items: [], + }); + + ddbMock + .on(ScanCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolvesOnce({ + Items: dynamoTableData, }) .rejects(); @@ -66,21 +80,63 @@ test("Happy path: Fetch all linkry redirects with proper roles", async () => { method: "GET", url: "/api/v1/linkry/redir", headers: { - Authorization: `Bearer ${testJwt}`, + Authorization: `Bearer ${adminJwt}`, }, }); expect(response.statusCode).toBe(200); + let body = JSON.parse(response.body); + expect(body.ownedLinks).toEqual([]); }); -test("Make sure that a DB scan is only called for admins", async () => { - const testManagerJwt = createJwt(undefined, "999", "test@gmail.com"); +test("Happy path: Fetch all linkry redirects with admin roles owned", async () => { + const adminOwnedJwt = createJwt( + undefined, + ["LINKS_ADMIN"], + "bob@illinois.edu", + ); + + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock + .on(ScanCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolvesOnce({ + Items: dynamoTableData, + }) + .rejects(); - ddbMock.on(QueryCommand).resolves({ - Items: [], + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir", + headers: { + Authorization: `Bearer ${adminOwnedJwt}`, + }, }); + expect(response.statusCode).toBe(200); + let body = JSON.parse(response.body); + expect(body.delegatedLinks).toEqual([]); +}); - ddbMock.on(ScanCommand).rejects(); +test("Make sure that a DB scan is only called for admins", async () => { + const testManagerJwt = createJwt( + undefined, + ["LINKS_MANAGER"], + "test@gmail.com", + ); + + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + ddbMock + .on(ScanCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .rejects(); const response = await app.inject({ method: "GET", @@ -93,23 +149,456 @@ test("Make sure that a DB scan is only called for admins", async () => { expect(response.statusCode).toBe(200); }); -test("Happy path: Create a new linkry redirect", async () => { - ddbMock.on(QueryCommand).resolves({ - Items: [], +test("Unhappy path: Fetch all linkry redirects database error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .rejects(); + + ddbMock + .on(ScanCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .rejects(); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir", + headers: { + Authorization: `Bearer ${adminJwt}`, + }, }); + expect(response.statusCode).toBe(500); + let body = JSON.parse(response.body); + expect(body.name).toEqual("DatabaseFetchError"); +}); + +//Create/Edit Link +test("Happy path: Create/Edit linkry redirect success", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "940e4f9e-6891-4e28-9e29-148798495cdb"], + "alice@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + ddbMock.on(TransactWriteItemsCommand).resolves({}); const payload = { - access: [], + access: ["940e4f9e-6891-4e28-9e29-148798495cdb", "newAccessGroupid1123"], redirect: "https://www.acm.illinois.edu/", - slug: "acm-test-slug", + slug: "WlQDmu", }; const response = await supertest(app.server) .post("/api/v1/linkry/redir") - .set("Authorization", `Bearer ${testJwt}`) + .set("Authorization", `Bearer ${userJwt}`) .send(payload); expect(response.statusCode).toBe(201); }); + +test("Unhappy path: Edit linkry redirect not authorized", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "IncorrectGroupID233"], + "alice@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const payload = { + access: [], + redirect: "https://www.acm.illinois.edu/", + slug: "WlQDmu", + }; + + const response = await supertest(app.server) + .post("/api/v1/linkry/redir") + .set("Authorization", `Bearer ${userJwt}`) + .send(payload); + + expect(response.statusCode).toBe(401); + expect(response.body.name).toEqual("UnauthorizedError"); +}); + +test("Unhappy path: Edit linkry time stamp mismatch", async () => { + const userJwt = createJwt(undefined, ["LINKS_ADMIN"], "alice@illinois.edu"); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [ + ...dynamoTableData, + { + slug: { + S: "WlQDmu", + }, + access: { + S: "GROUP#940e4f9e-6891-4e28-9e29-148798495cdb", + }, + createdAt: { + S: "2030-04-18T18:36:50.706Z", + }, + updatedAt: { + S: "2030-04-18T18:37:40.681Z", + }, + }, + ], + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const payload = { + access: [], + redirect: "https://www.acm.illinois.edu/", + slug: "WlQDmu", + }; + + const response = await supertest(app.server) + .post("/api/v1/linkry/redir") + .set("Authorization", `Bearer ${userJwt}`) + .send(payload); + + expect(response.statusCode).toBe(400); + expect(response.body.name).toEqual("ValidationError"); +}); + +vi.spyOn(app, "hasRoute").mockImplementation(({ url, method }) => { + return url === "/reserved" && method === "GET"; +}); + +test("Unhappy path: Linkry slug is reserved", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const payload = { + access: [], + redirect: "https://www.acm.illinois.edu/", + slug: "reserved", + }; + + const response = await supertest(app.server) + .post("/api/v1/linkry/redir") + .set("Authorization", `Bearer ${adminJwt}`) + .send(payload); + + expect(response.statusCode).toBe(400); + expect(response.body.name).toEqual("ValidationError"); + expect(response.body.message).toEqual( + `Slug reserved is reserved by the system.`, + ); +}); + +test("Unhappy path: TransactionCancelled error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + ddbMock.on(TransactWriteItemsCommand).rejects( + new TransactionCanceledException({ + $metadata: {}, + message: "Transaction intentionally cancelled", + CancellationReasons: [ + { + Code: "ConditionalCheckFailed", + Message: "Transaction intentionally cancelled", + }, + ], + }), + ); + + const payload = { + access: [], + redirect: "https://www.acm.illinois.edu/", + slug: "ABCDEFG", + }; + + const response = await supertest(app.server) + .post("/api/v1/linkry/redir") + .set("Authorization", `Bearer ${adminJwt}`) + .send(payload); + + expect(response.statusCode).toBe(400); + expect(response.body.name).toEqual("ValidationError"); + expect(response.body.message).toEqual( + "The record was modified by another process. Please try again.", + ); +}); + +test("Unhappy path: Database error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + ddbMock.on(TransactWriteItemsCommand).rejects(); + + const payload = { + access: [], + redirect: "https://www.acm.illinois.edu/", + slug: "ABCDEFG", + }; + + const response = await supertest(app.server) + .post("/api/v1/linkry/redir") + .set("Authorization", `Bearer ${adminJwt}`) + .send(payload); + + expect(response.statusCode).toBe(500); + expect(response.body.name).toEqual("DatabaseInsertError"); + expect(response.body.message).toEqual(`Failed to save data to DynamoDB.`); +}); + +//Delete Link +test("Happy path: Delete linkry success", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "940e4f9e-6891-4e28-9e29-148798495cdb"], + "alice@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const response = await supertest(app.server) + .delete("/api/v1/linkry/redir/WLQDmu") + .set("Authorization", `Bearer ${userJwt}`); + + expect(response.statusCode).toBe(200); +}); + +test("Unhappy path: Delete linkry slug not found/invalid", async () => { + const userJwt = createJwt(undefined, ["LINKS_MANAGER"], "alice@illinois.edu"); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const response = await supertest(app.server) + .delete("/api/v1/linkry/redir/invalid") + .set("Authorization", `Bearer ${userJwt}`); + + expect(response.statusCode).toBe(404); + expect(response.body.name).toEqual("NotFoundError"); +}); + +test("Unhappy path: Delete linkry Invalid Access", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "InvalidGroupId22"], + "alice@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock.on(TransactWriteItemsCommand).resolves({}); + + const response = await supertest(app.server) + .delete("/api/v1/linkry/redir/WLQDmu") + .set("Authorization", `Bearer ${userJwt}`); + + expect(response.statusCode).toBe(401); + expect(response.body.name).toEqual("UnauthorizedError"); +}); + +test("Unhappy path: TransactionCancelled error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock.on(TransactWriteItemsCommand).rejects( + new TransactionCanceledException({ + $metadata: {}, + message: "Transaction intentionally cancelled", + CancellationReasons: [ + { + Code: "ConditionalCheckFailed", + Message: "Transaction intentionally cancelled", + }, + ], + }), + ); + + const response = await supertest(app.server) + .delete("/api/v1/linkry/redir/WLQDmu") + .set("Authorization", `Bearer ${adminJwt}`); + + expect(response.statusCode).toBe(400); + expect(response.body.name).toEqual("ValidationError"); + expect(response.body.message).toEqual( + "The record was modified by another process. Please try again.", + ); +}); + +test("Unhappy path: Delete linkry Database Error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + ddbMock.on(TransactWriteItemsCommand).rejects(); + + const response = await supertest(app.server) + .delete("/api/v1/linkry/redir/WLQDmu") + .set("Authorization", `Bearer ${adminJwt}`); + + expect(response.statusCode).toBe(500); + expect(response.body.name).toEqual("DatabaseDeleteError"); +}); + +//Get Link by Slug + +test("Happy path: Get Delegated Link by Slug Correct Access", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "940e4f9e-6891-4e28-9e29-148798495cdb"], + "cloud@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir/WlQDmu", + headers: { + Authorization: `Bearer ${userJwt}`, + }, + }); + expect(response.statusCode).toBe(200); + let body = JSON.parse(response.body); + expect(body).toEqual({ + slug: "WlQDmu", + access: [ + "f8dfc4cf-456b-4da3-9053-f7fdeda5d5d6", + "940e4f9e-6891-4e28-9e29-148798495cdb", + ], + createdAt: "2025-04-18T18:36:50.706Z", + redirect: "https://www.gmaill.com", + updatedAt: "2025-04-18T18:37:40.681Z", + owner: "bob@illinois.edu", + }); +}); + +test("Happy path: Get Delegated Link by Slug Admin Access", async () => { + const userJwt = createJwt(undefined, ["LINKS_ADMIN"], "test@illinois.edu"); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir/WlQDmu", + headers: { + Authorization: `Bearer ${userJwt}`, + }, + }); + expect(response.statusCode).toBe(200); + let body = JSON.parse(response.body); + expect(body).toEqual({ + slug: "WlQDmu", + access: [ + "f8dfc4cf-456b-4da3-9053-f7fdeda5d5d6", + "940e4f9e-6891-4e28-9e29-148798495cdb", + ], + createdAt: "2025-04-18T18:36:50.706Z", + redirect: "https://www.gmaill.com", + updatedAt: "2025-04-18T18:37:40.681Z", + owner: "bob@illinois.edu", + }); +}); + +test("Unhappy path: Get Delegated Link by Slug Incorrect Access", async () => { + const userJwt = createJwt( + undefined, + ["LINKS_MANAGER", "NotValidGroupId222"], + "cloud@illinois.edu", + ); + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: dynamoTableData, + }); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir/WlQDmu", + headers: { + Authorization: `Bearer ${userJwt}`, + }, + }); + expect(response.statusCode).toBe(401); + let body = JSON.parse(response.body); + expect(body.name).toEqual("UnauthorizedError"); +}); + +test("Unhappy path: Get Delegated Link by Slug Not Found", async () => { + const userJwt = createJwt(undefined, ["LINKS_ADMIN"], "cloud@illinois.edu"); + + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .resolves({ + Items: [], + }); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir/WlQDmu", + headers: { + Authorization: `Bearer ${userJwt}`, + }, + }); + expect(response.statusCode).toBe(404); + let body = JSON.parse(response.body); + expect(body.name).toEqual("NotFoundError"); +}); + +test("Unhappy path: Get Delegated Link by Slug Database Error", async () => { + ddbMock + .on(QueryCommand, { TableName: genericConfig.LinkryDynamoTableName }) + .rejects(); + + const response = await app.inject({ + method: "GET", + url: "/api/v1/linkry/redir/WlQDmu", + headers: { + Authorization: `Bearer ${adminJwt}`, + }, + }); + + expect(response.statusCode).toBe(500); + let body = JSON.parse(response.body); + expect(body.name).toEqual("DatabaseFetchError"); +}); diff --git a/tests/unit/mockLinkryData.testdata.ts b/tests/unit/mockLinkryData.testdata.ts new file mode 100644 index 00000000..049779e4 --- /dev/null +++ b/tests/unit/mockLinkryData.testdata.ts @@ -0,0 +1,49 @@ +const linkry1 = { + slug: { + S: "WlQDmu", + }, + access: { + S: "OWNER#bob@illinois.edu", + }, + createdAt: { + S: "2025-04-18T18:36:50.706Z", + }, + redirect: { + S: "https://www.gmaill.com", + }, + updatedAt: { + S: "2025-04-18T18:37:40.681Z", + }, +}; +const linkry2 = { + slug: { + S: "WlQDmu", + }, + access: { + S: "GROUP#f8dfc4cf-456b-4da3-9053-f7fdeda5d5d6", + }, + createdAt: { + S: "2025-04-18T18:36:50.706Z", + }, + updatedAt: { + S: "2025-04-18T18:37:40.681Z", + }, +}; +const linkry3 = { + slug: { + S: "WlQDmu", + }, + access: { + S: "GROUP#940e4f9e-6891-4e28-9e29-148798495cdb", + }, + createdAt: { + S: "2025-04-18T18:36:50.706Z", + }, + updatedAt: { + S: "2025-04-18T18:37:40.681Z", + }, +}; + +const dynamoTableData = [linkry1, linkry2, linkry3]; + +export { dynamoTableData }; diff --git a/tests/unit/stripe.test.ts b/tests/unit/stripe.test.ts index 082b96e8..7c06f3ea 100644 --- a/tests/unit/stripe.test.ts +++ b/tests/unit/stripe.test.ts @@ -234,7 +234,7 @@ describe("Test Stripe link creation", async () => { }); const testJwt = createJwt( undefined, - "1", + ["1"], "infra-unit-test-stripeonly@acm.illinois.edu", ); const response = await supertest(app.server) diff --git a/tests/unit/tickets.test.ts b/tests/unit/tickets.test.ts index 2b1ab02a..c3e24f80 100644 --- a/tests/unit/tickets.test.ts +++ b/tests/unit/tickets.test.ts @@ -96,7 +96,7 @@ describe("Test getting ticketing + merch metadata", async () => { Items: ticketsMetadata as Record[], }) .rejects(); - const testJwt = createJwt(undefined, "scanner-only"); + const testJwt = createJwt(undefined, ["scanner-only"]); await app.ready(); const response = await supertest(app.server) .get("/api/v1/tickets") diff --git a/tests/unit/vitest.setup.ts b/tests/unit/vitest.setup.ts index 0d274eee..241eec49 100644 --- a/tests/unit/vitest.setup.ts +++ b/tests/unit/vitest.setup.ts @@ -43,9 +43,9 @@ vi.mock( const mockGroupRoles = { "0": allAppRoles, "1": [], - LINKS_ADMIN: [AppRoles.LINKS_ADMIN], "scanner-only": [AppRoles.TICKETS_SCANNER], - "999": [AppRoles.LINKS_MANAGER], + LINKS_ADMIN: [AppRoles.LINKS_ADMIN], + LINKS_MANAGER: [AppRoles.LINKS_MANAGER], }; return mockGroupRoles[groupId] || [];