feat(FN-302): add getClient mock to TranslationService test and enqueue path tests (+1 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled

Commits merged:
- chore(FN-302): apply biome formatting to test file
- fix(FN-302): add getClient mock to TranslationService test and enqueue path tests

Files changed:
.../src/translations/translations.service.spec.ts  | 78 ++++++++++++++++++++--
 1 file changed, 73 insertions(+), 5 deletions(-)

Fusion-Task-Id: FN-302
This commit is contained in:
Fusion
2026-05-13 14:15:15 +00:00
parent 7a1b289931
commit ecf74f8959

View File

@@ -21,31 +21,50 @@ function createMockDeps() {
insert: vi.fn().mockReturnValue(insertChain),
};
const pipeline = {
set: vi.fn().mockReturnThis(),
exec: vi.fn().mockResolvedValue([]),
};
const ioredisClient = {
get: vi.fn().mockResolvedValue(null),
set: vi.fn().mockResolvedValue("OK"),
mget: vi.fn().mockResolvedValue([]),
pipeline: vi.fn().mockReturnValue(pipeline),
};
const redis = {
getJson: vi.fn().mockResolvedValue(null),
setJson: vi.fn().mockResolvedValue(undefined),
del: vi.fn().mockResolvedValue(undefined),
getClient: vi.fn().mockReturnValue(ioredisClient),
};
return { db, redis };
return { db, redis, ioredisClient, pipeline };
}
function createService(db: unknown, redis: unknown): TranslationsService {
const queue = { add: vi.fn().mockResolvedValue(undefined) };
return new TranslationsService(db as any, redis as any, queue as any);
function createService(db: unknown, redis: unknown, queue?: unknown): TranslationsService {
const q = queue ?? { add: vi.fn().mockResolvedValue(undefined) };
return new TranslationsService(db as any, redis as any, q as any);
}
describe("TranslationsService", () => {
let service: TranslationsService;
let db: ReturnType<typeof createMockDeps>["db"];
let redis: ReturnType<typeof createMockDeps>["redis"];
let ioredisClient: ReturnType<typeof createMockDeps>["ioredisClient"];
let pipeline: ReturnType<typeof createMockDeps>["pipeline"];
let queue: { add: ReturnType<typeof vi.fn> };
beforeEach(() => {
vi.clearAllMocks();
const deps = createMockDeps();
db = deps.db;
redis = deps.redis;
service = createService(db, redis);
ioredisClient = deps.ioredisClient;
pipeline = deps.pipeline;
queue = { add: vi.fn().mockResolvedValue(undefined) };
service = createService(db, redis, queue);
});
// Dictionary fallback removed by design \u2014 its word-by-word replacement
@@ -111,4 +130,53 @@ describe("TranslationsService", () => {
expect(result.isAutoTranslated).toBe(false);
});
});
describe("async enqueue", () => {
it("should enqueue unknown terms via BullMQ when Redis NX flag is set", async () => {
// Cache miss + DB miss: translate() fires enqueueTranslation()
redis.getJson.mockResolvedValue(null);
// DB select returns empty (using default mock which returns [])
// Simulate Redis SET NX succeeding: exec returns [[null, "OK"]]
pipeline.exec.mockResolvedValue([[null, "OK"]]);
const result = await service.translate("cat:unknown", "unknown-part");
// Translation result must still be the original text (non-blocking)
expect(result.translatedText).toBe("unknown-part");
expect(result.source).toBe("none");
// Verify getClient was called to reach the ioredis client
expect(redis.getClient).toHaveBeenCalled();
// Verify pipeline was used for the dedup check
expect(ioredisClient.pipeline).toHaveBeenCalled();
expect(pipeline.set).toHaveBeenCalledWith("tr:queued:unknown-part", "1", "EX", 300, "NX");
// The term was fresh → queue.add must be called
expect(queue.add).toHaveBeenCalledWith(
"translate",
{ terms: ["unknown-part"] },
expect.objectContaining({ jobId: expect.stringContaining("tr:") as string }),
);
});
it("should skip enqueue when Redis NX dedup flag already exists", async () => {
redis.getJson.mockResolvedValue(null);
// Simulate Redis SET NX returning null (key already existed)
pipeline.exec.mockResolvedValue([[null, null]]);
const result = await service.translate("cat:dup", "already-queued");
expect(result.translatedText).toBe("already-queued");
expect(result.source).toBe("none");
// Pipeline was still called for the dedup check
expect(ioredisClient.pipeline).toHaveBeenCalled();
// But queue.add must NOT be called because NX returned null (already queued)
expect(queue.add).not.toHaveBeenCalled();
});
});
});