diff --git a/src/constants/operationsResultsMessages.ts b/src/constants/operationsResultsMessages.ts index 0f72c6e..d4c44e8 100644 --- a/src/constants/operationsResultsMessages.ts +++ b/src/constants/operationsResultsMessages.ts @@ -41,13 +41,14 @@ const operationsResultsMessages = { "Your huggingface account was created successfully", failedHuggingfaceAccountCreation: "Failed to create Your huggingface account", failedHuggingfaceOAuthProcess: - "Huggingface OAuth callback failed to complate", + "Huggingface OAuth callback failed to complete", noHuggingfaceAccount: "You don't have a linked huggingface account", successullyDatasetUpload: "The dataset has been uploaded to the repository successfully", noLinkedDatasetRepository: "The dataset has no linked huggingface repository", successfulDatasetSyncWithRepository: "The Dataset has been synced with the repository successfully", + failedAccessTokenRefreshing: "Failed to refresh user's Huggingface access token" }; export default operationsResultsMessages; diff --git a/src/middlewares/createDatasetRepositoryInputValidator.ts b/src/middlewares/createDatasetRepositoryInputValidator.ts index cf97135..6a554b0 100644 --- a/src/middlewares/createDatasetRepositoryInputValidator.ts +++ b/src/middlewares/createDatasetRepositoryInputValidator.ts @@ -12,7 +12,10 @@ const datasetRepositorySchema = validationSchema({ export default function createDatasetRepositoryInputValidator(request: Req) { try { - request.json = datasetRepositorySchema.validate(request.json); + request.json = datasetRepositorySchema.validate({ + name: request.json.name, + license: request.json.license + }); } catch (e: any) { const validationErrors = e.errors as ValidationError["errors"]; return ErrorResponse({ validationErrors }, 403); diff --git a/src/services/huggingface/createDatasetRepository_service.ts b/src/services/huggingface/createDatasetRepository_service.ts index 9ea79cf..a9186e2 100644 --- a/src/services/huggingface/createDatasetRepository_service.ts +++ b/src/services/huggingface/createDatasetRepository_service.ts @@ -6,7 +6,7 @@ import type { CreateDatasetRepositoryInput } from "../../types/huggingface"; export default async function createDatasetRepository_service( this: HuggingfaceService, userId: string, - { name, license = "mit" }: CreateDatasetRepositoryInput + { name, license }: CreateDatasetRepositoryInput ) { const { isSuccess, diff --git a/src/services/huggingface/refreshHuggingfaceAccessToken_service.ts b/src/services/huggingface/refreshHuggingfaceAccessToken_service.ts index 7821cc9..5e6a035 100644 --- a/src/services/huggingface/refreshHuggingfaceAccessToken_service.ts +++ b/src/services/huggingface/refreshHuggingfaceAccessToken_service.ts @@ -1,6 +1,7 @@ import DatasetsModel from "../../models/DatasetsModel"; import ServiceOperationResult from "../../utilities/ServiceOperationResult"; import { refreshAccessToken } from "./huggingFaceOAuthTokenRequests"; +import operationsResultsMessages from "../../constants/operationsResultsMessages"; export default async function refreshHuggingfaceAccessToken_service( userId: string, @@ -34,6 +35,6 @@ export default async function refreshHuggingfaceAccessToken_service( } return ServiceOperationResult.failure( - "Failed to refresh user's Huggingface access token" + operationsResultsMessages.failedAccessTokenRefreshing ); } diff --git a/tests/e2e/huggingface/createDatasetRepository.test.ts b/tests/e2e/huggingface/createDatasetRepository.test.ts new file mode 100644 index 0000000..296f52e --- /dev/null +++ b/tests/e2e/huggingface/createDatasetRepository.test.ts @@ -0,0 +1,41 @@ +import { expect, describe, it, afterAll, mock } from "bun:test"; +import DatasetsModel from "../../../src/models/DatasetsModel"; +import { fakeUserHuggingfaceAccount } from "../../fake-data/fakeUserHuggingfaceAccount"; +import { request } from "../.."; + +const exampleDotCom = "https://example.com" + +mock.module("@huggingface/hub", () => { + return { + createRepo: async () => ({ + repoUrl: exampleDotCom + }), + }; +}); + +const path = "huggingface/datasets"; + +describe(`POST /${path}`, () => { + it("Should complete creating huggingface dataset repository process successfully", async () => { + await DatasetsModel.create({ + _id: process.env.TESTING_USER_ID, + huggingfaceAccount: fakeUserHuggingfaceAccount, + datasets: [] + }) + + const { resBody, status } = await request.POST(path, { + name: "username", + license: "mit" + }) + + expect(status).toBe(200) + expect(resBody.data).toMatchObject({ + repoUrl: exampleDotCom + }) + }); +}); + +afterAll(async () => { + mock.restore() + await DatasetsModel.deleteMany(); +}); diff --git a/tests/fake-data/fakeUserHuggingfaceAccount.ts b/tests/fake-data/fakeUserHuggingfaceAccount.ts new file mode 100644 index 0000000..282cb4b --- /dev/null +++ b/tests/fake-data/fakeUserHuggingfaceAccount.ts @@ -0,0 +1,8 @@ +export const fakeUserHuggingfaceAccount = { + accessToken: "anyaccessToken", + accessTokenExpiresIn: new Date().getTime() + 946334, + refreshToken: "anyrefreshToken", + username: "anyname", + emailVerified: true, +} + diff --git a/tests/integration/huggingface/createHuggingfaceAccount.test.ts b/tests/integration/huggingface/createHuggingfaceAccount.test.ts new file mode 100644 index 0000000..fd89a98 --- /dev/null +++ b/tests/integration/huggingface/createHuggingfaceAccount.test.ts @@ -0,0 +1,54 @@ +import { describe, expect, it, afterAll, beforeAll, mock } from "bun:test"; +import operationsResultsMessages from "../../../src/constants/operationsResultsMessages"; +import DatasetsModel from "../../../src/models/DatasetsModel"; +import databaseConnection from "../../../src/configurations/databaseConnection"; +import huggingfaceService from "../../../src/services/huggingface"; + +mock.module("@huggingface/hub", () => { + return { + whoAmI: async () => ({ + name: "username", + emailVerified: true + }), + }; +}); + +beforeAll(async () => { + await databaseConnection(); +}); + +describe("Test `createHuggingfaceAccount` service method", () => { + + const userHuggingfaceAccountCredentials = { + accessTokenExpiresIn: 57478, // in seconds + hfAccessToken: "n@!*&edVryme573@55n", + hfRefreshToken: "kjbgv&B^#65jbN(*Y#@1hj" + } + + it("Should fail to create the huggingface account for the user because the user is not existant", async () => { + const result = await huggingfaceService.createHuggingfaceAccount( + process.env.TESTING_USER_ID, + userHuggingfaceAccountCredentials + ); + + expect(result.isSuccess).toBeFalse(); + expect(result.message).toBe(operationsResultsMessages.failedHuggingfaceAccountCreation); + }); + + it("Should create the huggingface account for the user", async () => { + await DatasetsModel.create({ _id: process.env.TESTING_USER_ID }) + + const result = await huggingfaceService.createHuggingfaceAccount( + process.env.TESTING_USER_ID, + userHuggingfaceAccountCredentials + ); + + expect(result.isSuccess).toBeTrue(); + expect(result.result).toBe(operationsResultsMessages.successfulHuggingfaceAccountCreation); + }); +}); + +afterAll(async () => { + mock.restore() + await DatasetsModel.deleteMany(); +}); diff --git a/tests/integration/huggingface/getHuggingfaceAccount.test.ts b/tests/integration/huggingface/getHuggingfaceAccount.test.ts new file mode 100644 index 0000000..fe42425 --- /dev/null +++ b/tests/integration/huggingface/getHuggingfaceAccount.test.ts @@ -0,0 +1,45 @@ +import { describe, expect, it, afterAll, beforeAll } from "bun:test"; +import operationsResultsMessages from "../../../src/constants/operationsResultsMessages"; +import DatasetsModel from "../../../src/models/DatasetsModel"; +import databaseConnection from "../../../src/configurations/databaseConnection"; +import huggingfaceService from "../../../src/services/huggingface"; +import { fakeUserHuggingfaceAccount } from "../../fake-data/fakeUserHuggingfaceAccount"; + +beforeAll(async () => { + await databaseConnection(); +}); + +describe("Test `getHuggingfaceAccount` service method", () => { + it("Should not return user's huggingface account because it is not existant", async () => { + const result = await huggingfaceService.getHuggingfaceAccount( + process.env.TESTING_USER_ID + ); + + expect(result.isSuccess).toBeTrue(); + expect(result.message).toBe(operationsResultsMessages.noHuggingfaceAccount); + }); + + it("Should return user's huggingface account successfully", async () => { + await DatasetsModel.create({ + _id: process.env.TESTING_USER_ID, + huggingfaceAccount: fakeUserHuggingfaceAccount + }) + + const result = await huggingfaceService.getHuggingfaceAccount( + process.env.TESTING_USER_ID + ); + + expect(result.isSuccess).toBeTrue(); + expect(result.result).toMatchObject({ + accessToken: fakeUserHuggingfaceAccount.accessToken, + accessTokenExpiresIn: expect.any(Date), + refreshToken: fakeUserHuggingfaceAccount.refreshToken, + username: fakeUserHuggingfaceAccount.username, + emailVerified: fakeUserHuggingfaceAccount.emailVerified, + }); + }); +}); + +afterAll(async () => { + await DatasetsModel.deleteMany(); +}); diff --git a/tests/integration/huggingface/refreshHuggingfaceAccessToken.test.ts b/tests/integration/huggingface/refreshHuggingfaceAccessToken.test.ts new file mode 100644 index 0000000..e53d9cc --- /dev/null +++ b/tests/integration/huggingface/refreshHuggingfaceAccessToken.test.ts @@ -0,0 +1,69 @@ +import { describe, expect, it, afterAll, beforeAll, mock } from "bun:test"; +import databaseConnection from "../../../src/configurations/databaseConnection"; +import huggingfaceService from "../../../src/services/huggingface"; +import DatasetsModel from "../../../src/models/DatasetsModel"; +import operationsResultsMessages from "../../../src/constants/operationsResultsMessages"; +import { fakeUserHuggingfaceAccount } from "../../fake-data/fakeUserHuggingfaceAccount"; + +beforeAll(async () => { + await databaseConnection(); +}); + +const originalFetch = global.fetch + +describe("Test `refreshHuggingfaceAccessToken` service method", () => { + it('Should complete refreshing access token process successfully', async () => { + const mockFetch = mock(async () => { + const payload = { + access_token: "-p0__8dso$@pyv94wmm6@#7", + refresh_token: "gj%*dqvp082_+9-ym8nmhj", + expires_in: 46346 + } + return new Response(JSON.stringify(payload), { status: 200 }) + }) + + global.fetch = mockFetch + + await DatasetsModel.create({ + _id: process.env.TESTING_USER_ID, + huggingfaceAccount: fakeUserHuggingfaceAccount + }) + + const result = await huggingfaceService.refreshHuggingfaceAccessToken( + process.env.TESTING_USER_ID, + "f5@*)d#@dSjykgyk*&t84gCk" + ); + + expect(mockFetch).toHaveBeenCalled() + expect(result.isSuccess).toBeTrue(); + expect(result.result).toMatchObject({ + accessToken: expect.any(String), + accessTokenExpiresIn: expect.any(Date), + refreshToken: expect.any(String), + username: "anyname", + emailVerified: true, + }); + }); + + it('Should catch and return an error because of failed external API request', async () => { + const mockFetch = mock(async () => { + return new Response("{}", { status: 400 }) + }) + + global.fetch = mockFetch + + const result = await huggingfaceService.refreshHuggingfaceAccessToken( + process.env.TESTING_USER_ID, + "f5@*)d#@dSjykgyk*&t84gCk" + ); + + expect(mockFetch).toHaveBeenCalled() + expect(result.isSuccess).toBeFalse(); + expect(result.message).toBe(operationsResultsMessages.failedAccessTokenRefreshing); + }); +}); + +afterAll(async () => { + global.fetch = originalFetch + await DatasetsModel.deleteMany(); +}); diff --git a/tests/lib/TestingRequest.ts b/tests/lib/TestingRequest.ts index c5cfa98..8d2b57f 100644 --- a/tests/lib/TestingRequest.ts +++ b/tests/lib/TestingRequest.ts @@ -1,9 +1,9 @@ import type { Server } from "bun"; export default class TestingRequest { - constructor(private server: Server) {} + constructor(private server: Server) { } - async returnRespons(res: Response) { + async returnResponse(res: Response) { return { resBody: await res.json(), status: res.status, @@ -11,7 +11,7 @@ export default class TestingRequest { } async GET(path: string) { - return fetch(`${this.server.url.origin}/${path}`).then(this.returnRespons); + return fetch(`${this.server.url.origin}/${path}`).then(this.returnResponse); } async POST(path: string, body: Record | string) { @@ -20,7 +20,7 @@ export default class TestingRequest { method: "POST", }; return fetch(`${this.server.url.origin}/${path}`, init).then( - this.returnRespons + this.returnResponse ); } @@ -29,7 +29,7 @@ export default class TestingRequest { method: "DELETE", }; return fetch(`${this.server.url.origin}/${path}`, init).then( - this.returnRespons + this.returnResponse ); } @@ -39,7 +39,7 @@ export default class TestingRequest { method: "PATCH", }; return fetch(`${this.server.url.origin}/${path}`, init).then( - this.returnRespons + this.returnResponse ); } @@ -49,7 +49,7 @@ export default class TestingRequest { method: "PUT", }; return fetch(`${this.server.url.origin}/${path}`, init).then( - this.returnRespons + this.returnResponse ); } }