forked from Yara724/api
fanavaran duplication request problems fixed.
This commit is contained in:
@@ -1,6 +1,43 @@
|
||||
import { FanavaranAuthService } from "./fanavaran-auth.service";
|
||||
|
||||
describe("FanavaranAuthService", () => {
|
||||
const createAuthTokenModel = () => {
|
||||
const store = new Map<
|
||||
string,
|
||||
{ clientKey: string; authenticationToken: string; expiresAt: Date }
|
||||
>();
|
||||
return {
|
||||
findOne: jest.fn((query: { clientKey: string }) => ({
|
||||
lean: () => ({
|
||||
exec: async () => store.get(query.clientKey) ?? null,
|
||||
}),
|
||||
})),
|
||||
findOneAndUpdate: jest.fn(
|
||||
(
|
||||
query: { clientKey: string },
|
||||
update: { $set: { authenticationToken: string; expiresAt: Date } },
|
||||
) => ({
|
||||
exec: async () => {
|
||||
const next = {
|
||||
clientKey: query.clientKey,
|
||||
authenticationToken: update.$set.authenticationToken,
|
||||
expiresAt: update.$set.expiresAt,
|
||||
};
|
||||
store.set(query.clientKey, next);
|
||||
return next;
|
||||
},
|
||||
}),
|
||||
),
|
||||
deleteOne: jest.fn((query: { clientKey: string }) => ({
|
||||
exec: async () => {
|
||||
store.delete(query.clientKey);
|
||||
return { deletedCount: 1 };
|
||||
},
|
||||
})),
|
||||
_store: store,
|
||||
};
|
||||
};
|
||||
|
||||
it("detects Fanavaran transient try-later messages", () => {
|
||||
expect(
|
||||
FanavaranAuthService.isTransientTryLaterError(
|
||||
@@ -24,22 +61,11 @@ describe("FanavaranAuthService", () => {
|
||||
e instanceof Error ? e.message : String(e),
|
||||
sanitizeErrorDetails: () => ({}),
|
||||
formatErrorWithTrackingCode: (m: string) => m,
|
||||
captureAxiosExchange: () => ({}),
|
||||
};
|
||||
const authTokenModel = createAuthTokenModel();
|
||||
|
||||
let loginCalls = 0;
|
||||
http.post.mockImplementation((url: string) => {
|
||||
if (url.includes("GetAppToken")) {
|
||||
return {
|
||||
toPromise: undefined,
|
||||
pipe: undefined,
|
||||
subscribe: undefined,
|
||||
// firstValueFrom uses Observable — mock as Observable-like via rxjs
|
||||
};
|
||||
}
|
||||
return {};
|
||||
});
|
||||
|
||||
// Use real firstValueFrom path by mocking httpService.post to return an Observable
|
||||
const { of, delay } = await import("rxjs");
|
||||
http.post.mockImplementation((url: string) => {
|
||||
if (url.includes("GetAppToken")) {
|
||||
@@ -57,7 +83,11 @@ describe("FanavaranAuthService", () => {
|
||||
}).pipe(delay(20));
|
||||
});
|
||||
|
||||
const service = new FanavaranAuthService(http as any, audit as any);
|
||||
const service = new FanavaranAuthService(
|
||||
http as any,
|
||||
audit as any,
|
||||
authTokenModel as any,
|
||||
);
|
||||
|
||||
const [a, b, c] = await Promise.all([
|
||||
service.getAuthenticationToken("parsian"),
|
||||
@@ -75,28 +105,74 @@ describe("FanavaranAuthService", () => {
|
||||
expect(loginCalls).toBe(1);
|
||||
});
|
||||
|
||||
it("reuses persisted token across service instances", async () => {
|
||||
const http = { post: jest.fn() };
|
||||
const audit = {
|
||||
recordStep: jest.fn().mockResolvedValue(undefined),
|
||||
extractErrorMessage: (e: unknown) =>
|
||||
e instanceof Error ? e.message : String(e),
|
||||
sanitizeErrorDetails: () => ({}),
|
||||
formatErrorWithTrackingCode: (m: string) => m,
|
||||
captureAxiosExchange: () => ({}),
|
||||
};
|
||||
const authTokenModel = createAuthTokenModel();
|
||||
const { of } = await import("rxjs");
|
||||
let loginCalls = 0;
|
||||
http.post.mockImplementation((url: string) => {
|
||||
if (url.includes("GetAppToken")) {
|
||||
return of({
|
||||
status: 200,
|
||||
headers: { apptoken: "app-1" },
|
||||
data: {},
|
||||
});
|
||||
}
|
||||
loginCalls += 1;
|
||||
return of({
|
||||
status: 200,
|
||||
headers: { authenticationtoken: "auth-persisted" },
|
||||
data: {},
|
||||
});
|
||||
});
|
||||
|
||||
const first = new FanavaranAuthService(
|
||||
http as any,
|
||||
audit as any,
|
||||
authTokenModel as any,
|
||||
);
|
||||
await first.getAuthenticationToken("tejaratno");
|
||||
expect(loginCalls).toBe(1);
|
||||
|
||||
const second = new FanavaranAuthService(
|
||||
http as any,
|
||||
audit as any,
|
||||
authTokenModel as any,
|
||||
);
|
||||
const token = await second.getAuthenticationToken("tejaratno");
|
||||
expect(token).toBe("auth-persisted");
|
||||
expect(loginCalls).toBe(1);
|
||||
});
|
||||
|
||||
it("enters tenant backoff on try-later errors", () => {
|
||||
const service = new FanavaranAuthService({} as any, {} as any);
|
||||
const service = new FanavaranAuthService(
|
||||
{} as any,
|
||||
{} as any,
|
||||
createAuthTokenModel() as any,
|
||||
);
|
||||
service.registerFailure(
|
||||
"parsian",
|
||||
"کد پیگیری خطا: 1\r\n.لطفا پس از چند لحظه مجدد تلاش فرمایید.",
|
||||
);
|
||||
expect(service.isInBackoff("parsian")).toBe(true);
|
||||
expect(() => service.assertNotInBackoff("parsian")).toThrow(
|
||||
/backoff/i,
|
||||
);
|
||||
expect(() => service.assertNotInBackoff("parsian")).toThrow(/backoff/i);
|
||||
});
|
||||
|
||||
it("computes next Asia/Tehran midnight expiry after now", () => {
|
||||
// 2026-08-02 10:00:00 UTC ≈ 13:30 Tehran (UTC+3:30) → same calendar day midnight
|
||||
const now = Date.parse("2026-08-02T10:00:00.000Z");
|
||||
const expiry = FanavaranAuthService.getNextMidnightExpiryMs(now);
|
||||
expect(expiry).toBeGreaterThan(now);
|
||||
// Must land within ~14h (before next Tehran midnight)
|
||||
expect(expiry - now).toBeLessThanOrEqual(24 * 60 * 60 * 1000);
|
||||
expect(expiry - now).toBeGreaterThan(0);
|
||||
|
||||
// Just after Tehran midnight: 2026-08-01 20:30:01 UTC = 2026-08-02 00:00:01 Tehran
|
||||
const justAfterMidnight = Date.parse("2026-08-01T20:30:01.000Z");
|
||||
const next = FanavaranAuthService.getNextMidnightExpiryMs(justAfterMidnight);
|
||||
expect(next - justAfterMidnight).toBeGreaterThan(23 * 60 * 60 * 1000);
|
||||
|
||||
Reference in New Issue
Block a user