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
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:
@@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user