diff --git a/src/aplication/Nfc.usecases.test.ts b/src/aplication/Nfc.usecases.test.ts index 5f3c082..f6791db 100644 --- a/src/aplication/Nfc.usecases.test.ts +++ b/src/aplication/Nfc.usecases.test.ts @@ -1,18 +1,18 @@ import { httpclient } from "config/httpclient.config.js"; import { pgClient } from "config/pgclient.config.js"; import type { ServerContext } from "domain/ServerContext.js"; -import test, { describe } from "node:test"; +import test, { describe, beforeEach } from "node:test"; import { NfcUsecases } from "./Nfc.usecases.js"; +import type { NfcRepository } from "infrastructure/Nfc.repository.js"; import assert from "node:assert"; - -describe("NFC activation code", () => { +describe("NFC activation code (Private methods)", () => { const serverContext: ServerContext = { PostgresClient: pgClient, HttpClient: httpclient } - const nfcUsecases = new NfcUsecases(serverContext) + const nfcUsecases = new NfcUsecases(serverContext, {} as NfcRepository) test("Should generate 8 digit codes", () => { // @ts-expect-error @@ -43,3 +43,89 @@ describe("NFC activation code", () => { assert(validation == false) }) }) + +describe("NfcUsecases Unit Tests (Public methods with Mocks)", () => { + let mockNfcRepository: any; + let mockServerContext: ServerContext; + let nfcUsecases: NfcUsecases; + + beforeEach(() => { + mockNfcRepository = { + findActivationCodes: test.mock.fn(), + createActivationCode: test.mock.fn(), + }; + + mockServerContext = { + PostgresClient: {} as any, + HttpClient: {} as any, + }; + + nfcUsecases = new NfcUsecases(mockServerContext, mockNfcRepository as unknown as NfcRepository); + }); + + describe("generateActivationLabel", () => { + const card_id = "test-card-id"; + + test("should return error if repository.findActivationCodes fails", async () => { + const expectedError = "Repository error"; + mockNfcRepository.findActivationCodes.mock.mockImplementation(async () => ({ + error: expectedError + })); + + const usecase = nfcUsecases.generateActivationLabel(); + const result = await usecase({ card_id }); + + assert.strictEqual(result.error, expectedError); + assert.strictEqual(mockNfcRepository.findActivationCodes.mock.callCount(), 1); + }); + + test("should reuse existing code if found and override is false", async () => { + const existingCode = "v1234567"; + mockNfcRepository.findActivationCodes.mock.mockImplementation(async () => ({ + data: [{ code_plain: existingCode }] + })); + mockNfcRepository.createActivationCode.mock.mockImplementation(async () => ({ + data: { card_id, code_plain: existingCode } + })); + + const usecase = nfcUsecases.generateActivationLabel(); + const result = await usecase({ card_id, override: false }); + + assert.ok(result.data); + assert.strictEqual(result.data.code, existingCode); + assert.strictEqual(result.data.reused, true); + assert.strictEqual(result.data.overriden, false); + + assert.strictEqual(mockNfcRepository.createActivationCode.mock.callCount(), 1); + }); + + test("should generate new code if no existing codes found", async () => { + mockNfcRepository.findActivationCodes.mock.mockImplementation(async () => ({ + data: [] + })); + + const usecase = nfcUsecases.generateActivationLabel(); + const result = await usecase({ card_id }); + + assert.ok(result.data); + assert.ok(result.data.code); + assert.strictEqual(result.data.reused, false); + assert.strictEqual(result.data.overriden, false); + }); + + test("should generate new code if override is true even if existing codes exist", async () => { + const existingCode = "v1234567"; + mockNfcRepository.findActivationCodes.mock.mockImplementation(async () => ({ + data: [{ code_plain: existingCode }] + })); + + const usecase = nfcUsecases.generateActivationLabel(); + const result = await usecase({ card_id, override: true }); + + assert.ok(result.data); + assert.notStrictEqual(result.data.code, existingCode); + assert.strictEqual(result.data.reused, false); + assert.strictEqual(result.data.overriden, true); + }); + }); +}); diff --git a/src/aplication/Nfc.usecases.ts b/src/aplication/Nfc.usecases.ts index 45007e4..28cefdc 100644 --- a/src/aplication/Nfc.usecases.ts +++ b/src/aplication/Nfc.usecases.ts @@ -1,12 +1,20 @@ import type { ServerContext } from "domain/ServerContext.js"; import { labelTemplate } from "./labelTemplate.js"; +import type { NfcRepository } from "infrastructure/Nfc.repository.js"; +import assert from "node:assert"; +import type { CodeGenerationResultDTO } from "domain/NfcDTOs.js"; +import type { Result } from "domain/Result.js"; export class NfcUsecases { private ctx: ServerContext; + private nfcRepository: NfcRepository; + constructor( - serverContext: ServerContext + serverContext: ServerContext, + nfcRepository: NfcRepository ) { this.ctx = serverContext + this.nfcRepository = nfcRepository } private getRandomDayOffset() { @@ -69,16 +77,58 @@ export class NfcUsecases { return validationChar == testValidationChar } + /** + * Caso de uso principal, genera una etiqueta con un codigo de activación, + * si ya se ha generado uno para esa tarjeta se avisará. + * Se puede forzar generar uno nuevo. + */ public generateActivationLabel() { - return () => { + return async (args: { + card_id: string, + override?: boolean, + }): Promise> => { + const generatedCodes = await this.nfcRepository.findActivationCodes(args.card_id) + + if (generatedCodes.error != undefined) { + console.error(generatedCodes.error) + return generatedCodes // Caso de error + } + + if (generatedCodes.data.length > 0 && !args.override) { + // Ya se habia generado un codigo + assert(generatedCodes.data[0] != undefined) + const code = generatedCodes.data[0].code_plain + assert(code != undefined) + + const label = labelTemplate(code) + this.nfcRepository.createActivationCode({ + cardId: args.card_id, + code: code + }) + + return { + data: { + code: code, + label: label, + overriden: false, + reused: true, + } + } + } + const codigo = this.generateActivationCode() const label = labelTemplate(codigo) // Introducir en la bdd + // Caso base: codigo limpio generado return { - code: codigo, - label: label + data: { + code: codigo, + label: label, + overriden: args.override ?? false, + reused: false + } } } } diff --git a/src/domain/NfcRegistry.ts b/src/domain/NfcDTOs.ts similarity index 70% rename from src/domain/NfcRegistry.ts rename to src/domain/NfcDTOs.ts index b3bdb60..68323d7 100644 --- a/src/domain/NfcRegistry.ts +++ b/src/domain/NfcDTOs.ts @@ -26,3 +26,12 @@ export type ActivationLogDTO = { geo_location: string, created_at: Date, } + +export type CodeGenerationResultDTO = { + code: string, + label: string, + /** Si ya existia un codigo anterior pero se ha creado uno nuevo*/ + overriden: boolean, + /** Si ya existia un código anterior y se ha reusado */ + reused: boolean +} diff --git a/src/infrastructure/Nfc.repository.ts b/src/infrastructure/Nfc.repository.ts index d4d7fc6..2943777 100644 --- a/src/infrastructure/Nfc.repository.ts +++ b/src/infrastructure/Nfc.repository.ts @@ -1,4 +1,4 @@ -import type { ActivationCodeDTO } from "domain/NfcRegistry.js"; +import type { ActivationCodeDTO } from "domain/NfcDTOs.js"; import type { Result } from "domain/Result.js"; import type { ServerContext } from "domain/ServerContext.js"; import { constrainedMemory } from "node:process";