From 2ccef4e28c5bb261d93c0459e578f0e99ff58b3f Mon Sep 17 00:00:00 2001 From: Sugui Date: Sat, 30 Mar 2024 12:58:22 +0100 Subject: [PATCH 1/4] Added base64 encoding for image content --- src/services/ImageService.ts | 16 ++++++++++++---- test/ImageService.test.ts | 11 +++++++---- 2 files changed, 19 insertions(+), 8 deletions(-) diff --git a/src/services/ImageService.ts b/src/services/ImageService.ts index 2b8ca37..d511775 100644 --- a/src/services/ImageService.ts +++ b/src/services/ImageService.ts @@ -5,7 +5,7 @@ import GelbooruApiService from "./GelbooruApiService"; class ImageService { postsQueue: Image[] = []; - + async get(): Promise { while (this.postsQueue.length === 0) { const validPosts = await this.getNewValidImages(); @@ -21,12 +21,20 @@ class ImageService { const botResponse = await BotApiService.getAll(); const imagesUrls = botResponse.images.map(image => image.url); - const validPosts = posts + const validPosts = Promise.all(posts .filter(post => !imagesUrls.some(url => url === post.url)) - .map(post => ({ url: post.url, tags: post.tags, content: "TODO" })); - + .map(async (post): Promise => { + const encodedImage = await this.imageUrlToBase64(post.url); + return { url: post.url, tags: post.tags, content: encodedImage }; + })); + return validPosts; } + + private async imageUrlToBase64(url: string): Promise { + const data = await (await fetch(url)).text(); + return Buffer.from(data, "base64").toString("binary"); + } } export default new ImageService(); \ No newline at end of file diff --git a/test/ImageService.test.ts b/test/ImageService.test.ts index 6b9381a..1d6a60a 100644 --- a/test/ImageService.test.ts +++ b/test/ImageService.test.ts @@ -1,4 +1,4 @@ -import { describe, expect, it, mock } from "bun:test"; +import { describe, expect, it, mock, spyOn } from "bun:test"; import Image from "../src/types/Image"; import ImageService from "../src/services/ImageService"; import GelbooruApiResponse from "../src/types/GelbooruServiceResponse"; @@ -6,8 +6,8 @@ import { BotApiResponse } from "../src/types/BotApiResponse"; describe("endpoint gets a non repeated image", () => { it("should return an image that is not in the response of the /images endpoint of the bot API", async () => { - const REPEATED_URL = "image.com/1"; - const UNIQUE_URL = "image.com/2"; + const REPEATED_URL = "https://fastly.picsum.photos/id/1/10/20.jpg?hmac=gY6PvUXFacKfYpBpTTVcNLxumpyMmoCamM-J5DOPwNc"; + const UNIQUE_URL = "https://fastly.picsum.photos/id/2/10/20.jpg?hmac=zy6lz21CuRIstr9ETx9h5AuoH50s_L2uIEct3dROpY8"; mock.module("../src/services/GelbooruApiService", () => { let alreadyCalled = false; @@ -35,7 +35,10 @@ describe("endpoint gets a non repeated image", () => { } })); + const imageUrlToBase64Spy = spyOn(ImageService as any, 'imageUrlToBase64'); + const image: Image = await ImageService.get(); expect(image.url).not.toBe(REPEATED_URL); - }) + expect(imageUrlToBase64Spy).toHaveBeenCalledTimes(1); + }); }) \ No newline at end of file -- 2.39.2 From 47ac55f6faeeceedbe01f90016f71a5032e7aeaa Mon Sep 17 00:00:00 2001 From: Sugui Date: Sat, 30 Mar 2024 13:56:22 +0100 Subject: [PATCH 2/4] Added tests for base64 image encoding --- src/services/ImageService.ts | 12 +++++--- test/{ => ImageService}/ImageService.test.ts | 28 +++++++++++++------ test/ImageService/testImage.b64 | 1 + test/ImageService/testImage.jpg | Bin 0 -> 744 bytes 4 files changed, 28 insertions(+), 13 deletions(-) rename test/{ => ImageService}/ImageService.test.ts (60%) create mode 100644 test/ImageService/testImage.b64 create mode 100644 test/ImageService/testImage.jpg diff --git a/src/services/ImageService.ts b/src/services/ImageService.ts index d511775..6b47e37 100644 --- a/src/services/ImageService.ts +++ b/src/services/ImageService.ts @@ -24,16 +24,20 @@ class ImageService { const validPosts = Promise.all(posts .filter(post => !imagesUrls.some(url => url === post.url)) .map(async (post): Promise => { - const encodedImage = await this.imageUrlToBase64(post.url); + const imageData = await this.urlToImageData(post.url); + const encodedImage = this.imageToBase64(imageData); return { url: post.url, tags: post.tags, content: encodedImage }; })); return validPosts; } - private async imageUrlToBase64(url: string): Promise { - const data = await (await fetch(url)).text(); - return Buffer.from(data, "base64").toString("binary"); + private async urlToImageData(url: string): Promise { + return await (await fetch(url)).text(); + } + + private imageToBase64(imageData: string): string { + return Buffer.from(imageData, "binary").toString("base64"); } } diff --git a/test/ImageService.test.ts b/test/ImageService/ImageService.test.ts similarity index 60% rename from test/ImageService.test.ts rename to test/ImageService/ImageService.test.ts index 1d6a60a..359a131 100644 --- a/test/ImageService.test.ts +++ b/test/ImageService/ImageService.test.ts @@ -1,15 +1,16 @@ import { describe, expect, it, mock, spyOn } from "bun:test"; -import Image from "../src/types/Image"; -import ImageService from "../src/services/ImageService"; -import GelbooruApiResponse from "../src/types/GelbooruServiceResponse"; -import { BotApiResponse } from "../src/types/BotApiResponse"; +import Image from "../../src/types/Image"; +import ImageService from "../../src/services/ImageService"; +import GelbooruApiResponse from "../../src/types/GelbooruServiceResponse"; +import { BotApiResponse } from "../../src/types/BotApiResponse"; +import fs from "node:fs"; describe("endpoint gets a non repeated image", () => { it("should return an image that is not in the response of the /images endpoint of the bot API", async () => { const REPEATED_URL = "https://fastly.picsum.photos/id/1/10/20.jpg?hmac=gY6PvUXFacKfYpBpTTVcNLxumpyMmoCamM-J5DOPwNc"; const UNIQUE_URL = "https://fastly.picsum.photos/id/2/10/20.jpg?hmac=zy6lz21CuRIstr9ETx9h5AuoH50s_L2uIEct3dROpY8"; - mock.module("../src/services/GelbooruApiService", () => { + mock.module("../../src/services/GelbooruApiService", () => { let alreadyCalled = false; return { default: { @@ -25,7 +26,7 @@ describe("endpoint gets a non repeated image", () => { } }); - mock.module("../src/services/BotApiService", () => ({ + mock.module("../../src/services/BotApiService", () => ({ default: { getAll: (): BotApiResponse => ({ images: [ @@ -35,10 +36,19 @@ describe("endpoint gets a non repeated image", () => { } })); - const imageUrlToBase64Spy = spyOn(ImageService as any, 'imageUrlToBase64'); - const image: Image = await ImageService.get(); expect(image.url).not.toBe(REPEATED_URL); - expect(imageUrlToBase64Spy).toHaveBeenCalledTimes(1); }); +}); + +describe("imageToBase64 works as expected", () => { + it("should encode image to base64 properly", () => { + const imageData = fs.readFileSync('./test/ImageService/testImage.jpg'); + const imageToBase64Spy = spyOn(ImageService as any, 'imageToBase64'); + + const actualEncoding = imageToBase64Spy.call(ImageService, imageData); + const expectedEncoding = fs.readFileSync('./test/ImageService/testImage.b64', "base64").toString(); + + expect(actualEncoding).toBe(expectedEncoding); + }) }) \ No newline at end of file diff --git a/test/ImageService/testImage.b64 b/test/ImageService/testImage.b64 new file mode 100644 index 0000000..c559d97 --- /dev/null +++ b/test/ImageService/testImage.b64 @@ -0,0 +1 @@ +/9j/4QDeRXhpZgAASUkqAAgAAAAGABIBAwABAAAAAQAAABoBBQABAAAAVgAAABsBBQABAAAAXgAAACgBAwABAAAAAgAAABMCAwABAAAAAQAAAGmHBAABAAAAZgAAAAAAAABIAAAAAQAAAEgAAAABAAAABwAAkAcABAAAADAyMTABkQcABAAAAAECAwCGkgcAFQAAAMAAAAAAoAcABAAAADAxMDABoAMAAQAAAP//AAACoAQAAQAAAAIAAAADoAQAAQAAAAIAAAAAAAAAQVNDSUkAAABQaWNzdW0gSUQ6IDEwAP/bAEMACAYGBwYFCAcHBwkJCAoMFA0MCwsMGRITDxQdGh8eHRocHCAkLicgIiwjHBwoNyksMDE0NDQfJzk9ODI8LjM0Mv/bAEMBCQkJDAsMGA0NGDIhHCEyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMv/CABEIAAIAAgMBIgACEQEDEQH/xAAVAAEBAAAAAAAAAAAAAAAAAAAABf/EABUBAQEAAAAAAAAAAAAAAAAAAAIE/9oADAMBAAIQAxAAAAGIHP8A/8QAFhAAAwAAAAAAAAAAAAAAAAAAAAMT/9oACAEBAAEFAqsP/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPwF//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPwF//8QAFxAAAwEAAAAAAAAAAAAAAAAAAAIzkf/aAAgBAQAGPwKjaf/EABUQAQEAAAAAAAAAAAAAAAAAAADx/9oACAEBAAE/Ibt//9oADAMBAAIAAwAAABD3/8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAwEBPxB//8QAFBEBAAAAAAAAAAAAAAAAAAAAAP/aAAgBAgEBPxB//8QAGBAAAgMAAAAAAAAAAAAAAAAAAAERgfD/2gAIAQEAAT8QUCyWf//Z \ No newline at end of file diff --git a/test/ImageService/testImage.jpg b/test/ImageService/testImage.jpg new file mode 100644 index 0000000000000000000000000000000000000000..90ec4fa00004153498461137fa299e3abf509308 GIT binary patch literal 744 zcmb7AO-lk%6utL7osS%GMy<@o{3y^uWE_P)C?qUsc0$myO%S+f6+t^`Q!TSaYoTR7 zp-n_Tq7MlA2eoSXx^L7n7e*J}yNAoU=bZP%qj-bY&Ew`ifafiM1^@~~kpmKvCy7D7 z?+~8u;a$Ro9)}T*u^)c(Ea3A=(624JrB1)A02e9*XyUkK2iskYq;Ptv!Z^+E=s>5( zDLW2!y6QpzuufN9y7B=mSJt=do=3gW+&erv$aotonX&`o1?r$FimC)PRaJFe8#Cfz zBNQ^GqOnLkl}yj1lD3_hE9En};;d~K7UzpjxmvBJ^UG^X?rN!0b)^XEx^9GwL^zyq zvv$_~cZqwjG+@9u=73qqEff#*VwB%ASV1|);V}z{XD~PdGr38BdTt9K;)Dr&aG5ZV z5mTdm46>UDapKmn1^5h(Si{x`0_zhd$z@1`RoXoprN*wCGP60H89w?+YT4TsVL!hg h*!&#fD}$MT1@j3Blm7Gf`_Vdi|F$(7#jBQh`U3M5Xb%7Y literal 0 HcmV?d00001 -- 2.39.2 From 7a403dd4db73048643ce6e2f34d2d72e3047ba28 Mon Sep 17 00:00:00 2001 From: Sugui Date: Sat, 30 Mar 2024 13:56:36 +0100 Subject: [PATCH 3/4] Added new task --- TODO.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index be9a789..f96f564 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,3 @@ - Parametrize bot API url in env variable -- Encode images in base64 +- Encode images in base64 with proper prefix "data:image/png;base64," - Parametrize tags \ No newline at end of file -- 2.39.2 From 4f058e38c1cf4148453223cc4b656dbc5f59267f Mon Sep 17 00:00:00 2001 From: Sugui Date: Sat, 30 Mar 2024 14:01:42 +0100 Subject: [PATCH 4/4] Updated TODO --- TODO.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/TODO.md b/TODO.md index f96f564..b60e289 100644 --- a/TODO.md +++ b/TODO.md @@ -1,3 +1,4 @@ - Parametrize bot API url in env variable - Encode images in base64 with proper prefix "data:image/png;base64," -- Parametrize tags \ No newline at end of file +- Parametrize tags +- Generalize the ImageService test with faker \ No newline at end of file -- 2.39.2