Repositorio de codigos de activacion + test
This commit is contained in:
@@ -29,17 +29,6 @@ AS $$
|
|||||||
$$
|
$$
|
||||||
;
|
;
|
||||||
|
|
||||||
-- 2. Tabla de Tarjetas
|
|
||||||
-- Posiblemente haya que mantener solo el id e ignorar el PAN
|
|
||||||
CREATE TABLE payment_cards (
|
|
||||||
card_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
|
||||||
user_id UUID REFERENCES users(user_id),
|
|
||||||
pan_last_four VARCHAR(4) NOT NULL,
|
|
||||||
card_holder_name VARCHAR(100) NOT NULL,
|
|
||||||
status VARCHAR(20) DEFAULT 'PENDING_ACTIVATION', -- PENDING, ACTIVE, BLOCKED, EXPIRED
|
|
||||||
created_at TIMESTAMPTZ DEFAULT NOW(),
|
|
||||||
activated_at TIMESTAMPTZ
|
|
||||||
);
|
|
||||||
|
|
||||||
-- 3. Tabla de Códigos de Activación
|
-- 3. Tabla de Códigos de Activación
|
||||||
-- No creo que vaya a recibir confirmación de activación porque es de otro proyecto,
|
-- No creo que vaya a recibir confirmación de activación porque es de otro proyecto,
|
||||||
@@ -47,8 +36,8 @@ CREATE TABLE payment_cards (
|
|||||||
-- El algoritmo de hash es sha256
|
-- El algoritmo de hash es sha256
|
||||||
CREATE TABLE activation_codes (
|
CREATE TABLE activation_codes (
|
||||||
code_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
code_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
card_id UUID REFERENCES payment_cards(card_id), -- Una tarjeta, maximo un un código activo borrar o solo con expires_at?
|
card_id UUID NOT NULL, -- ID EXTERNO
|
||||||
code_plain TEXT NOT NULL, --
|
code_plain TEXT NOT NULL,
|
||||||
code_hash TEXT NOT NULL, -- Guardar el código hasheado, el original se imprime y se manda
|
code_hash TEXT NOT NULL, -- Guardar el código hasheado, el original se imprime y se manda
|
||||||
is_used BOOLEAN DEFAULT FALSE,
|
is_used BOOLEAN DEFAULT FALSE,
|
||||||
is_blocked BOOLEAN DEFAULT FALSE,
|
is_blocked BOOLEAN DEFAULT FALSE,
|
||||||
@@ -61,7 +50,7 @@ CREATE TABLE activation_codes (
|
|||||||
-- Lo mismo, muy sobredimensionado, no creo que haya falta en este punto
|
-- Lo mismo, muy sobredimensionado, no creo que haya falta en este punto
|
||||||
CREATE TABLE activation_logs (
|
CREATE TABLE activation_logs (
|
||||||
log_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
log_id UUID PRIMARY KEY DEFAULT uuid_generate_v7(),
|
||||||
card_id UUID REFERENCES payment_cards(card_id),
|
card_id UUID NOT NULL, -- ID EXTERNO
|
||||||
code_id UUID REFERENCES activation_codes(code_id),
|
code_id UUID REFERENCES activation_codes(code_id),
|
||||||
action_type VARCHAR(50) NOT NULL, -- TODO: CREAR ENUM'GENERATED', 'ATTEMPT_FAILED', 'ACTIVATED'
|
action_type VARCHAR(50) NOT NULL, -- TODO: CREAR ENUM'GENERATED', 'ATTEMPT_FAILED', 'ACTIVATED'
|
||||||
ip_address INET,
|
ip_address INET,
|
||||||
|
|||||||
1226
package-lock.json
generated
1226
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,7 +5,7 @@
|
|||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "tsx --test",
|
"test": "tsx --test --watch",
|
||||||
"dev": "tsx src/main.ts",
|
"dev": "tsx src/main.ts",
|
||||||
"build": "tsc",
|
"build": "tsc",
|
||||||
"build:esbuild": "esbuild --bundle src/main.ts --outdir=dist --platform=node --format=esm --packages=external",
|
"build:esbuild": "esbuild --bundle src/main.ts --outdir=dist --platform=node --format=esm --packages=external",
|
||||||
@@ -24,10 +24,11 @@
|
|||||||
"typescript": "^5.9.3"
|
"typescript": "^5.9.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@sf-alvar/db-migrate": "^1.0.3",
|
||||||
"axios": "^1.13.6",
|
"axios": "^1.13.6",
|
||||||
"db-migrate": "^0.11.14",
|
|
||||||
"dotenv": "^17.3.1",
|
"dotenv": "^17.3.1",
|
||||||
"express": "^5.2.1",
|
"express": "^5.2.1",
|
||||||
"pg": "^8.20.0"
|
"pg": "^8.20.0",
|
||||||
|
"uuidv7": "^1.1.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { httpclient } from "config/httpclient.config.js";
|
|||||||
import { pgClient } from "config/pgclient.config.js";
|
import { pgClient } from "config/pgclient.config.js";
|
||||||
import { NfcRepository } from "./Nfc.repository.js";
|
import { NfcRepository } from "./Nfc.repository.js";
|
||||||
import type { ServerContext } from "domain/ServerContext.js";
|
import type { ServerContext } from "domain/ServerContext.js";
|
||||||
|
import { uuidv7 } from "uuidv7";
|
||||||
|
|
||||||
describe("NfcRepository Integration Tests", () => {
|
describe("NfcRepository Integration Tests", () => {
|
||||||
const serverContext: ServerContext = {
|
const serverContext: ServerContext = {
|
||||||
@@ -12,7 +13,7 @@ describe("NfcRepository Integration Tests", () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const repo = new NfcRepository(serverContext);
|
const repo = new NfcRepository(serverContext);
|
||||||
const testCardId = "test-card-" + Date.now();
|
const testCardId = uuidv7()
|
||||||
const testCode = "12345678";
|
const testCode = "12345678";
|
||||||
|
|
||||||
// Clean up before and after tests to ensure isolation
|
// Clean up before and after tests to ensure isolation
|
||||||
@@ -61,12 +62,14 @@ describe("NfcRepository Integration Tests", () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
test("findActivationCodes should return empty array if card has no codes", async () => {
|
test("findActivationCodes should return empty array if card has no codes", async () => {
|
||||||
const result = await repo.findActivationCodes("non-existent-card");
|
const nonExistentCard = uuidv7()
|
||||||
|
const result = await repo.findActivationCodes(nonExistentCard);
|
||||||
|
|
||||||
if (result.error) {
|
if (result.error) {
|
||||||
assert.fail(`findActivationCodes failed: ${result.error}`);
|
assert.fail(`findActivationCodes failed: ${result.error}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
assert.ok(result.data, "Data should be returned");
|
assert.ok(result.data, "Data should be returned");
|
||||||
assert.strictEqual(result.data.length, 0);
|
assert.strictEqual(result.data.length, 0);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import type { ActivationCodeDTO } from "domain/NfcRegistry.js";
|
import type { ActivationCodeDTO } from "domain/NfcRegistry.js";
|
||||||
import type { Result } from "domain/Result.js";
|
import type { Result } from "domain/Result.js";
|
||||||
import type { ServerContext } from "domain/ServerContext.js";
|
import type { ServerContext } from "domain/ServerContext.js";
|
||||||
|
import { constrainedMemory } from "node:process";
|
||||||
|
|
||||||
// TODO: Pasar a Result<E,T>
|
// TODO: Pasar a Result<E,T>
|
||||||
|
|
||||||
@@ -26,6 +27,7 @@ export class NfcRepository {
|
|||||||
data: codeResult.rows
|
data: codeResult.rows
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
console.error(e)
|
||||||
return {
|
return {
|
||||||
error: e as string
|
error: e as string
|
||||||
}
|
}
|
||||||
@@ -33,6 +35,8 @@ export class NfcRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public async createActivationCode(args: { cardId: string, code: string }): Promise<Result<string, ActivationCodeDTO>> {
|
public async createActivationCode(args: { cardId: string, code: string }): Promise<Result<string, ActivationCodeDTO>> {
|
||||||
|
const conn = await this.ctx.PostgresClient.connect()
|
||||||
|
await conn.query("BEGIN")
|
||||||
const query = `
|
const query = `
|
||||||
INSERT INTO activation_codes(
|
INSERT INTO activation_codes(
|
||||||
card_id,
|
card_id,
|
||||||
@@ -44,18 +48,19 @@ export class NfcRepository {
|
|||||||
$2,
|
$2,
|
||||||
digest($2,'sha256')
|
digest($2,'sha256')
|
||||||
)
|
)
|
||||||
RETURNING(
|
RETURNING
|
||||||
code_id,card_id,code_plain,code_hash,is_used,is_blocked,failed_attempts,created_at,expires_at
|
code_id,card_id,code_plain,code_hash,is_used,is_blocked,failed_attempts,created_at,expires_at
|
||||||
)
|
|
||||||
`
|
`
|
||||||
const values = [args.cardId, args.code]
|
const values = [args.cardId, args.code]
|
||||||
try {
|
try {
|
||||||
const insertResult = await this.ctx.PostgresClient.query<ActivationCodeDTO>(query, values)
|
const insertResult = await conn.query<ActivationCodeDTO>(query, values)
|
||||||
|
await conn.query("COMMIT")
|
||||||
return {
|
return {
|
||||||
data: insertResult.rows[0]!
|
data: insertResult.rows[0]!
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error("Error createActivationCode: ", e)
|
console.error("Error createActivationCode: ", e)
|
||||||
|
await conn.query("ROLLBACK")
|
||||||
return {
|
return {
|
||||||
error: e as string
|
error: e as string
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user