Compare commits

...

2 Commits

Author SHA1 Message Date
Sugui 7a403dd4db Added new task 2024-03-30 13:56:36 +01:00
Sugui 47ac55f6fa Added tests for base64 image encoding 2024-03-30 13:56:22 +01:00
5 changed files with 29 additions and 14 deletions

View File

@ -1,3 +1,3 @@
- Parametrize bot API url in env variable - Parametrize bot API url in env variable
- Encode images in base64 - Encode images in base64 with proper prefix "data:image/png;base64,<encoding>"
- Parametrize tags - Parametrize tags

View File

@ -24,16 +24,20 @@ class ImageService {
const validPosts = Promise.all(posts const validPosts = Promise.all(posts
.filter(post => !imagesUrls.some(url => url === post.url)) .filter(post => !imagesUrls.some(url => url === post.url))
.map(async (post): Promise<Image> => { .map(async (post): Promise<Image> => {
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 { url: post.url, tags: post.tags, content: encodedImage };
})); }));
return validPosts; return validPosts;
} }
private async imageUrlToBase64(url: string): Promise<string> { private async urlToImageData(url: string): Promise<string> {
const data = await (await fetch(url)).text(); return await (await fetch(url)).text();
return Buffer.from(data, "base64").toString("binary"); }
private imageToBase64(imageData: string): string {
return Buffer.from(imageData, "binary").toString("base64");
} }
} }

View File

@ -1,15 +1,16 @@
import { describe, expect, it, mock, spyOn } from "bun:test"; import { describe, expect, it, mock, spyOn } from "bun:test";
import Image from "../src/types/Image"; import Image from "../../src/types/Image";
import ImageService from "../src/services/ImageService"; import ImageService from "../../src/services/ImageService";
import GelbooruApiResponse from "../src/types/GelbooruServiceResponse"; import GelbooruApiResponse from "../../src/types/GelbooruServiceResponse";
import { BotApiResponse } from "../src/types/BotApiResponse"; import { BotApiResponse } from "../../src/types/BotApiResponse";
import fs from "node:fs";
describe("endpoint gets a non repeated image", () => { 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 () => { 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 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"; 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; let alreadyCalled = false;
return { return {
default: { default: {
@ -25,7 +26,7 @@ describe("endpoint gets a non repeated image", () => {
} }
}); });
mock.module("../src/services/BotApiService", () => ({ mock.module("../../src/services/BotApiService", () => ({
default: { default: {
getAll: (): BotApiResponse => ({ getAll: (): BotApiResponse => ({
images: [ 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(); const image: Image = await ImageService.get();
expect(image.url).not.toBe(REPEATED_URL); 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);
})
}) })

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 744 B